line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test::Class::Tiny; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
1008168
|
use strict; |
|
4
|
|
|
|
|
25
|
|
|
4
|
|
|
|
|
119
|
|
4
|
4
|
|
|
4
|
|
23
|
use warnings; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
344
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION; |
7
|
|
|
|
|
|
|
$VERSION = '0.03_01'; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=encoding utf-8 |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Test::Class::Tiny - xUnit in Perl, simplified |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
package t::mytest; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use parent qw( Test::Class::Tiny ); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
__PACKAGE__->runtests() if !caller; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub T_startup_something { |
24
|
|
|
|
|
|
|
# Runs at the start of the test run. |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub something_T_setup { |
28
|
|
|
|
|
|
|
# Runs before each normal test function |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# Expects 2 assertions: |
32
|
|
|
|
|
|
|
sub T2_normal { |
33
|
|
|
|
|
|
|
ok(1, 'yes'); |
34
|
|
|
|
|
|
|
ok( !0, 'no'); |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Ignores assertion count: |
38
|
|
|
|
|
|
|
sub T0_whatever { |
39
|
|
|
|
|
|
|
ok(1, 'yes'); |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub T_teardown_something { |
43
|
|
|
|
|
|
|
# Runs after each normal test function |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub T_shutdown_something { |
47
|
|
|
|
|
|
|
# Runs at the end of the test run. |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 STATUS |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
This module is B. If you use it, you MUST check the changelog |
53
|
|
|
|
|
|
|
before upgrading to a new version. Any CPAN distributions that use this module |
54
|
|
|
|
|
|
|
could break whenever this module is updated. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 DESCRIPTION |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
L has served Perl’s xUnit needs for a long time |
59
|
|
|
|
|
|
|
but is incompatible with the L framework. This module allows for |
60
|
|
|
|
|
|
|
a similar workflow but in a way that works with both L and the older, |
61
|
|
|
|
|
|
|
L-based modules. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head1 HOW (AND WHY) TO USE THIS MODULE |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
xUnit encourages well-designed tests by encouraging organization of test |
66
|
|
|
|
|
|
|
logic into independent chunks of test logic rather than a single monolithic |
67
|
|
|
|
|
|
|
block of code. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
xUnit provides standard hooks for: |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=over |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item * startup: The start of all tests |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item * setup: The start of an individual test group (i.e., Perl function) |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item * teardown: The end of an individual test group |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=item * shutdown: The end of all tests |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=back |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
To write functions that execute at these points in the workflow, |
84
|
|
|
|
|
|
|
name those functions with the prefixes C, C, |
85
|
|
|
|
|
|
|
C, or C. B, name such functions |
86
|
|
|
|
|
|
|
with the I C<_T_startup>, C<_T_setup>, C<_T_teardown>, or |
87
|
|
|
|
|
|
|
C<_T_shutdown>. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
To write a test function—i.e., a function that actually runs some |
90
|
|
|
|
|
|
|
assertions—prefix the function name with C, the number of test assertions |
91
|
|
|
|
|
|
|
in the function, then an underscore. For example, a function that contains |
92
|
|
|
|
|
|
|
9 assertions might be named C. If that function |
93
|
|
|
|
|
|
|
doesn’t run exactly 9 assertions, a test failure is produced. |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
To forgo counting test assertions, use 0 as the test count, e.g., |
96
|
|
|
|
|
|
|
C. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
You may alternatively use suffix-style naming for test functions well, |
99
|
|
|
|
|
|
|
e.g., C, C. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
The above convention is a significant departure from L, |
102
|
|
|
|
|
|
|
which uses Perl subroutine attributes to indicate this information. |
103
|
|
|
|
|
|
|
Using method names is dramatically simpler to implement and also easier |
104
|
|
|
|
|
|
|
to type. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
In most other respects this module attempts to imitate L. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head2 PLANS |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
The concept of a global “plan” (i.e., an expected number of assertions) |
111
|
|
|
|
|
|
|
isn’t all that sensible with xUnit because each test function has its |
112
|
|
|
|
|
|
|
own plan. So, ideally the total number of expected assertions for a given |
113
|
|
|
|
|
|
|
test module is just the sum of all test functions’ expected assertions. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Thus, currently, C sets the L object’s plan to |
116
|
|
|
|
|
|
|
C if the plan is undefined. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head1 TEST INHERITANCE |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Like L, this module seamlessly integrates inherited methods. |
121
|
|
|
|
|
|
|
To have one test module inherit another module’s tests, just make that |
122
|
|
|
|
|
|
|
first module a subclass of the latter. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
B Inheritance in tests, while occasionally useful, can also |
125
|
|
|
|
|
|
|
make for difficult maintenance over time if overused. Where I’ve found it |
126
|
|
|
|
|
|
|
most useful is cases like L, where each test needs to run with |
127
|
|
|
|
|
|
|
each backend implementation. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head1 RUNNING YOUR TEST |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
To use this module to write normal Perl test scripts, just define |
132
|
|
|
|
|
|
|
the script’s package (ideally not C, but it’ll work) as a subclass of |
133
|
|
|
|
|
|
|
this module. Then put the following somewhere in the script: |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
__PACKAGE__->runtests() if !caller; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Your test will thus execute as a “modulino”. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head1 SPECIAL FEATURES |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=over |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item * As in L, a C method may be defined. If this |
144
|
|
|
|
|
|
|
method returns truthy, then the class’s tests are skipped, and that truthy |
145
|
|
|
|
|
|
|
return is given as the reason for the skip. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item * The C environment variable is honored as in L. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item * L’s C method is NOT recognized |
150
|
|
|
|
|
|
|
here because an early return will already trigger a failure. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item * Within a test method, C may be called to retrieve the |
153
|
|
|
|
|
|
|
number of expected test assertions. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item * To define a test function whose test count isn’t known until runtime, |
156
|
|
|
|
|
|
|
name it B the usual C prefix, then at runtime do: |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
$test_obj->num_method_tests( $name, $count ) |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
See F in the distribution for an example of this. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=back |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=head1 COMMON PITFALLS |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Avoid the following: |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=over |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item * Writing startup logic outside of the module class, e.g.: |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
if (!caller) { |
173
|
|
|
|
|
|
|
my $mock = Test::MockModule->new('Some::Module'); |
174
|
|
|
|
|
|
|
$mock->redefine('somefunc', sub { .. } ); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
__PACKAGE__->runtests(); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
The above works I if the test module runs in its own process; if you try |
180
|
|
|
|
|
|
|
to run this module with anything else it’ll fail because C will be |
181
|
|
|
|
|
|
|
truthy, which will prevent the mocking from being set up, which your test |
182
|
|
|
|
|
|
|
probably depends on. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Instead of the above, write a wrapper around C, thus: |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub runtests { |
187
|
|
|
|
|
|
|
my $self = shift; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my $mock = Test::MockModule->new('Some::Module'); |
190
|
|
|
|
|
|
|
$mock->redefine('somefunc', sub { .. } ); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
$self->SUPER::runtests(); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
This ensures your test module will always run with the intended mocking. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item * REDUX: Writing startup logic outside of the module class, e.g.: |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
my $mock = Test::MockModule->new('Some::Module'); |
200
|
|
|
|
|
|
|
$mock->redefine('somefunc', sub { .. } ); |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
__PACKAGE__->runtests() if !caller; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
This is even worse than before because the mock will be global, which |
205
|
|
|
|
|
|
|
will quietly apply it where we don’t intend. This produces |
206
|
|
|
|
|
|
|
action-at-a-distance bugs, which can be notoriously hard to find. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=back |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head1 SEE ALSO |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Besides L, you might also look at the following: |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=over |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=item * L also implements xUnit for L but doesn’t |
217
|
|
|
|
|
|
|
allow inheritance. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=item * L works with L, but the L requirement |
220
|
|
|
|
|
|
|
makes use in CPAN modules problematic. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=back |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 AUTHOR |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Copyright 2019 L (FELIPE) |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head1 LICENSE |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
This code is licensed under the same license as Perl itself. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=cut |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
235
|
|
|
|
|
|
|
|
236
|
4
|
|
|
4
|
|
29
|
use mro (); |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
65
|
|
237
|
|
|
|
|
|
|
|
238
|
4
|
|
|
4
|
|
20
|
use Test2::API (); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
215
|
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
our ($a, $b); |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
243
|
|
|
|
|
|
|
|
244
|
4
|
|
|
4
|
|
29
|
use constant SKIP_CLASS => (); |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
4308
|
|
245
|
|
|
|
|
|
|
|
246
|
4
|
|
|
4
|
0
|
106
|
sub new { bless {}, shift } |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub num_tests { |
249
|
1
|
|
|
1
|
1
|
107
|
my ($self) = @_; |
250
|
|
|
|
|
|
|
|
251
|
1
|
50
|
|
|
|
5
|
if (!$self->{'_running'}) { |
252
|
0
|
|
|
|
|
0
|
die "num_tests() called outside of running test!"; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
1
|
|
|
|
|
6
|
return $self->{'_num_tests'}; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub num_method_tests { |
259
|
1
|
|
|
1
|
0
|
10
|
my ($self, $name, $count) = @_; |
260
|
|
|
|
|
|
|
|
261
|
1
|
50
|
|
|
|
5
|
die 'need name!' if !$name; |
262
|
|
|
|
|
|
|
|
263
|
1
|
50
|
|
|
|
4
|
if (@_ == 2) { |
264
|
0
|
|
|
|
|
0
|
return $self->{'test'}{$name}; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
1
|
|
|
|
|
7
|
$self->{'test'}{$name}{'count'} = $count; |
268
|
1
|
|
|
|
|
3
|
$self->{'test'}{$name}{'simple_name'} = $name; |
269
|
|
|
|
|
|
|
|
270
|
1
|
|
|
|
|
3
|
return $self; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub runtests { |
274
|
4
|
|
|
4
|
0
|
1809
|
my ($self) = @_; |
275
|
|
|
|
|
|
|
|
276
|
4
|
100
|
|
|
|
17
|
if (!ref $self) { |
277
|
3
|
|
|
|
|
17
|
$self = $self->new(); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
4
|
|
|
|
|
23
|
local $self->{'_running'} = 1; |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# Allow calls as either instance or object method. |
283
|
4
|
50
|
|
|
|
17
|
if (!ref $self) { |
284
|
0
|
|
|
|
|
0
|
my $obj = $self->new(); |
285
|
0
|
|
|
|
|
0
|
$self = $obj; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
4
|
|
|
|
|
16
|
my $big_ctx = Test2::API::context(); |
289
|
4
|
|
|
|
|
12200
|
my $ctx = $big_ctx->snapshot(); |
290
|
4
|
|
|
|
|
96
|
$big_ctx->release(); |
291
|
|
|
|
|
|
|
|
292
|
4
|
50
|
|
|
|
161
|
if (my $reason = $self->SKIP_CLASS()) { |
293
|
0
|
|
|
|
|
0
|
$ctx->plan(1); |
294
|
0
|
|
|
|
|
0
|
$ctx->skip( ref($self), $reason ); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
else { |
297
|
4
|
|
|
|
|
29
|
$self->_analyze(); |
298
|
|
|
|
|
|
|
|
299
|
4
|
50
|
|
|
|
20
|
if ( my $startup_hr = $self->{'startup'} ) { |
300
|
0
|
|
|
|
|
0
|
$self->_run_funcs($startup_hr); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
|
303
|
4
|
50
|
|
|
|
17
|
if ( my $tests_hr = $self->{'test'} ) { |
304
|
4
|
|
|
|
|
8
|
my $setup_hr = $self->{'setup'}; |
305
|
4
|
|
|
|
|
10
|
my $teardown_hr = $self->{'teardown'}; |
306
|
|
|
|
|
|
|
|
307
|
4
|
|
|
|
|
9
|
my $filter_fn; |
308
|
|
|
|
|
|
|
my $got_count; |
309
|
|
|
|
|
|
|
|
310
|
4
|
|
|
|
|
23
|
my $hub = $ctx->hub(); |
311
|
|
|
|
|
|
|
|
312
|
4
|
50
|
|
|
|
34
|
$hub->plan('NO PLAN') if !defined $hub->plan(); |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
my $filter_cr = sub { |
315
|
18
|
|
|
18
|
|
15484
|
my ($hub, $event) = @_; |
316
|
|
|
|
|
|
|
|
317
|
18
|
100
|
|
|
|
68
|
$got_count++ if $event->increments_count(); |
318
|
|
|
|
|
|
|
|
319
|
18
|
100
|
100
|
|
|
171
|
if ($event->can('name') && !defined $event->name()) { |
320
|
9
|
|
|
|
|
66
|
my $name = $tests_hr->{$filter_fn}{'simple_name'}; |
321
|
9
|
|
|
|
|
40
|
$name =~ tr<_>< >; |
322
|
9
|
|
|
|
|
26
|
$event->set_name($name); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
18
|
|
|
|
|
116
|
return $event; |
326
|
4
|
|
|
|
|
138
|
}; |
327
|
|
|
|
|
|
|
|
328
|
4
|
|
|
|
|
22
|
$hub->filter($filter_cr); |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
my @sorted_fns = sort { |
331
|
4
|
50
|
|
|
|
107
|
( $tests_hr->{$a}{'simple_name'} cmp $tests_hr->{$b}{'simple_name'} ) |
|
12
|
|
|
|
|
48
|
|
332
|
|
|
|
|
|
|
|| ( $a cmp $b ) |
333
|
|
|
|
|
|
|
} keys %$tests_hr; |
334
|
|
|
|
|
|
|
|
335
|
4
|
|
|
|
|
15
|
for my $fn (@sorted_fns) { |
336
|
12
|
|
|
|
|
46
|
$filter_fn = $fn; |
337
|
|
|
|
|
|
|
|
338
|
12
|
50
|
|
|
|
54
|
if (my $ptn = $ENV{'TEST_METHOD'}) { |
339
|
0
|
0
|
|
|
|
0
|
next if $fn !~ m<$ptn>; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
12
|
50
|
|
|
|
39
|
if ($ENV{'TEST_VERBOSE'}) { |
343
|
0
|
|
|
|
|
0
|
$ctx->diag( $/ . ref($self) . "->$fn()" ); |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
12
|
|
|
|
|
40
|
$self->_run_funcs($setup_hr); |
347
|
|
|
|
|
|
|
|
348
|
12
|
|
|
|
|
19
|
$got_count = 0; |
349
|
|
|
|
|
|
|
|
350
|
12
|
|
|
|
|
36
|
my $want_count = $tests_hr->{$fn}{'count'}; |
351
|
|
|
|
|
|
|
|
352
|
12
|
|
|
|
|
30
|
local $self->{'_num_tests'} = $want_count; |
353
|
|
|
|
|
|
|
|
354
|
12
|
|
|
|
|
23
|
local $@; |
355
|
12
|
50
|
|
|
|
20
|
eval { $self->$fn(); 1 } or do { |
|
12
|
|
|
|
|
58
|
|
|
12
|
|
|
|
|
1909
|
|
356
|
0
|
|
|
|
|
0
|
my $err = $@; |
357
|
0
|
|
|
|
|
0
|
$ctx->fail("$fn()", "Caught exception: $err"); |
358
|
|
|
|
|
|
|
}; |
359
|
|
|
|
|
|
|
|
360
|
12
|
100
|
|
|
|
37
|
if ($want_count) { |
361
|
11
|
50
|
|
|
|
31
|
if ($want_count != $got_count) { |
362
|
0
|
|
|
|
|
0
|
$ctx->fail("Test count mismatch: got $got_count, expected $want_count"); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
12
|
|
|
|
|
33
|
$self->_run_funcs($teardown_hr); |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
4
|
|
|
|
|
22
|
$hub->unfilter($filter_cr); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
4
|
50
|
|
|
|
130
|
if ( my $shutdown_hr = $self->{'shutdown'} ) { |
373
|
0
|
|
|
|
|
0
|
$self->_run_funcs($shutdown_hr); |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
4
|
|
|
|
|
16
|
return; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub _analyze { |
381
|
4
|
|
|
4
|
|
13
|
my ($self) = @_; |
382
|
|
|
|
|
|
|
|
383
|
4
|
50
|
|
|
|
18
|
if (!$self->{'_analyzed'}) { |
384
|
4
|
|
|
|
|
25
|
my @isa = @{ mro::get_linear_isa(ref $self) }; |
|
4
|
|
|
|
|
32
|
|
385
|
|
|
|
|
|
|
|
386
|
4
|
|
|
|
|
21
|
my $t_regexp = q; |
387
|
4
|
|
|
|
|
213
|
my $prefix_regexp = qr<\A${t_regexp}_(.+)>; |
388
|
4
|
|
|
|
|
141
|
my $suffix_regexp = qr<(.+)_$t_regexp\z>; |
389
|
|
|
|
|
|
|
|
390
|
4
|
|
|
|
|
18
|
for my $ns (@isa) { |
391
|
10
|
|
|
|
|
17
|
my $ptbl_hr = do { |
392
|
4
|
|
|
4
|
|
33
|
no strict 'refs'; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
1217
|
|
393
|
10
|
|
|
|
|
17
|
\%{"${ns}::"}; |
|
10
|
|
|
|
|
47
|
|
394
|
|
|
|
|
|
|
}; |
395
|
|
|
|
|
|
|
|
396
|
10
|
|
|
|
|
173
|
for my $name (keys %$ptbl_hr) { |
397
|
595
|
100
|
|
|
|
1538
|
next if !$self->can($name); |
398
|
|
|
|
|
|
|
|
399
|
565
|
|
|
|
|
857
|
my ($whatsit, $simple_name); |
400
|
|
|
|
|
|
|
|
401
|
565
|
100
|
|
|
|
2138
|
if ($name =~ $prefix_regexp) { |
|
|
50
|
|
|
|
|
|
402
|
11
|
|
|
|
|
35
|
$whatsit = $1; |
403
|
11
|
|
|
|
|
24
|
$simple_name = $2; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
elsif ($name =~ $suffix_regexp) { |
406
|
0
|
|
|
|
|
0
|
$simple_name = $1; |
407
|
0
|
|
|
|
|
0
|
$whatsit = $2; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
else { |
410
|
554
|
|
|
|
|
960
|
next; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
11
|
50
|
|
|
|
35
|
if ( $whatsit =~ s<_><> ) { |
414
|
0
|
|
|
|
|
0
|
$self->{$whatsit}{$name} = undef; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
else { |
417
|
11
|
|
|
|
|
50
|
$self->{'test'}{$name} = { |
418
|
|
|
|
|
|
|
count => $whatsit, |
419
|
|
|
|
|
|
|
simple_name => $simple_name, |
420
|
|
|
|
|
|
|
}; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
4
|
|
|
|
|
24
|
$self->{'_analyzed'} = 1; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
4
|
|
|
|
|
11
|
return; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub _run_funcs { |
432
|
24
|
|
|
24
|
|
44
|
my ($self, $funcs_hr) = @_; |
433
|
|
|
|
|
|
|
|
434
|
24
|
|
|
|
|
82
|
for my $fn (sort keys %$funcs_hr) { |
435
|
0
|
0
|
|
|
|
0
|
if ( $funcs_hr->{$fn} ) { |
436
|
0
|
|
|
|
|
0
|
$funcs_hr->{$fn}->($self); |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
else { |
439
|
0
|
|
|
|
|
0
|
$self->$fn(); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
24
|
|
|
|
|
74
|
return; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
1; |