line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test2::Aggregate; |
2
|
|
|
|
|
|
|
|
3
|
16
|
|
|
16
|
|
3762359
|
use strict; |
|
16
|
|
|
|
|
119
|
|
|
16
|
|
|
|
|
487
|
|
4
|
16
|
|
|
16
|
|
88
|
use warnings; |
|
16
|
|
|
|
|
35
|
|
|
16
|
|
|
|
|
454
|
|
5
|
|
|
|
|
|
|
|
6
|
16
|
|
|
16
|
|
86
|
use File::Find; |
|
16
|
|
|
|
|
33
|
|
|
16
|
|
|
|
|
1029
|
|
7
|
16
|
|
|
16
|
|
100
|
use File::Path; |
|
16
|
|
|
|
|
30
|
|
|
16
|
|
|
|
|
1076
|
|
8
|
16
|
|
|
16
|
|
14601
|
use Path::Tiny; |
|
16
|
|
|
|
|
195038
|
|
|
16
|
|
|
|
|
997
|
|
9
|
|
|
|
|
|
|
|
10
|
16
|
|
|
16
|
|
753
|
use Test2::V0 'subtest'; |
|
16
|
|
|
|
|
152387
|
|
|
16
|
|
|
|
|
131
|
|
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.16_0 |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=cut |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
our $VERSION = '0.16_0'; |
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 iterativelly 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
|
|
|
|
|
|
|
By default, C will be used to read your test list files. |
205
|
|
|
|
|
|
|
If you would like to use C with your own parameters instead, pass them |
206
|
|
|
|
|
|
|
here. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=item * C (optional) |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
String with code to run with eval before each test. You might be inclined to do |
211
|
|
|
|
|
|
|
this for example: |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
pre_eval => "no warnings 'redefine';" |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
You might expect it to silence redefine warnings (when you have similarly named |
216
|
|
|
|
|
|
|
subs on many tests), but even if you don't set warnings explicitly in your tests, |
217
|
|
|
|
|
|
|
most test bundles will set warnings automatically for you (e.g. for L |
218
|
|
|
|
|
|
|
you'd have to do C |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item * C (optional) |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
C specifies a path where a file will be created to print out |
223
|
|
|
|
|
|
|
running time per test (average if multiple iterations) and passing percentage. |
224
|
|
|
|
|
|
|
Output is sorted from slowest test to fastest. On negative C the stats |
225
|
|
|
|
|
|
|
of each successful run will be written separately instead of the averages. |
226
|
|
|
|
|
|
|
The name of the file is C. |
227
|
|
|
|
|
|
|
If C<'-'> is passed instead of a path, then the output will be written to STDOUT. |
228
|
|
|
|
|
|
|
The timing stats are useful because the test harness doesn't normally measure |
229
|
|
|
|
|
|
|
time per subtest (remember, your individual aggregated tests become subtests). |
230
|
|
|
|
|
|
|
If you prefer to capture the hash output of the function and use that for your |
231
|
|
|
|
|
|
|
reports, you still need to define C to enable timing (just send |
232
|
|
|
|
|
|
|
the output to C, C etc). |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item * C (optional) |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
This option exist to make the default output format of C be fixed, |
237
|
|
|
|
|
|
|
but still allow additions in future versions that will only be written with the |
238
|
|
|
|
|
|
|
C option enabled. |
239
|
|
|
|
|
|
|
Additions with C as of the current version: |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=over 4 |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
- starting date/time in ISO_8601. |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=back |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=back |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub run_tests { |
252
|
38
|
|
|
38
|
1
|
125988
|
my %args = @_; |
253
|
|
|
|
|
|
|
Test2::V0::plan skip_all => 'Skipping slow tests.' |
254
|
38
|
100
|
100
|
|
|
178
|
if $args{slow} && $ENV{SKIP_SLOW}; |
255
|
|
|
|
|
|
|
|
256
|
37
|
|
|
|
|
72
|
eval "use $_;" foreach @{$args{load_modules}}; |
|
37
|
|
|
|
|
194
|
|
257
|
37
|
|
|
|
|
1209
|
local $ENV{AGGREGATE_TESTS} = 1; |
258
|
|
|
|
|
|
|
|
259
|
37
|
100
|
|
|
|
142
|
my $override = $args{override} ? _override($args{override}) : undef; |
260
|
37
|
|
|
|
|
89
|
my @dirs = (); |
261
|
37
|
|
100
|
|
|
195
|
my $root = $args{root} || ''; |
262
|
37
|
|
|
|
|
67
|
my @tests; |
263
|
|
|
|
|
|
|
|
264
|
37
|
100
|
|
|
|
127
|
@dirs = @{$args{dirs}} if $args{dirs}; |
|
27
|
|
|
|
|
89
|
|
265
|
37
|
100
|
100
|
|
|
168
|
$root .= '/' unless !$root || $root =~ m#/$#; |
266
|
|
|
|
|
|
|
|
267
|
37
|
100
|
100
|
|
|
237
|
if ($root && ! -e $root) { |
268
|
2
|
|
|
|
|
29
|
warn "Root '$root' does not exist, no tests are loaded." |
269
|
|
|
|
|
|
|
} else { |
270
|
35
|
|
|
|
|
67
|
foreach my $file (@{$args{lists}}) { |
|
35
|
|
|
|
|
135
|
|
271
|
|
|
|
|
|
|
push @dirs, |
272
|
36
|
100
|
|
|
|
3963
|
map { /^\s*#/ ? () : $_ } |
273
|
12
|
|
|
|
|
84
|
split( /\r?\n/, read_file("$root$file", $args{slurp_param}) ); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
find( |
277
|
114
|
100
|
|
114
|
|
4744
|
sub {push @tests, $File::Find::name if /\.t$/}, |
278
|
35
|
100
|
|
|
|
364
|
grep {-e} map {$root . $_} @dirs |
|
49
|
|
|
|
|
4282
|
|
|
49
|
|
|
|
|
193
|
|
279
|
|
|
|
|
|
|
) |
280
|
|
|
|
|
|
|
if @dirs; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
37
|
100
|
|
|
|
339
|
$args{unique} = 1 unless defined $args{unique}; |
284
|
37
|
|
100
|
|
|
212
|
$args{repeat} ||= 1; |
285
|
|
|
|
|
|
|
|
286
|
37
|
|
|
|
|
202
|
_process_run_order(\@tests, \%args); |
287
|
|
|
|
|
|
|
|
288
|
37
|
|
|
|
|
148
|
my @stack = caller(); |
289
|
37
|
|
100
|
|
|
161
|
$args{caller} = $stack[1] || 'aggregate'; |
290
|
37
|
|
|
|
|
392
|
$args{caller} =~ s#^.*?([^/]+)$#$1#; |
291
|
|
|
|
|
|
|
|
292
|
37
|
|
|
|
|
120
|
my $warnings = []; |
293
|
37
|
100
|
|
|
|
157
|
if ($args{repeat} < 0) { |
|
|
100
|
|
|
|
|
|
294
|
3
|
|
|
4
|
|
221
|
eval 'use Test2::Plugin::BailOnFail'; |
|
4
|
|
|
|
|
38
|
|
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
25
|
|
295
|
3
|
|
|
|
|
51
|
my $iter = 0; |
296
|
3
|
|
|
|
|
15
|
while (!@$warnings) { |
297
|
4
|
|
|
|
|
13
|
$iter++; |
298
|
4
|
|
|
|
|
320
|
print "Test suite iteration $iter\n"; |
299
|
4
|
100
|
|
|
|
30
|
if ($args{test_warnings}) { |
300
|
|
|
|
|
|
|
$warnings = _process_warnings( |
301
|
3
|
|
|
3
|
|
50
|
Test2::V0::warnings{_run_tests(\@tests, \%args)}, |
302
|
3
|
|
|
|
|
27
|
\%args |
303
|
|
|
|
|
|
|
); |
304
|
|
|
|
|
|
|
} else { |
305
|
1
|
|
|
|
|
5
|
_run_tests(\@tests, \%args); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
} elsif ($args{test_warnings}) { |
309
|
|
|
|
|
|
|
$warnings = _process_warnings( |
310
|
5
|
|
|
5
|
|
72
|
Test2::V0::warnings { _run_tests(\@tests, \%args) }, |
311
|
5
|
|
|
|
|
34
|
\%args |
312
|
|
|
|
|
|
|
); |
313
|
5
|
|
|
|
|
43
|
Test2::V0::is( |
314
|
|
|
|
|
|
|
@$warnings, |
315
|
|
|
|
|
|
|
0, |
316
|
|
|
|
|
|
|
'No warnings in the aggregate tests.' |
317
|
|
|
|
|
|
|
); |
318
|
|
|
|
|
|
|
} else { |
319
|
29
|
|
|
|
|
106
|
_run_tests(\@tests, \%args); |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
35
|
100
|
|
|
|
7645
|
warn "Test warning output:\n".join("\n", @$warnings)."\n" |
323
|
|
|
|
|
|
|
if @$warnings; |
324
|
|
|
|
|
|
|
|
325
|
35
|
|
|
|
|
887
|
return $args{stats}; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub read_file { |
329
|
12
|
|
|
12
|
0
|
40
|
my $path = shift; |
330
|
12
|
|
|
|
|
37
|
my $param = shift; |
331
|
12
|
|
|
|
|
61
|
my $file = path($path); |
332
|
12
|
100
|
|
|
|
632
|
return $param ? $file->slurp_utf8 : $file->slurp($param); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub _process_run_order { |
336
|
37
|
|
|
37
|
|
72
|
my $tests = shift; |
337
|
37
|
|
|
|
|
61
|
my $args = shift; |
338
|
|
|
|
|
|
|
|
339
|
37
|
100
|
|
|
|
153
|
@$tests = grep(!/$args->{exclude}/, @$tests) if $args->{exclude}; |
340
|
37
|
100
|
|
|
|
137
|
@$tests = grep(/$args->{include}/, @$tests) if $args->{include}; |
341
|
|
|
|
|
|
|
|
342
|
37
|
100
|
|
|
|
170
|
@$tests = _uniq(@$tests) if $args->{unique}; |
343
|
37
|
100
|
|
|
|
139
|
@$tests = reverse @$tests if $args->{reverse}; |
344
|
|
|
|
|
|
|
|
345
|
37
|
100
|
|
|
|
153
|
if ($args->{shuffle}) { |
|
|
100
|
|
|
|
|
|
346
|
1
|
|
|
|
|
9
|
require List::Util; |
347
|
1
|
|
|
|
|
8
|
@$tests = List::Util::shuffle @$tests; |
348
|
|
|
|
|
|
|
} elsif ($args->{sort}) { |
349
|
3
|
|
|
|
|
13
|
@$tests = sort @$tests; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
sub _process_warnings { |
354
|
8
|
|
|
8
|
|
71
|
my $warnings = shift; |
355
|
8
|
|
|
|
|
16
|
my $args = shift; |
356
|
8
|
|
|
|
|
58
|
my @warnings = split(/<-Test2::Aggregate\n/, join('',@$warnings)); |
357
|
8
|
|
|
|
|
20
|
my @clean = (); |
358
|
|
|
|
|
|
|
|
359
|
8
|
|
|
|
|
17
|
foreach my $warn (@warnings) { |
360
|
22
|
100
|
|
|
|
80
|
if ($warn =~ m/(.*)->Test2::Aggregate\n(.*\S.*)/s) { |
361
|
4
|
|
|
|
|
22
|
push @clean, "<$1>\n$2"; |
362
|
4
|
|
|
|
|
13
|
$args->{stats}->{$1}->{warnings} = $2; |
363
|
4
|
|
|
|
|
11
|
$args->{stats}->{$1}->{pass_perc} = 0; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
8
|
|
|
|
|
36
|
return \@clean; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub _run_tests { |
370
|
37
|
|
|
37
|
|
66
|
my $tests = shift; |
371
|
37
|
|
|
|
|
69
|
my $args = shift; |
372
|
|
|
|
|
|
|
|
373
|
37
|
|
|
|
|
90
|
my $repeat = $args->{repeat}; |
374
|
37
|
100
|
|
|
|
121
|
$repeat = 1 if $repeat < 0; |
375
|
37
|
|
|
|
|
86
|
my (%stats, $start); |
376
|
|
|
|
|
|
|
|
377
|
37
|
100
|
|
|
|
885
|
require Time::HiRes if $args->{stats_output}; |
378
|
|
|
|
|
|
|
|
379
|
37
|
|
|
|
|
2033
|
for my $i (1 .. $repeat) { |
380
|
41
|
100
|
|
|
|
148
|
my $iter = $repeat > 1 ? "Iter: $i/$repeat - " : ''; |
381
|
41
|
|
|
|
|
71
|
my $count = 1; |
382
|
41
|
|
|
|
|
103
|
foreach my $test (@$tests) { |
383
|
|
|
|
|
|
|
|
384
|
66
|
100
|
|
|
|
291
|
warn "$test->Test2::Aggregate\n" if $args->{test_warnings}; |
385
|
|
|
|
|
|
|
|
386
|
66
|
100
|
|
|
|
391
|
$stats{$test}{test_no} = $count unless $stats{$test}{test_no}; |
387
|
66
|
100
|
|
|
|
220
|
$start = Time::HiRes::time() if $args->{stats_output}; |
388
|
66
|
|
|
|
|
171
|
$stats{$test}{timestamp} = _timestamp(); |
389
|
|
|
|
|
|
|
|
390
|
66
|
|
|
|
|
168
|
my $exec_error; |
391
|
|
|
|
|
|
|
my $result = subtest $iter. "Running test $test" => sub { |
392
|
66
|
100
|
|
66
|
|
46497
|
eval $args->{pre_eval} if $args->{pre_eval}; |
393
|
|
|
|
|
|
|
|
394
|
66
|
100
|
|
|
|
215
|
if ($args->{dry_run}) { |
395
|
2
|
|
|
|
|
8
|
Test2::V0::ok($test); |
396
|
|
|
|
|
|
|
} else { |
397
|
|
|
|
|
|
|
$args->{package} |
398
|
64
|
100
|
|
|
|
15894
|
? eval "package Test::$i" . '::' . "$count; do '$test';" |
399
|
|
|
|
|
|
|
: do $test; |
400
|
63
|
|
|
|
|
195726
|
$exec_error = $@; |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
Test2::V0::is($exec_error, '', 'Execution should not fail/warn') |
403
|
65
|
100
|
100
|
|
|
1161
|
if !$args->{allow_errors} && $exec_error; |
404
|
66
|
|
|
|
|
725
|
}; |
405
|
|
|
|
|
|
|
|
406
|
65
|
100
|
|
|
|
88326
|
warn "<-Test2::Aggregate\n" if $args->{test_warnings}; |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
$stats{$test}{time} += (Time::HiRes::time() - $start)/$repeat |
409
|
65
|
100
|
|
|
|
332
|
if $args->{stats_output}; |
410
|
65
|
100
|
|
|
|
306
|
$stats{$test}{pass_perc} += $result ? 100/$repeat : 0; |
411
|
65
|
|
|
|
|
178
|
$count++; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
|
415
|
36
|
100
|
|
|
|
141
|
_print_stats(\%stats, $args) if $args->{stats_output}; |
416
|
35
|
|
|
|
|
178
|
$args->{stats} = \%stats; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub _override { |
420
|
1
|
|
|
1
|
|
3
|
my $replace = shift; |
421
|
|
|
|
|
|
|
|
422
|
1
|
|
|
|
|
12
|
require Sub::Override; |
423
|
|
|
|
|
|
|
|
424
|
1
|
|
|
|
|
13
|
my $override = Sub::Override->new; |
425
|
1
|
|
|
|
|
12
|
$override->replace($_, $replace->{$_}) for (keys %{$replace}); |
|
1
|
|
|
|
|
10
|
|
426
|
|
|
|
|
|
|
|
427
|
1
|
|
|
|
|
68
|
return $override; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
sub _print_stats { |
431
|
7
|
|
|
7
|
|
24
|
my ($stats, $args) = @_; |
432
|
|
|
|
|
|
|
|
433
|
7
|
100
|
|
|
|
266
|
unless (-e $args->{stats_output}) { |
434
|
4
|
|
|
|
|
524
|
my @create = mkpath($args->{stats_output}); |
435
|
4
|
100
|
|
|
|
285
|
unless (scalar @create) { |
436
|
1
|
|
|
|
|
23
|
warn "Could not create ".$args->{stats_output}; |
437
|
1
|
|
|
|
|
8
|
return; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
6
|
|
|
|
|
42
|
my $fh; |
442
|
6
|
100
|
|
|
|
30
|
if ($args->{stats_output} =~ /^-$/) { |
443
|
2
|
|
|
|
|
9
|
$fh = *STDOUT |
444
|
|
|
|
|
|
|
} else { |
445
|
4
|
|
|
|
|
36
|
my $file = $args->{stats_output}."/".$args->{caller}."-"._timestamp().".txt"; |
446
|
4
|
100
|
|
|
|
265
|
open($fh, '>', $file) or die "Can't open > $file: $!"; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
5
|
|
|
|
|
16
|
my $total = 0; |
450
|
5
|
100
|
|
|
|
18
|
my $extra = $args->{extend_stats} ? ' TIMESTAMP' : ''; |
451
|
5
|
|
|
|
|
109
|
print $fh "TIME PASS%$extra TEST\n"; |
452
|
|
|
|
|
|
|
|
453
|
5
|
|
|
|
|
38
|
foreach my $test (sort {$stats->{$b}->{time}<=>$stats->{$a}->{time}} keys %$stats) { |
|
5
|
|
|
|
|
30
|
|
454
|
10
|
100
|
|
|
|
32
|
$extra = ' '.$stats->{$test}->{timestamp} if $args->{extend_stats}; |
455
|
10
|
|
|
|
|
20
|
$total += $stats->{$test}->{time}; |
456
|
|
|
|
|
|
|
printf $fh "%.2f %d$extra $test\n", |
457
|
10
|
|
|
|
|
145
|
$stats->{$test}->{time}, $stats->{$test}->{pass_perc}; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
5
|
|
|
|
|
46
|
printf $fh "TOTAL TIME: %.1f sec\n", $total; |
461
|
5
|
100
|
|
|
|
111
|
close $fh unless $args->{stats_output} =~ /^-$/; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub _uniq { |
465
|
36
|
|
|
36
|
|
59
|
my %seen; |
466
|
36
|
|
|
|
|
225
|
grep !$seen{$_}++, @_; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
sub _timestamp { |
470
|
70
|
|
|
70
|
|
2325
|
my ($s, $m, $h, $D, $M, $Y) = localtime(time); |
471
|
70
|
|
|
|
|
775
|
return sprintf "%04d%02d%02dT%02d%02d%02d", $Y+1900, $M+1, $D, $h, $m, $s; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head1 USAGE NOTES |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
Not all tests can be modified to run under the aggregator, it is not intended |
477
|
|
|
|
|
|
|
for tests that require an isolated environment, do overrides etc. For other tests |
478
|
|
|
|
|
|
|
which can potentially run under the aggregator, sometimes very simple changes may be |
479
|
|
|
|
|
|
|
needed like giving unique names to subs (or not warning for redefines, or trying the |
480
|
|
|
|
|
|
|
package option), replacing things that complain, restoring the environment at |
481
|
|
|
|
|
|
|
the end of the test etc. |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Unit tests are usually great for aggregating. You could use the hash that C |
484
|
|
|
|
|
|
|
returns in a script that tries to add more tests automatically to an aggregate list |
485
|
|
|
|
|
|
|
to see which added tests passed and keep them, dropping failures. See later in the |
486
|
|
|
|
|
|
|
notes for a detailed example. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Trying to aggregate too many tests into a single one can be counter-intuitive as |
489
|
|
|
|
|
|
|
you would ideally want to parallelize your test suite (so a super-long aggregated |
490
|
|
|
|
|
|
|
test continuing after the rest are done will slow down the suite). And in general |
491
|
|
|
|
|
|
|
more tests will run aggregated if they are grouped so that tests that can't be |
492
|
|
|
|
|
|
|
aggregated together are in different groups. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
In general you can call C multiple times in a test and |
495
|
|
|
|
|
|
|
even load C with tests that already contain another C, the |
496
|
|
|
|
|
|
|
only real issue with multiple calls is that if you use C on a call, |
497
|
|
|
|
|
|
|
L is loaded so any subsequent failure, on any following |
498
|
|
|
|
|
|
|
C call will trigger a Bail. |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=head2 Test::More |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
If you haven't switched to the L you are generally advised to do so |
503
|
|
|
|
|
|
|
for a number of reasons, compatibility with this module being only a very minor |
504
|
|
|
|
|
|
|
one. If you are stuck with a L suite, L can still |
505
|
|
|
|
|
|
|
probably help you more than the similarly-named C modules. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Although the module tries to load C with minimal imports to not interfere, |
508
|
|
|
|
|
|
|
it is generally better to do C |
509
|
|
|
|
|
|
|
alongside with C |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=head2 BEGIN / END Blocks |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
C / C blocks will run at the start/end of each test and any overrides |
514
|
|
|
|
|
|
|
etc you might have set will apply to the rest of the tests, so if you use them you |
515
|
|
|
|
|
|
|
probably need to make changes for aggregation. An example of such a change is when |
516
|
|
|
|
|
|
|
you have a C<*GLOBAL::CORE::exit> override to test scripts that can call C. |
517
|
|
|
|
|
|
|
A solution is to use something like L: |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
BEGIN { |
520
|
|
|
|
|
|
|
unless ($Test::Trap::VERSION) { # Avoid warnings for multiple loads in aggregation |
521
|
|
|
|
|
|
|
require Test::Trap; |
522
|
|
|
|
|
|
|
Test::Trap->import(); |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head2 Test::Class |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
L is sort of an aggregator itself. You make your tests into modules |
529
|
|
|
|
|
|
|
and then load them on the same C<.t> file, so ideally you will not end up with many |
530
|
|
|
|
|
|
|
C<.t> files that would require further aggregation. If you do, due to the L |
531
|
|
|
|
|
|
|
implementation specifics, those C<.t> files won't run under L. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=head2 $ENV{AGGREGATE_TESTS} |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
The environment variable C will be set while the tests are running |
536
|
|
|
|
|
|
|
for your convenience. Example usage is making a test you know cannot run under the |
537
|
|
|
|
|
|
|
aggregator check and croak if it was run under it, or a module that can only be loaded |
538
|
|
|
|
|
|
|
once, so you load it on the aggregated test file and then use something like this in |
539
|
|
|
|
|
|
|
the individual test files: |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
eval 'use My::Module' unless $ENV{AGGREGATE_TESTS}; |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
If you have a custom test bundle, you could use the variable to do things like |
544
|
|
|
|
|
|
|
disable warnings on redefines only for tests that run aggregated: |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
use Import::Into; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
sub import { |
549
|
|
|
|
|
|
|
... |
550
|
|
|
|
|
|
|
'warnings'->unimport::out_of($package, 'redefine') |
551
|
|
|
|
|
|
|
if $ENV{AGGREGATE_TESTS}; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
Another idea is to make the test die when it is run under the aggregator, if, at |
555
|
|
|
|
|
|
|
design time, you know it is not supposed to run aggregated. |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=head2 Example aggregating strategy |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
There are many approaches you could do to use C with an existing |
560
|
|
|
|
|
|
|
test suite, so for example you can start by making a list of the test files you |
561
|
|
|
|
|
|
|
are trying to aggregate: |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
find t -name '*.t' > all.lst |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
If you have a substantial test suite, perhaps try with a portion of it (a subdir?) |
566
|
|
|
|
|
|
|
instead of the entire suite. In any case, try running them aggregated like this: |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
use Test2::Aggregate; |
569
|
|
|
|
|
|
|
use Test2::V0; # Or Test::More; |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
my $stats = Test2::Aggregate::run_tests( |
572
|
|
|
|
|
|
|
lists => ['all.lst'], |
573
|
|
|
|
|
|
|
); |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
open OUT, ">pass.lst"; |
576
|
|
|
|
|
|
|
foreach my $test (sort {$stats->{$a}->{test_no} <=> $stats->{$b}->{test_no}} keys %$stats) { |
577
|
|
|
|
|
|
|
print OUT "$test\n" if $stats->{$test}->{pass_perc}; |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
close OUT; |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
done_testing(); |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Run the above with C or C in verbose mode, so that in case the run |
584
|
|
|
|
|
|
|
hangs (it can happen), you can see where it did so and edit C removing |
585
|
|
|
|
|
|
|
the offending test. |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
If the run completes, you have a "starting point" - i.e. a list that can run under |
588
|
|
|
|
|
|
|
the aggregator in C. |
589
|
|
|
|
|
|
|
You can try adding back some of the failed tests - test failures can be cascading, |
590
|
|
|
|
|
|
|
so some might be passing if added back, or have small issues you can address. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
Try adding C 1> to C to fix warnings as well, unless |
593
|
|
|
|
|
|
|
it is common for your tests to have C output. |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
To have your entire suite run aggregated tests together once and not repeat them |
596
|
|
|
|
|
|
|
along with the other, non-aggregated, tests, it is a good idea to use the |
597
|
|
|
|
|
|
|
C<--exclude-list> option of the C. |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
Hopefully your tests can run in parallel (C), in which case you |
600
|
|
|
|
|
|
|
would split your aggregated tests into multiple lists to have them run in parallel. |
601
|
|
|
|
|
|
|
Here is an example of a wrapper around C, to easily handle multiple lists: |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
BEGIN { |
604
|
|
|
|
|
|
|
my @args = (); |
605
|
|
|
|
|
|
|
foreach (@ARGV) { |
606
|
|
|
|
|
|
|
if (/--exclude-lists=(\S+)/) { |
607
|
|
|
|
|
|
|
my $all = 't/aggregate/aggregated.tests'; |
608
|
|
|
|
|
|
|
`awk '{print "t/"\$0}' $1 > $all`; |
609
|
|
|
|
|
|
|
push @args, "--exclude-list=$all"; |
610
|
|
|
|
|
|
|
} else { push @args, $_ if $_; } |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
push @args, qw(-P...) # Preload module list (useful for non-aggregated tests) |
613
|
|
|
|
|
|
|
unless grep {/--cover/} @args; |
614
|
|
|
|
|
|
|
@ARGV = @args; |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
exec ('yath', @ARGV); |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
You would call it with something like C<--exclude-lists=t/aggregate/*.lst>, and |
619
|
|
|
|
|
|
|
the tests listed will be excluded (you will have them running aggregated through |
620
|
|
|
|
|
|
|
their own C<.t> files using L). |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head1 AUTHOR |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Dimitrios Kechagias, C<< >> |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head1 BUGS |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, |
629
|
|
|
|
|
|
|
or through the web interface at L. |
630
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on your |
631
|
|
|
|
|
|
|
bug as I make changes. You could also submit issues or even pull requests to the |
632
|
|
|
|
|
|
|
github repo (see below). |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head1 GIT |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
L |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
Copyright (C) 2019, SpareRoom.com |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
This program is free software; you can redistribute |
643
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=cut |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
1; |