line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test2::Aggregate; |
2
|
|
|
|
|
|
|
|
3
|
16
|
|
|
16
|
|
3061372
|
use strict; |
|
16
|
|
|
|
|
123
|
|
|
16
|
|
|
|
|
413
|
|
4
|
16
|
|
|
16
|
|
75
|
use warnings; |
|
16
|
|
|
|
|
27
|
|
|
16
|
|
|
|
|
339
|
|
5
|
|
|
|
|
|
|
|
6
|
16
|
|
|
16
|
|
68
|
use File::Find; |
|
16
|
|
|
|
|
29
|
|
|
16
|
|
|
|
|
838
|
|
7
|
16
|
|
|
16
|
|
86
|
use File::Path; |
|
16
|
|
|
|
|
27
|
|
|
16
|
|
|
|
|
855
|
|
8
|
16
|
|
|
16
|
|
11576
|
use Path::Tiny; |
|
16
|
|
|
|
|
166005
|
|
|
16
|
|
|
|
|
885
|
|
9
|
|
|
|
|
|
|
|
10
|
16
|
|
|
16
|
|
562
|
use Test2::V0 'subtest'; |
|
16
|
|
|
|
|
120923
|
|
|
16
|
|
|
|
|
111
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Test2::Aggregate - Aggregate tests for increased speed |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use Test2::Aggregate; |
19
|
|
|
|
|
|
|
use Test2::V0; # Or 'use Test::More' etc if your suite uses an other framework |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
Test2::Aggregate::run_tests( |
22
|
|
|
|
|
|
|
dirs => \@test_dirs |
23
|
|
|
|
|
|
|
); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
done_testing(); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 VERSION |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Version 0.17 |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=cut |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
our $VERSION = '0.17'; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Aggregates all tests specified with C (which can even be individual tests) |
38
|
|
|
|
|
|
|
to avoid forking, reloading etc. that can help with performance (dramatically if |
39
|
|
|
|
|
|
|
you have numerous small tests) and also facilitate group profiling. It is quite |
40
|
|
|
|
|
|
|
common to have tests that take over a second of startup time for milliseconds of |
41
|
|
|
|
|
|
|
actual runtime - L removes that overhead. |
42
|
|
|
|
|
|
|
Test files are expected to end in B<.t> and are run as subtests of a single |
43
|
|
|
|
|
|
|
aggregate test. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
A bit similar (mainly in intent) to L, but no inspiration was |
46
|
|
|
|
|
|
|
drawn from the specific module, so simpler in concept and execution, which |
47
|
|
|
|
|
|
|
makes it much more likely to work with your test suite (especially if you use modern |
48
|
|
|
|
|
|
|
tools like L). It does not even try to package each test by default |
49
|
|
|
|
|
|
|
(there is an option), which may be good or bad, depending on your requirements. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Generally, the way to use this module is to try to aggregate sets of quick tests |
52
|
|
|
|
|
|
|
(e.g. unit tests). Try to iteratively add tests to the aggregator, using the C |
53
|
|
|
|
|
|
|
option, so you can easily edit and remove those that do not work. Trying an entire, |
54
|
|
|
|
|
|
|
large, suite in one go is not a good idea, as an incompatible test can break the |
55
|
|
|
|
|
|
|
run making the subsequent tests fail (especially when doing things like globally |
56
|
|
|
|
|
|
|
redefining built-ins etc.) - see the module usage notes for help. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
The module can work with L / L suites, but you will |
59
|
|
|
|
|
|
|
have less issues with L (see notes). |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 METHODS |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head2 C |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my $stats = Test2::Aggregate::run_tests( |
66
|
|
|
|
|
|
|
dirs => \@dirs, # optional if lists defined |
67
|
|
|
|
|
|
|
lists => \@lists, # optional if dirs defined |
68
|
|
|
|
|
|
|
exclude => qr/exclude_regex/, # optional |
69
|
|
|
|
|
|
|
include => qr/include_regex/, # optional |
70
|
|
|
|
|
|
|
root => '/testroot/', # optional |
71
|
|
|
|
|
|
|
load_modules => \@modules, # optional |
72
|
|
|
|
|
|
|
package => 0, # optional |
73
|
|
|
|
|
|
|
shuffle => 0, # optional |
74
|
|
|
|
|
|
|
sort => 0, # optional |
75
|
|
|
|
|
|
|
reverse => 0, # optional |
76
|
|
|
|
|
|
|
unique => 1, # optional |
77
|
|
|
|
|
|
|
repeat => 1, # optional, requires Test2::Plugin::BailOnFail for < 0 |
78
|
|
|
|
|
|
|
slow => 0, # optional |
79
|
|
|
|
|
|
|
override => \%override, # optional, requires Sub::Override |
80
|
|
|
|
|
|
|
stats_output => $stats_output_path, # optional |
81
|
|
|
|
|
|
|
extend_stats => 0, # optional |
82
|
|
|
|
|
|
|
test_warnings => 0, # optional |
83
|
|
|
|
|
|
|
allow_errors => 0, # optional |
84
|
|
|
|
|
|
|
pre_eval => $code_to_eval, # optional |
85
|
|
|
|
|
|
|
dry_run => 0, # optional |
86
|
|
|
|
|
|
|
slurp_param => {binmode => $mode} # optional |
87
|
|
|
|
|
|
|
); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Runs the aggregate tests. Returns a hashref with stats like this: |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
$stats = { |
92
|
|
|
|
|
|
|
'test.t' => { |
93
|
|
|
|
|
|
|
'test_no' => 1, # numbering starts at 1 |
94
|
|
|
|
|
|
|
'pass_perc' => 100, # for single runs pass/fail is 100/0 |
95
|
|
|
|
|
|
|
'timestamp' => '20190705T145043', # start of test |
96
|
|
|
|
|
|
|
'time' => '0.1732', # seconds - only with stats_output |
97
|
|
|
|
|
|
|
'warnings' => $STDERR # only with test_warnings on non empty STDERR |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
}; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
The parameters to pass: |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=over 4 |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=item * C (either this or C is required) |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
An arrayref containing directories which will be searched recursively, or even |
108
|
|
|
|
|
|
|
individual tests. The directories (unless C or C are true) |
109
|
|
|
|
|
|
|
will be processed and tests run in order specified. Test files are expected to |
110
|
|
|
|
|
|
|
end in C<.t>. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=item * C (either this or C is required) |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Arrayref of flat files from which each line will be pushed to C (so they |
115
|
|
|
|
|
|
|
have a lower precedence - note C still applies, don't include it in the |
116
|
|
|
|
|
|
|
paths inside the list files). If the path does not exist, it will currently be |
117
|
|
|
|
|
|
|
silently ignored, however the "official" way to skip a line without checking it |
118
|
|
|
|
|
|
|
as a path is to start with a C<#> to denote a comment. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
This option is nicely combined with the C<--exclude-list> option of C (the |
121
|
|
|
|
|
|
|
L) to skip the individual runs of the tests you aggregated. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=item * C (optional) |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
A regex to filter out tests that you want excluded. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item * C (optional) |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
A regex which the tests have to match in order to be included in the test run. |
130
|
|
|
|
|
|
|
Applied after C. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item * C (optional) |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
If defined, must be a valid root directory that will prefix all C and |
135
|
|
|
|
|
|
|
C items. You may want to set it to C<'./'> if you want dirs relative |
136
|
|
|
|
|
|
|
to the current directory and the dot is not in your C<@INC>. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=item * C (optional) |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
Arrayref with modules to be loaded (with C) at the start of the |
141
|
|
|
|
|
|
|
test. Useful for testing modules with special namespace requirements. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item * C (optional) |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Will package each test in its own namespace. While it may help avoid things like |
146
|
|
|
|
|
|
|
redefine warnings, from experience, it can break some tests, so it is disabled |
147
|
|
|
|
|
|
|
by default. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item * C (optional) |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Pass L compatible key/values as a hashref. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item * C (optional) |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Number of times to repeat the test(s) (default is 1 for a single run). If |
156
|
|
|
|
|
|
|
C is negative, L is required, as the tests |
157
|
|
|
|
|
|
|
will repeat until they bail on a failure. It can be combined with C |
158
|
|
|
|
|
|
|
in which case a warning will also cause the test run to end. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=item * C (optional) |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
From v0.11, duplicate tests are by default removed from the running list as that |
163
|
|
|
|
|
|
|
could mess up the stats output. You can still define it as false to allow duplicate |
164
|
|
|
|
|
|
|
tests in the list. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item * C (optional) |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Sort tests alphabetically if set to true. Provides a way to fix the test order |
169
|
|
|
|
|
|
|
across systems. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=item * C (optional) |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Random order of tests if set to true. Will override C. |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item * C (optional) |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Reverse order of tests if set to true. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item * C (optional) |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
When true, tests will be skipped if the environment variable C is set. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item * C (optional) |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Tests for warnings over all the tests if set to true - this is added as a final |
186
|
|
|
|
|
|
|
test which expects zero as the number of tests which had STDERR output. |
187
|
|
|
|
|
|
|
The STDERR output of each test will be printed at the end of the test run (and |
188
|
|
|
|
|
|
|
included in the test run result hash), so if you want to see warnings the moment |
189
|
|
|
|
|
|
|
they are generated leave this option disabled. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item * C (optional) |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
If enabled, it will allow errors that exit tests prematurely (so they may return |
194
|
|
|
|
|
|
|
a pass if one of their subtests had passed). The option is available to enable |
195
|
|
|
|
|
|
|
old behaviour (version <= 0.12), before the module stopped allowing this. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item * C (optional) |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Instead of running the tests, will do C for each one. Otherwise, |
200
|
|
|
|
|
|
|
test order, stats files etc. will be produced (as if all tests passed). |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=item * C (optional) |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
If you are using list files, by default, C will be used |
205
|
|
|
|
|
|
|
to read them. |
206
|
|
|
|
|
|
|
If you would like to use C with your own parameters instead, like a UTF16 |
207
|
|
|
|
|
|
|
binmode etc, you can pass them here. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item * C (optional) |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
String with code to run with eval before each test. You might be inclined to do |
212
|
|
|
|
|
|
|
this for example: |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
pre_eval => "no warnings 'redefine';" |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
You might expect it to silence redefine warnings (when you have similarly named |
217
|
|
|
|
|
|
|
subs on many tests), but even if you don't set warnings explicitly in your tests, |
218
|
|
|
|
|
|
|
most test bundles will set warnings automatically for you (e.g. for L |
219
|
|
|
|
|
|
|
you'd have to do C |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=item * C (optional) |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
C specifies a path where a file will be created to print out |
224
|
|
|
|
|
|
|
running time per test (average if multiple iterations) and passing percentage. |
225
|
|
|
|
|
|
|
Output is sorted from slowest test to fastest. On negative C the stats |
226
|
|
|
|
|
|
|
of each successful run will be written separately instead of the averages. |
227
|
|
|
|
|
|
|
The name of the file is C. |
228
|
|
|
|
|
|
|
If C<'-'> is passed instead of a path, then the output will be written to STDOUT. |
229
|
|
|
|
|
|
|
The timing stats are useful because the test harness doesn't normally measure |
230
|
|
|
|
|
|
|
time per subtest (remember, your individual aggregated tests become subtests). |
231
|
|
|
|
|
|
|
If you prefer to capture the hash output of the function and use that for your |
232
|
|
|
|
|
|
|
reports, you still need to define C to enable timing (just send |
233
|
|
|
|
|
|
|
the output to C, C etc). |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=item * C (optional) |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
This option exist to make the default output format of C be fixed, |
238
|
|
|
|
|
|
|
but still allow additions in future versions that will only be written with the |
239
|
|
|
|
|
|
|
C option enabled. |
240
|
|
|
|
|
|
|
Additions with C as of the current version: |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=over 4 |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
- starting date/time in ISO_8601. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=back |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=back |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=cut |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub run_tests { |
253
|
38
|
|
|
38
|
1
|
100955
|
my %args = @_; |
254
|
|
|
|
|
|
|
Test2::V0::plan skip_all => 'Skipping slow tests.' |
255
|
38
|
100
|
100
|
|
|
144
|
if $args{slow} && $ENV{SKIP_SLOW}; |
256
|
|
|
|
|
|
|
|
257
|
37
|
|
|
|
|
65
|
eval "use $_;" foreach @{$args{load_modules}}; |
|
37
|
|
|
|
|
172
|
|
258
|
37
|
|
|
|
|
965
|
local $ENV{AGGREGATE_TESTS} = 1; |
259
|
|
|
|
|
|
|
|
260
|
37
|
100
|
|
|
|
126
|
my $override = $args{override} ? _override($args{override}) : undef; |
261
|
37
|
|
|
|
|
68
|
my @dirs = (); |
262
|
37
|
|
100
|
|
|
157
|
my $root = $args{root} || ''; |
263
|
37
|
|
|
|
|
58
|
my @tests; |
264
|
|
|
|
|
|
|
|
265
|
37
|
100
|
|
|
|
107
|
@dirs = @{$args{dirs}} if $args{dirs}; |
|
27
|
|
|
|
|
72
|
|
266
|
37
|
100
|
100
|
|
|
129
|
$root .= '/' unless !$root || $root =~ m#/$#; |
267
|
|
|
|
|
|
|
|
268
|
37
|
100
|
100
|
|
|
235
|
if ($root && ! -e $root) { |
269
|
2
|
|
|
|
|
26
|
warn "Root '$root' does not exist, no tests are loaded." |
270
|
|
|
|
|
|
|
} else { |
271
|
35
|
|
|
|
|
54
|
foreach my $file (@{$args{lists}}) { |
|
35
|
|
|
|
|
120
|
|
272
|
|
|
|
|
|
|
push @dirs, |
273
|
48
|
100
|
|
|
|
3515
|
map { /^\s*(?:#|$)/ ? () : $_ } |
274
|
12
|
|
|
|
|
72
|
split( /\r?\n/, _read_file("$root$file", $args{slurp_param}) ); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
find( |
278
|
114
|
100
|
|
114
|
|
3745
|
sub {push @tests, $File::Find::name if /\.t$/}, |
279
|
35
|
100
|
|
|
|
331
|
grep {-e} map {$root . $_} @dirs |
|
49
|
|
|
|
|
3346
|
|
|
49
|
|
|
|
|
171
|
|
280
|
|
|
|
|
|
|
) |
281
|
|
|
|
|
|
|
if @dirs; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
37
|
100
|
|
|
|
294
|
$args{unique} = 1 unless defined $args{unique}; |
285
|
37
|
|
100
|
|
|
186
|
$args{repeat} ||= 1; |
286
|
|
|
|
|
|
|
|
287
|
37
|
|
|
|
|
189
|
_process_run_order(\@tests, \%args); |
288
|
|
|
|
|
|
|
|
289
|
37
|
|
|
|
|
156
|
my @stack = caller(); |
290
|
37
|
|
100
|
|
|
139
|
$args{caller} = $stack[1] || 'aggregate'; |
291
|
37
|
|
|
|
|
279
|
$args{caller} =~ s#^.*?([^/]+)$#$1#; |
292
|
|
|
|
|
|
|
|
293
|
37
|
|
|
|
|
85
|
my $warnings = []; |
294
|
37
|
100
|
|
|
|
182
|
if ($args{repeat} < 0) { |
|
|
100
|
|
|
|
|
|
295
|
3
|
|
|
4
|
|
187
|
eval 'use Test2::Plugin::BailOnFail'; |
|
4
|
|
|
|
|
29
|
|
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
22
|
|
296
|
3
|
|
|
|
|
38
|
my $iter = 0; |
297
|
3
|
|
|
|
|
12
|
while (!@$warnings) { |
298
|
4
|
|
|
|
|
10
|
$iter++; |
299
|
4
|
|
|
|
|
154
|
print "Test suite iteration $iter\n"; |
300
|
4
|
100
|
|
|
|
29
|
if ($args{test_warnings}) { |
301
|
|
|
|
|
|
|
$warnings = _process_warnings( |
302
|
3
|
|
|
3
|
|
52
|
Test2::V0::warnings{_run_tests(\@tests, \%args)}, |
303
|
3
|
|
|
|
|
32
|
\%args |
304
|
|
|
|
|
|
|
); |
305
|
|
|
|
|
|
|
} else { |
306
|
1
|
|
|
|
|
4
|
_run_tests(\@tests, \%args); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
} elsif ($args{test_warnings}) { |
310
|
|
|
|
|
|
|
$warnings = _process_warnings( |
311
|
5
|
|
|
5
|
|
78
|
Test2::V0::warnings { _run_tests(\@tests, \%args) }, |
312
|
5
|
|
|
|
|
44
|
\%args |
313
|
|
|
|
|
|
|
); |
314
|
5
|
|
|
|
|
46
|
Test2::V0::is( |
315
|
|
|
|
|
|
|
@$warnings, |
316
|
|
|
|
|
|
|
0, |
317
|
|
|
|
|
|
|
'No warnings in the aggregate tests.' |
318
|
|
|
|
|
|
|
); |
319
|
|
|
|
|
|
|
} else { |
320
|
29
|
|
|
|
|
94
|
_run_tests(\@tests, \%args); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
35
|
100
|
|
|
|
6082
|
warn "Test warning output:\n".join("\n", @$warnings)."\n" |
324
|
|
|
|
|
|
|
if @$warnings; |
325
|
|
|
|
|
|
|
|
326
|
35
|
|
|
|
|
702
|
return $args{stats}; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub _read_file { |
330
|
12
|
|
|
12
|
|
45
|
my $path = shift; |
331
|
12
|
|
|
|
|
36
|
my $param = shift; |
332
|
12
|
|
|
|
|
57
|
my $file = path($path); |
333
|
12
|
100
|
|
|
|
455
|
return $param ? $file->slurp_utf8 : $file->slurp($param); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub _process_run_order { |
337
|
37
|
|
|
37
|
|
69
|
my $tests = shift; |
338
|
37
|
|
|
|
|
54
|
my $args = shift; |
339
|
|
|
|
|
|
|
|
340
|
37
|
100
|
|
|
|
128
|
@$tests = grep(!/$args->{exclude}/, @$tests) if $args->{exclude}; |
341
|
37
|
100
|
|
|
|
118
|
@$tests = grep(/$args->{include}/, @$tests) if $args->{include}; |
342
|
|
|
|
|
|
|
|
343
|
37
|
100
|
|
|
|
166
|
@$tests = _uniq(@$tests) if $args->{unique}; |
344
|
37
|
100
|
|
|
|
109
|
@$tests = reverse @$tests if $args->{reverse}; |
345
|
|
|
|
|
|
|
|
346
|
37
|
100
|
|
|
|
149
|
if ($args->{shuffle}) { |
|
|
100
|
|
|
|
|
|
347
|
1
|
|
|
|
|
7
|
require List::Util; |
348
|
1
|
|
|
|
|
7
|
@$tests = List::Util::shuffle @$tests; |
349
|
|
|
|
|
|
|
} elsif ($args->{sort}) { |
350
|
3
|
|
|
|
|
12
|
@$tests = sort @$tests; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub _process_warnings { |
355
|
8
|
|
|
8
|
|
93
|
my $warnings = shift; |
356
|
8
|
|
|
|
|
18
|
my $args = shift; |
357
|
8
|
|
|
|
|
71
|
my @warnings = split(/<-Test2::Aggregate\n/, join('',@$warnings)); |
358
|
8
|
|
|
|
|
22
|
my @clean = (); |
359
|
|
|
|
|
|
|
|
360
|
8
|
|
|
|
|
18
|
foreach my $warn (@warnings) { |
361
|
22
|
100
|
|
|
|
74
|
if ($warn =~ m/(.*)->Test2::Aggregate\n(.*\S.*)/s) { |
362
|
4
|
|
|
|
|
21
|
push @clean, "<$1>\n$2"; |
363
|
4
|
|
|
|
|
13
|
$args->{stats}->{$1}->{warnings} = $2; |
364
|
4
|
|
|
|
|
10
|
$args->{stats}->{$1}->{pass_perc} = 0; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
} |
367
|
8
|
|
|
|
|
41
|
return \@clean; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub _run_tests { |
371
|
37
|
|
|
37
|
|
57
|
my $tests = shift; |
372
|
37
|
|
|
|
|
53
|
my $args = shift; |
373
|
|
|
|
|
|
|
|
374
|
37
|
|
|
|
|
70
|
my $repeat = $args->{repeat}; |
375
|
37
|
100
|
|
|
|
98
|
$repeat = 1 if $repeat < 0; |
376
|
37
|
|
|
|
|
71
|
my (%stats, $start); |
377
|
|
|
|
|
|
|
|
378
|
37
|
100
|
|
|
|
606
|
require Time::HiRes if $args->{stats_output}; |
379
|
|
|
|
|
|
|
|
380
|
37
|
|
|
|
|
1341
|
for my $i (1 .. $repeat) { |
381
|
41
|
100
|
|
|
|
126
|
my $iter = $repeat > 1 ? "Iter: $i/$repeat - " : ''; |
382
|
41
|
|
|
|
|
63
|
my $count = 1; |
383
|
41
|
|
|
|
|
77
|
foreach my $test (@$tests) { |
384
|
|
|
|
|
|
|
|
385
|
66
|
100
|
|
|
|
276
|
warn "$test->Test2::Aggregate\n" if $args->{test_warnings}; |
386
|
|
|
|
|
|
|
|
387
|
66
|
100
|
|
|
|
317
|
$stats{$test}{test_no} = $count unless $stats{$test}{test_no}; |
388
|
66
|
100
|
|
|
|
166
|
$start = Time::HiRes::time() if $args->{stats_output}; |
389
|
66
|
|
|
|
|
178
|
$stats{$test}{timestamp} = _timestamp(); |
390
|
|
|
|
|
|
|
|
391
|
66
|
|
|
|
|
102
|
my $exec_error; |
392
|
|
|
|
|
|
|
my $result = subtest $iter. "Running test $test" => sub { |
393
|
66
|
100
|
|
66
|
|
40675
|
eval $args->{pre_eval} if $args->{pre_eval}; |
394
|
|
|
|
|
|
|
|
395
|
66
|
100
|
|
|
|
206
|
if ($args->{dry_run}) { |
396
|
2
|
|
|
|
|
7
|
Test2::V0::ok($test); |
397
|
|
|
|
|
|
|
} else { |
398
|
|
|
|
|
|
|
$args->{package} |
399
|
64
|
100
|
|
|
|
13411
|
? eval "package Test::$i" . '::' . "$count; do '$test';" |
400
|
|
|
|
|
|
|
: do $test; |
401
|
63
|
|
|
|
|
164107
|
$exec_error = $@; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
Test2::V0::is($exec_error, '', 'Execution should not fail/warn') |
404
|
65
|
100
|
100
|
|
|
946
|
if !$args->{allow_errors} && $exec_error; |
405
|
66
|
|
|
|
|
623
|
}; |
406
|
|
|
|
|
|
|
|
407
|
65
|
100
|
|
|
|
70003
|
warn "<-Test2::Aggregate\n" if $args->{test_warnings}; |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
$stats{$test}{time} += (Time::HiRes::time() - $start)/$repeat |
410
|
65
|
100
|
|
|
|
313
|
if $args->{stats_output}; |
411
|
65
|
100
|
|
|
|
285
|
$stats{$test}{pass_perc} += $result ? 100/$repeat : 0; |
412
|
65
|
|
|
|
|
161
|
$count++; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
36
|
100
|
|
|
|
129
|
_print_stats(\%stats, $args) if $args->{stats_output}; |
417
|
35
|
|
|
|
|
154
|
$args->{stats} = \%stats; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub _override { |
421
|
1
|
|
|
1
|
|
3
|
my $replace = shift; |
422
|
|
|
|
|
|
|
|
423
|
1
|
|
|
|
|
12
|
require Sub::Override; |
424
|
|
|
|
|
|
|
|
425
|
1
|
|
|
|
|
15
|
my $override = Sub::Override->new; |
426
|
1
|
|
|
|
|
10
|
$override->replace($_, $replace->{$_}) for (keys %{$replace}); |
|
1
|
|
|
|
|
8
|
|
427
|
|
|
|
|
|
|
|
428
|
1
|
|
|
|
|
63
|
return $override; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub _print_stats { |
432
|
7
|
|
|
7
|
|
20
|
my ($stats, $args) = @_; |
433
|
|
|
|
|
|
|
|
434
|
7
|
100
|
|
|
|
176
|
unless (-e $args->{stats_output}) { |
435
|
4
|
|
|
|
|
568
|
my @create = mkpath($args->{stats_output}); |
436
|
4
|
100
|
|
|
|
168
|
unless (scalar @create) { |
437
|
1
|
|
|
|
|
13
|
warn "Could not create ".$args->{stats_output}; |
438
|
1
|
|
|
|
|
7
|
return; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
6
|
|
|
|
|
34
|
my $fh; |
443
|
6
|
100
|
|
|
|
29
|
if ($args->{stats_output} =~ /^-$/) { |
444
|
2
|
|
|
|
|
7
|
$fh = *STDOUT |
445
|
|
|
|
|
|
|
} else { |
446
|
4
|
|
|
|
|
25
|
my $file = $args->{stats_output}."/".$args->{caller}."-"._timestamp().".txt"; |
447
|
4
|
100
|
|
|
|
208
|
open($fh, '>', $file) or die "Can't open > $file: $!"; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
5
|
|
|
|
|
15
|
my $total = 0; |
451
|
5
|
100
|
|
|
|
15
|
my $extra = $args->{extend_stats} ? ' TIMESTAMP' : ''; |
452
|
5
|
|
|
|
|
112
|
print $fh "TIME PASS%$extra TEST\n"; |
453
|
|
|
|
|
|
|
|
454
|
5
|
|
|
|
|
35
|
foreach my $test (sort {$stats->{$b}->{time}<=>$stats->{$a}->{time}} keys %$stats) { |
|
5
|
|
|
|
|
36
|
|
455
|
10
|
100
|
|
|
|
31
|
$extra = ' '.$stats->{$test}->{timestamp} if $args->{extend_stats}; |
456
|
10
|
|
|
|
|
19
|
$total += $stats->{$test}->{time}; |
457
|
|
|
|
|
|
|
printf $fh "%.2f %d$extra $test\n", |
458
|
10
|
|
|
|
|
162
|
$stats->{$test}->{time}, $stats->{$test}->{pass_perc}; |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
5
|
|
|
|
|
42
|
printf $fh "TOTAL TIME: %.1f sec\n", $total; |
462
|
5
|
100
|
|
|
|
113
|
close $fh unless $args->{stats_output} =~ /^-$/; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub _uniq { |
466
|
36
|
|
|
36
|
|
51
|
my %seen; |
467
|
36
|
|
|
|
|
197
|
grep !$seen{$_}++, @_; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
sub _timestamp { |
471
|
70
|
|
|
70
|
|
2294
|
my ($s, $m, $h, $D, $M, $Y) = localtime(time); |
472
|
70
|
|
|
|
|
686
|
return sprintf "%04d%02d%02dT%02d%02d%02d", $Y+1900, $M+1, $D, $h, $m, $s; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=head1 USAGE NOTES |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Not all tests can be modified to run under the aggregator, it is not intended |
478
|
|
|
|
|
|
|
for tests that require an isolated environment, do overrides etc. For other tests |
479
|
|
|
|
|
|
|
which can potentially run under the aggregator, sometimes very simple changes may be |
480
|
|
|
|
|
|
|
needed like giving unique names to subs (or not warning for redefines, or trying the |
481
|
|
|
|
|
|
|
package option), replacing things that complain, restoring the environment at |
482
|
|
|
|
|
|
|
the end of the test etc. |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Unit tests are usually great for aggregating. You could use the hash that C |
485
|
|
|
|
|
|
|
returns in a script that tries to add more tests automatically to an aggregate list |
486
|
|
|
|
|
|
|
to see which added tests passed and keep them, dropping failures. See later in the |
487
|
|
|
|
|
|
|
notes for a detailed example. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Trying to aggregate too many tests into a single one can be counter-intuitive as |
490
|
|
|
|
|
|
|
you would ideally want to parallelize your test suite (so a super-long aggregated |
491
|
|
|
|
|
|
|
test continuing after the rest are done will slow down the suite). And in general |
492
|
|
|
|
|
|
|
more tests will run aggregated if they are grouped so that tests that can't be |
493
|
|
|
|
|
|
|
aggregated together are in different groups. |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
In general you can call C multiple times in a test and |
496
|
|
|
|
|
|
|
even load C with tests that already contain another C, the |
497
|
|
|
|
|
|
|
only real issue with multiple calls is that if you use C on a call, |
498
|
|
|
|
|
|
|
L is loaded so any subsequent failure, on any following |
499
|
|
|
|
|
|
|
C call will trigger a Bail. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=head2 Test::More |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
If you haven't switched to the L you are generally advised to do so |
504
|
|
|
|
|
|
|
for a number of reasons, compatibility with this module being only a very minor |
505
|
|
|
|
|
|
|
one. If you are stuck with a L suite, L can still |
506
|
|
|
|
|
|
|
probably help you more than the similarly-named C modules. |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
Although the module tries to load C with minimal imports to not interfere, |
509
|
|
|
|
|
|
|
it is generally better to do C |
510
|
|
|
|
|
|
|
alongside with C |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head2 BEGIN / END Blocks |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
C / C blocks will run at the start/end of each test and any overrides |
515
|
|
|
|
|
|
|
etc you might have set will apply to the rest of the tests, so if you use them you |
516
|
|
|
|
|
|
|
probably need to make changes for aggregation. An example of such a change is when |
517
|
|
|
|
|
|
|
you have a C<*GLOBAL::CORE::exit> override to test scripts that can call C. |
518
|
|
|
|
|
|
|
A solution is to use something like L: |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
BEGIN { |
521
|
|
|
|
|
|
|
unless ($Test::Trap::VERSION) { # Avoid warnings for multiple loads in aggregation |
522
|
|
|
|
|
|
|
require Test::Trap; |
523
|
|
|
|
|
|
|
Test::Trap->import(); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head2 Test::Class |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
L is sort of an aggregator itself. You make your tests into modules |
530
|
|
|
|
|
|
|
and then load them on the same C<.t> file, so ideally you will not end up with many |
531
|
|
|
|
|
|
|
C<.t> files that would require further aggregation. If you do, due to the L |
532
|
|
|
|
|
|
|
implementation specifics, those C<.t> files won't run under L. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=head2 $ENV{AGGREGATE_TESTS} |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
The environment variable C will be set while the tests are running |
537
|
|
|
|
|
|
|
for your convenience. Example usage is making a test you know cannot run under the |
538
|
|
|
|
|
|
|
aggregator check and croak if it was run under it, or a module that can only be loaded |
539
|
|
|
|
|
|
|
once, so you load it on the aggregated test file and then use something like this in |
540
|
|
|
|
|
|
|
the individual test files: |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
eval 'use My::Module' unless $ENV{AGGREGATE_TESTS}; |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
If you have a custom test bundle, you could use the variable to do things like |
545
|
|
|
|
|
|
|
disable warnings on redefines only for tests that run aggregated: |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
use Import::Into; |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
sub import { |
550
|
|
|
|
|
|
|
... |
551
|
|
|
|
|
|
|
'warnings'->unimport::out_of($package, 'redefine') |
552
|
|
|
|
|
|
|
if $ENV{AGGREGATE_TESTS}; |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
Another idea is to make the test die when it is run under the aggregator, if, at |
556
|
|
|
|
|
|
|
design time, you know it is not supposed to run aggregated. |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=head2 Example aggregating strategy |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
There are many approaches you could do to use C with an existing |
561
|
|
|
|
|
|
|
test suite, so for example you can start by making a list of the test files you |
562
|
|
|
|
|
|
|
are trying to aggregate: |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
find t -name '*.t' > all.lst |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
If you have a substantial test suite, perhaps try with a portion of it (a subdir?) |
567
|
|
|
|
|
|
|
instead of the entire suite. In any case, try running them aggregated like this: |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
use Test2::Aggregate; |
570
|
|
|
|
|
|
|
use Test2::V0; # Or Test::More; |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
my $stats = Test2::Aggregate::run_tests( |
573
|
|
|
|
|
|
|
lists => ['all.lst'], |
574
|
|
|
|
|
|
|
); |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
open OUT, ">pass.lst"; |
577
|
|
|
|
|
|
|
foreach my $test (sort {$stats->{$a}->{test_no} <=> $stats->{$b}->{test_no}} keys %$stats) { |
578
|
|
|
|
|
|
|
print OUT "$test\n" if $stats->{$test}->{pass_perc}; |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
close OUT; |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
done_testing(); |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
Run the above with C or C in verbose mode, so that in case the run |
585
|
|
|
|
|
|
|
hangs (it can happen), you can see where it did so and edit C removing |
586
|
|
|
|
|
|
|
the offending test. |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
If the run completes, you have a "starting point" - i.e. a list that can run under |
589
|
|
|
|
|
|
|
the aggregator in C. |
590
|
|
|
|
|
|
|
You can try adding back some of the failed tests - test failures can be cascading, |
591
|
|
|
|
|
|
|
so some might be passing if added back, or have small issues you can address. |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
Try adding C 1> to C to fix warnings as well, unless |
594
|
|
|
|
|
|
|
it is common for your tests to have C output. |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
To have your entire suite run aggregated tests together once and not repeat them |
597
|
|
|
|
|
|
|
along with the other, non-aggregated, tests, it is a good idea to use the |
598
|
|
|
|
|
|
|
C<--exclude-list> option of the C. |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
Hopefully your tests can run in parallel (C), in which case you |
601
|
|
|
|
|
|
|
would split your aggregated tests into multiple lists to have them run in parallel. |
602
|
|
|
|
|
|
|
Here is an example of a wrapper around C, to easily handle multiple lists: |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
BEGIN { |
605
|
|
|
|
|
|
|
my @args = (); |
606
|
|
|
|
|
|
|
foreach (@ARGV) { |
607
|
|
|
|
|
|
|
if (/--exclude-lists=(\S+)/) { |
608
|
|
|
|
|
|
|
my $all = 't/aggregate/aggregated.tests'; |
609
|
|
|
|
|
|
|
`awk '{print "t/"\$0}' $1 > $all`; |
610
|
|
|
|
|
|
|
push @args, "--exclude-list=$all"; |
611
|
|
|
|
|
|
|
} else { push @args, $_ if $_; } |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
push @args, qw(-P...) # Preload module list (useful for non-aggregated tests) |
614
|
|
|
|
|
|
|
unless grep {/--cover/} @args; |
615
|
|
|
|
|
|
|
@ARGV = @args; |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
exec ('yath', @ARGV); |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
You would call it with something like C<--exclude-lists=t/aggregate/*.lst>, and |
620
|
|
|
|
|
|
|
the tests listed will be excluded (you will have them running aggregated through |
621
|
|
|
|
|
|
|
their own C<.t> files using L). |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head1 AUTHOR |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Dimitrios Kechagias, C<< >> |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=head1 BUGS |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, |
630
|
|
|
|
|
|
|
or through the web interface at L. |
631
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on your |
632
|
|
|
|
|
|
|
bug as I make changes. You could also submit issues or even pull requests to the |
633
|
|
|
|
|
|
|
github repo (see below). |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
=head1 GIT |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
L |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Copyright (C) 2019, SpareRoom.com |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
This program is free software; you can redistribute |
644
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=cut |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
1; |