line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
3
|
|
|
3
|
|
211584
|
use strict; |
|
3
|
|
|
|
|
25
|
|
|
3
|
|
|
|
|
91
|
|
2
|
3
|
|
|
3
|
|
15
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
168
|
|
3
|
|
|
|
|
|
|
package Test::Fatal; |
4
|
|
|
|
|
|
|
# ABSTRACT: incredibly simple helpers for testing code with exceptions |
5
|
|
|
|
|
|
|
$Test::Fatal::VERSION = '0.016'; |
6
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
7
|
|
|
|
|
|
|
#pod |
8
|
|
|
|
|
|
|
#pod use Test::More; |
9
|
|
|
|
|
|
|
#pod use Test::Fatal; |
10
|
|
|
|
|
|
|
#pod |
11
|
|
|
|
|
|
|
#pod use System::Under::Test qw(might_die); |
12
|
|
|
|
|
|
|
#pod |
13
|
|
|
|
|
|
|
#pod is( |
14
|
|
|
|
|
|
|
#pod exception { might_die; }, |
15
|
|
|
|
|
|
|
#pod undef, |
16
|
|
|
|
|
|
|
#pod "the code lived", |
17
|
|
|
|
|
|
|
#pod ); |
18
|
|
|
|
|
|
|
#pod |
19
|
|
|
|
|
|
|
#pod like( |
20
|
|
|
|
|
|
|
#pod exception { might_die; }, |
21
|
|
|
|
|
|
|
#pod qr/turns out it died/, |
22
|
|
|
|
|
|
|
#pod "the code died as expected", |
23
|
|
|
|
|
|
|
#pod ); |
24
|
|
|
|
|
|
|
#pod |
25
|
|
|
|
|
|
|
#pod isa_ok( |
26
|
|
|
|
|
|
|
#pod exception { might_die; }, |
27
|
|
|
|
|
|
|
#pod 'Exception::Whatever', |
28
|
|
|
|
|
|
|
#pod 'the thrown exception', |
29
|
|
|
|
|
|
|
#pod ); |
30
|
|
|
|
|
|
|
#pod |
31
|
|
|
|
|
|
|
#pod =head1 DESCRIPTION |
32
|
|
|
|
|
|
|
#pod |
33
|
|
|
|
|
|
|
#pod Test::Fatal is an alternative to the popular L. It does much |
34
|
|
|
|
|
|
|
#pod less, but should allow greater flexibility in testing exception-throwing code |
35
|
|
|
|
|
|
|
#pod with about the same amount of typing. |
36
|
|
|
|
|
|
|
#pod |
37
|
|
|
|
|
|
|
#pod It exports one routine by default: C. |
38
|
|
|
|
|
|
|
#pod |
39
|
|
|
|
|
|
|
#pod B C intentionally does not manipulate the call stack. |
40
|
|
|
|
|
|
|
#pod User-written test functions that use C must be careful to avoid |
41
|
|
|
|
|
|
|
#pod false positives if exceptions use stack traces that show arguments. For a more |
42
|
|
|
|
|
|
|
#pod magical approach involving globally overriding C, see |
43
|
|
|
|
|
|
|
#pod L. |
44
|
|
|
|
|
|
|
#pod |
45
|
|
|
|
|
|
|
#pod =cut |
46
|
|
|
|
|
|
|
|
47
|
3
|
|
|
3
|
|
18
|
use Carp (); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
81
|
|
48
|
3
|
|
|
3
|
|
1562
|
use Try::Tiny 0.07; |
|
3
|
|
|
|
|
6399
|
|
|
3
|
|
|
|
|
176
|
|
49
|
|
|
|
|
|
|
|
50
|
3
|
|
|
3
|
|
20
|
use Exporter 5.57 'import'; |
|
3
|
|
|
|
|
35
|
|
|
3
|
|
|
|
|
1937
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
our @EXPORT = qw(exception); |
53
|
|
|
|
|
|
|
our @EXPORT_OK = qw(exception success dies_ok lives_ok); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
#pod =func exception |
56
|
|
|
|
|
|
|
#pod |
57
|
|
|
|
|
|
|
#pod my $exception = exception { ... }; |
58
|
|
|
|
|
|
|
#pod |
59
|
|
|
|
|
|
|
#pod C takes a bare block of code and returns the exception thrown by |
60
|
|
|
|
|
|
|
#pod that block. If no exception was thrown, it returns undef. |
61
|
|
|
|
|
|
|
#pod |
62
|
|
|
|
|
|
|
#pod B If the block results in a I exception, such as 0 or the |
63
|
|
|
|
|
|
|
#pod empty string, Test::Fatal itself will die. Since either of these cases |
64
|
|
|
|
|
|
|
#pod indicates a serious problem with the system under testing, this behavior is |
65
|
|
|
|
|
|
|
#pod considered a I. If you must test for these conditions, you should use |
66
|
|
|
|
|
|
|
#pod L's try/catch mechanism. (Try::Tiny is the underlying exception |
67
|
|
|
|
|
|
|
#pod handling system of Test::Fatal.) |
68
|
|
|
|
|
|
|
#pod |
69
|
|
|
|
|
|
|
#pod Note that there is no TAP assert being performed. In other words, no "ok" or |
70
|
|
|
|
|
|
|
#pod "not ok" line is emitted. It's up to you to use the rest of C in an |
71
|
|
|
|
|
|
|
#pod existing test like C, C, C, et cetera. Or you may wish to use |
72
|
|
|
|
|
|
|
#pod the C and C wrappers, which do provide TAP output. |
73
|
|
|
|
|
|
|
#pod |
74
|
|
|
|
|
|
|
#pod C does I alter the stack presented to the called block, meaning |
75
|
|
|
|
|
|
|
#pod that if the exception returned has a stack trace, it will include some frames |
76
|
|
|
|
|
|
|
#pod between the code calling C and the thing throwing the exception. |
77
|
|
|
|
|
|
|
#pod This is considered a I because it avoids the occasionally twitchy |
78
|
|
|
|
|
|
|
#pod C mechanism. |
79
|
|
|
|
|
|
|
#pod |
80
|
|
|
|
|
|
|
#pod B This is not a great idea: |
81
|
|
|
|
|
|
|
#pod |
82
|
|
|
|
|
|
|
#pod sub exception_like(&$;$) { |
83
|
|
|
|
|
|
|
#pod my ($code, $pattern, $name) = @_; |
84
|
|
|
|
|
|
|
#pod like( &exception($code), $pattern, $name ); |
85
|
|
|
|
|
|
|
#pod } |
86
|
|
|
|
|
|
|
#pod |
87
|
|
|
|
|
|
|
#pod exception_like(sub { }, qr/foo/, 'foo appears in the exception'); |
88
|
|
|
|
|
|
|
#pod |
89
|
|
|
|
|
|
|
#pod If the code in the C<...> is going to throw a stack trace with the arguments to |
90
|
|
|
|
|
|
|
#pod each subroutine in its call stack (for example via C, |
91
|
|
|
|
|
|
|
#pod the test name, "foo appears in the exception" will itself be matched by the |
92
|
|
|
|
|
|
|
#pod regex. Instead, write this: |
93
|
|
|
|
|
|
|
#pod |
94
|
|
|
|
|
|
|
#pod like( exception { ... }, qr/foo/, 'foo appears in the exception' ); |
95
|
|
|
|
|
|
|
#pod |
96
|
|
|
|
|
|
|
#pod If you really want a test function that passes the test name, wrap the |
97
|
|
|
|
|
|
|
#pod arguments in an array reference to hide the literal text from a stack trace: |
98
|
|
|
|
|
|
|
#pod |
99
|
|
|
|
|
|
|
#pod sub exception_like(&$) { |
100
|
|
|
|
|
|
|
#pod my ($code, $args) = @_; |
101
|
|
|
|
|
|
|
#pod my ($pattern, $name) = @$args; |
102
|
|
|
|
|
|
|
#pod like( &exception($code), $pattern, $name ); |
103
|
|
|
|
|
|
|
#pod } |
104
|
|
|
|
|
|
|
#pod |
105
|
|
|
|
|
|
|
#pod exception_like(sub { }, [ qr/foo/, 'foo appears in the exception' ] ); |
106
|
|
|
|
|
|
|
#pod |
107
|
|
|
|
|
|
|
#pod To aid in avoiding the problem where the pattern is seen in the exception |
108
|
|
|
|
|
|
|
#pod because of the call stack, C<$Carp::MAxArgNums> is locally set to -1 when the |
109
|
|
|
|
|
|
|
#pod code block is called. If you really don't want that, set it back to whatever |
110
|
|
|
|
|
|
|
#pod value you like at the beginning of the code block. Obviously, this solution |
111
|
|
|
|
|
|
|
#pod doens't affect all possible ways that args of subroutines in the call stack |
112
|
|
|
|
|
|
|
#pod might taint the test. The intention here is to prevent some false passes from |
113
|
|
|
|
|
|
|
#pod people who didn't read the documentation. Your punishment for reading it is |
114
|
|
|
|
|
|
|
#pod that you must consider whether to do anything about this. |
115
|
|
|
|
|
|
|
#pod |
116
|
|
|
|
|
|
|
#pod B: One final bad idea: |
117
|
|
|
|
|
|
|
#pod |
118
|
|
|
|
|
|
|
#pod isnt( exception { ... }, undef, "my code died!"); |
119
|
|
|
|
|
|
|
#pod |
120
|
|
|
|
|
|
|
#pod It's true that this tests that your code died, but you should really test that |
121
|
|
|
|
|
|
|
#pod it died I. For example, if you make an unrelated mistake |
122
|
|
|
|
|
|
|
#pod in the block, like using the wrong dereference, your test will pass even though |
123
|
|
|
|
|
|
|
#pod the code to be tested isn't really run at all. If you're expecting an |
124
|
|
|
|
|
|
|
#pod inspectable exception with an identifier or class, test that. If you're |
125
|
|
|
|
|
|
|
#pod expecting a string exception, consider using C. |
126
|
|
|
|
|
|
|
#pod |
127
|
|
|
|
|
|
|
#pod =cut |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
our ($REAL_TBL, $REAL_CALCULATED_TBL) = (1, 1); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub exception (&) { |
132
|
15
|
|
|
15
|
1
|
10771
|
my $code = shift; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
return try { |
135
|
15
|
50
|
|
15
|
|
696
|
my $incremented = defined $Test::Builder::Level |
136
|
|
|
|
|
|
|
? $Test::Builder::Level - $REAL_CALCULATED_TBL |
137
|
|
|
|
|
|
|
: 0; |
138
|
15
|
|
|
|
|
23
|
local $Test::Builder::Level = $REAL_CALCULATED_TBL; |
139
|
15
|
100
|
|
|
|
44
|
if ($incremented) { |
140
|
|
|
|
|
|
|
# each call to exception adds 5 stack frames |
141
|
4
|
|
|
|
|
16
|
$Test::Builder::Level += 5; |
142
|
4
|
|
|
|
|
10
|
for my $i (1..$incremented) { |
143
|
|
|
|
|
|
|
# -2 because we want to see it from the perspective of the call to |
144
|
|
|
|
|
|
|
# is() within the call to $code->() |
145
|
5
|
|
|
|
|
12
|
my $caller = caller($Test::Builder::Level - 2); |
146
|
5
|
50
|
|
|
|
7
|
if ($caller eq __PACKAGE__) { |
147
|
|
|
|
|
|
|
# each call to exception adds 5 stack frames |
148
|
0
|
|
|
|
|
0
|
$Test::Builder::Level = $Test::Builder::Level + 5; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
else { |
151
|
5
|
|
|
|
|
12
|
$Test::Builder::Level = $Test::Builder::Level + 1; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
15
|
|
|
|
|
23
|
local $REAL_CALCULATED_TBL = $Test::Builder::Level; |
157
|
15
|
|
|
|
|
24
|
local $Carp::MaxArgNums = -1; |
158
|
15
|
|
|
|
|
40
|
$code->(); |
159
|
9
|
|
|
|
|
7020
|
return undef; |
160
|
|
|
|
|
|
|
} catch { |
161
|
6
|
100
|
|
6
|
|
205
|
return $_ if $_; |
162
|
|
|
|
|
|
|
|
163
|
1
|
50
|
|
|
|
17
|
my $problem = defined $_ ? 'false' : 'undef'; |
164
|
1
|
|
|
|
|
311
|
Carp::confess("$problem exception caught by Test::Fatal::exception"); |
165
|
15
|
|
|
|
|
100
|
}; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
#pod =func success |
169
|
|
|
|
|
|
|
#pod |
170
|
|
|
|
|
|
|
#pod try { |
171
|
|
|
|
|
|
|
#pod should_live; |
172
|
|
|
|
|
|
|
#pod } catch { |
173
|
|
|
|
|
|
|
#pod fail("boo, we died"); |
174
|
|
|
|
|
|
|
#pod } success { |
175
|
|
|
|
|
|
|
#pod pass("hooray, we lived"); |
176
|
|
|
|
|
|
|
#pod }; |
177
|
|
|
|
|
|
|
#pod |
178
|
|
|
|
|
|
|
#pod C, exported only by request, is a L helper with semantics |
179
|
|
|
|
|
|
|
#pod identical to L|Try::Tiny/finally>, but the body of the block will |
180
|
|
|
|
|
|
|
#pod only be run if the C block ran without error. |
181
|
|
|
|
|
|
|
#pod |
182
|
|
|
|
|
|
|
#pod Although almost any needed exception tests can be performed with C, |
183
|
|
|
|
|
|
|
#pod success blocks may sometimes help organize complex testing. |
184
|
|
|
|
|
|
|
#pod |
185
|
|
|
|
|
|
|
#pod =cut |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub success (&;@) { |
188
|
3
|
|
|
3
|
1
|
643
|
my $code = shift; |
189
|
|
|
|
|
|
|
return finally( sub { |
190
|
3
|
100
|
|
3
|
|
864
|
return if @_; # <-- only run on success |
191
|
1
|
|
|
|
|
3
|
$code->(); |
192
|
3
|
|
|
|
|
15
|
}, @_ ); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
#pod =func dies_ok |
196
|
|
|
|
|
|
|
#pod |
197
|
|
|
|
|
|
|
#pod =func lives_ok |
198
|
|
|
|
|
|
|
#pod |
199
|
|
|
|
|
|
|
#pod Exported only by request, these two functions run a given block of code, and |
200
|
|
|
|
|
|
|
#pod provide TAP output indicating if it did, or did not throw an exception. |
201
|
|
|
|
|
|
|
#pod These provide an easy upgrade path for replacing existing unit tests based on |
202
|
|
|
|
|
|
|
#pod C. |
203
|
|
|
|
|
|
|
#pod |
204
|
|
|
|
|
|
|
#pod RJBS does not suggest using this except as a convenience while porting tests to |
205
|
|
|
|
|
|
|
#pod use Test::Fatal's C routine. |
206
|
|
|
|
|
|
|
#pod |
207
|
|
|
|
|
|
|
#pod use Test::More tests => 2; |
208
|
|
|
|
|
|
|
#pod use Test::Fatal qw(dies_ok lives_ok); |
209
|
|
|
|
|
|
|
#pod |
210
|
|
|
|
|
|
|
#pod dies_ok { die "I failed" } 'code that fails'; |
211
|
|
|
|
|
|
|
#pod |
212
|
|
|
|
|
|
|
#pod lives_ok { return "I'm still alive" } 'code that does not fail'; |
213
|
|
|
|
|
|
|
#pod |
214
|
|
|
|
|
|
|
#pod =cut |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my $Tester; |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Signature should match that of Test::Exception |
219
|
|
|
|
|
|
|
sub dies_ok (&;$) { |
220
|
3
|
|
|
3
|
1
|
6526
|
my $code = shift; |
221
|
3
|
|
|
|
|
20
|
my $name = shift; |
222
|
|
|
|
|
|
|
|
223
|
3
|
|
|
|
|
18
|
require Test::Builder; |
224
|
3
|
|
66
|
|
|
16
|
$Tester ||= Test::Builder->new; |
225
|
|
|
|
|
|
|
|
226
|
3
|
|
|
|
|
19
|
my $tap_pos = $Tester->current_test; |
227
|
|
|
|
|
|
|
|
228
|
3
|
|
|
|
|
326
|
my $exception = exception( \&$code ); |
229
|
|
|
|
|
|
|
|
230
|
3
|
50
|
66
|
|
|
50
|
$name ||= $tap_pos != $Tester->current_test |
231
|
|
|
|
|
|
|
? "...and code should throw an exception" |
232
|
|
|
|
|
|
|
: "code should throw an exception"; |
233
|
|
|
|
|
|
|
|
234
|
3
|
|
|
|
|
121
|
my $ok = $Tester->ok( $exception, $name ); |
235
|
3
|
100
|
|
|
|
1670
|
$ok or $Tester->diag( "expected an exception but none was raised" ); |
236
|
3
|
|
|
|
|
236
|
return $ok; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub lives_ok (&;$) { |
240
|
3
|
|
|
3
|
1
|
7082
|
my $code = shift; |
241
|
3
|
|
|
|
|
5
|
my $name = shift; |
242
|
|
|
|
|
|
|
|
243
|
3
|
|
|
|
|
16
|
require Test::Builder; |
244
|
3
|
|
33
|
|
|
9
|
$Tester ||= Test::Builder->new; |
245
|
|
|
|
|
|
|
|
246
|
3
|
|
|
|
|
8
|
my $tap_pos = $Tester->current_test; |
247
|
|
|
|
|
|
|
|
248
|
3
|
|
|
|
|
309
|
my $exception = exception( \&$code ); |
249
|
|
|
|
|
|
|
|
250
|
3
|
50
|
66
|
|
|
52
|
$name ||= $tap_pos != $Tester->current_test |
251
|
|
|
|
|
|
|
? "...and code should not throw an exception" |
252
|
|
|
|
|
|
|
: "code should not throw an exception"; |
253
|
|
|
|
|
|
|
|
254
|
3
|
|
|
|
|
113
|
my $ok = $Tester->ok( ! $exception, $name ); |
255
|
3
|
100
|
|
|
|
1468
|
$ok or $Tester->diag( "expected return but an exception was raised" ); |
256
|
3
|
|
|
|
|
235
|
return $ok; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
1; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
__END__ |