Skip to content

Commit a0d4e1e

Browse files
committed
Write stats per file when repeat==1
1 parent 372fbb7 commit a0d4e1e

File tree

8 files changed

+118
-49
lines changed

8 files changed

+118
-49
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ jobs:
3232
- run: cpanm --notest --installdeps --verbose .
3333
- run: perl Makefile.PL
3434
- run: make
35-
- run: prove -wlvmb t
35+
- run: prove -wlvmb t

Changes

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
Revision history for Test2-Aggregate
22

3+
0.18 2024-06-29
4+
agg helper script.
5+
'pass_only' option.
6+
Write stats per test file (repeat==1) to avoid losing them on SIG exit.
7+
38
0.17 2022-02-10
49
Ignore empty lines in list files.
510

MANIFEST.SKIP

+2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ MYMETA.*
77
_build/
88
cover_db/
99
blib/
10+
.github/
1011
.git/
12+
.gitignore/
1113
inc/
1214
.lwpcookies
1315
.last_cover_stats

README.pod

+56-29
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Test2::Aggregate - Aggregate tests for increased speed
1515

1616
=head1 VERSION
1717

18-
Version 0.17
18+
Version 0.18
1919

2020
=head1 DESCRIPTION
2121

@@ -64,6 +64,7 @@ have less issues with L<Test2::Suite> (see notes).
6464
override => \%override, # optional, requires Sub::Override
6565
stats_output => $stats_output_path, # optional
6666
extend_stats => 0, # optional
67+
pass_only => 0, # optional
6768
test_warnings => 0, # optional
6869
allow_errors => 0, # optional
6970
pre_eval => $code_to_eval, # optional
@@ -219,11 +220,17 @@ the output to C</dev/null>, C</tmp> etc).
219220

220221
=item * C<extend_stats> (optional)
221222

222-
This option exists to make the default output format of C<stats_output> be fixed,
223+
This option exist to make the default output format of C<stats_output> be fixed,
223224
but still allow additions in future versions that will only be written with the
224225
C<extend_stats> option enabled.
225226
Additions with C<extend_stats> as of the current version:
226227

228+
=item * C<pass_only> (optional)
229+
230+
Modifies C<stats_output> by making it only print out a list of passing tests.
231+
Useful for creating lists of aggregateable tests.
232+
Has no effect if C<stats_output> is not defined.
233+
227234
=over 4
228235

229236
- starting date/time in ISO_8601.
@@ -315,47 +322,67 @@ disable warnings on redefines only for tests that run aggregated:
315322
Another idea is to make the test die when it is run under the aggregator, if, at
316323
design time, you know it is not supposed to run aggregated.
317324

325+
=head2 agg helper script
326+
327+
agg [options] <file/dir ...>
328+
329+
Pass a list of Perl test files/directories and they will run aggregated via yath
330+
(or prove if specified).
331+
332+
Options:
333+
--out <s>, -o <s> : Specify the test file to be created (tmp file by default).
334+
--prove, -p : Force prove (default is yath if detected).
335+
--verbose, -v : Verbose (passed to yath/prove)
336+
--include <s>, -I <s> : Library paths to include.
337+
--test_warnings, -w : Fail tests on warnings.
338+
--test_bundle <s>, -t : Test bundle (default: Test2::V0). Can be comma-separated list.
339+
--pass_list <s>, -l <s> : Output directory for list of 100% passing tests.
340+
--stats_output <s>, -s <s> : Stats output directory (does not combine with pass_list).
341+
--help -h : Show basic help and exit.
342+
318343
=head2 Example aggregating strategy
319344

320345
There are many approaches you could do to use C<Test2::Aggregate> with an existing
321-
test suite, so for example you can start by making a list of the test files you
322-
are trying to aggregate:
346+
test suite, usually involving an iterative process of trying to run several tests
347+
aggregated, seeing if you can fix the failing ones, otherwise you remove them from
348+
the aggregation etc.
323349

324-
find t -name '*.t' > all.lst
350+
This process can be done with the help of the C<agg> script. For example, to try
351+
all tests under C<t/> aggregated and a list of passing tests put under the C<pass>
352+
directory you would do:
325353

326-
If you have a substantial test suite, perhaps try with a portion of it (a subdir?)
327-
instead of the entire suite. In any case, try running them aggregated like this:
354+
> agg -p pass t
328355

329-
use Test2::Aggregate;
330-
use Test2::V0; # Or Test::More;
356+
If the run completes, you have a "starting point" - i.e. a .txt list that can run
357+
under the aggregator with the C<lists> option:
331358

332-
my $stats = Test2::Aggregate::run_tests(
333-
lists => ['all.lst'],
359+
Test2::Aggregate::run_tests(
360+
lists => ['pass/name_of_file.txt']
334361
);
335362

336-
open OUT, ">pass.lst";
337-
foreach my $test (sort {$stats->{$a}->{test_no} <=> $stats->{$b}->{test_no}} keys %$stats) {
338-
print OUT "$test\n" if $stats->{$test}->{pass_perc};
339-
}
340-
close OUT;
341-
342-
done_testing();
363+
If the run does not complete (e.g. signal 11 on some test), try fewer tests by
364+
choosing just a subdirectory. If that's not possible, you'll probably have to go
365+
to the more manual method of getting a full list of your tests
366+
(C<find t -name '*.t' E<gt> all.lst>) then trying to run parts of it, again with
367+
the C<lists> option.
343368

344-
Run the above with C<prove> or C<yath> in verbose mode, so that in case the run
345-
hangs (it can happen), you can see where it did so and edit C<all.lst> removing
346-
the offending test.
347-
348-
If the run completes, you have a "starting point" - i.e. a list that can run under
349-
the aggregator in C<pass.lst>.
350-
You can try adding back some of the failed tests - test failures can be cascading,
351-
so some might be passing if added back, or have small issues you can address.
369+
After you have a starting point, you can try see if there is an obvious reason some
370+
tests fail and address it to add them back to the pass list. You can even try adding
371+
back some of the failed tests that were not among the first to fail - test failures
372+
can sometimes be cascading, so some might be passing if added back, or have small
373+
issues you can address.
352374

353375
Try adding C<test_warnings =E<gt> 1> to C<run_tests> to fix warnings as well, unless
354376
it is common for your tests to have C<STDERR> output.
355377

356-
To have your entire suite run aggregated tests together once and not repeat them
357-
along with the other, non-aggregated, tests, it is a good idea to use the
358-
C<--exclude-list> option of the C<Test2::Harness>.
378+
In the end, you will end up with part of your tests aggregated in (multiple if you
379+
want to run them in parallel) list files, with the rest of your tests to be run
380+
non-aggregated.
381+
382+
You don't actually have to move and separate aggregated/non-aggregated files when
383+
using lists, you can still have your entire suite run the aggregated tests once and
384+
not repeat them along with the other, non-aggregated tests, by taking advantage of
385+
the C<--exclude-list> option of the C<Test2::Harness>.
359386

360387
Hopefully your tests can run in parallel (C<prove/yath -j>), in which case you
361388
would split your aggregated tests into multiple lists to have them run in parallel.

agg

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ my \$stats = Test2::Aggregate::run_tests(
8383
";
8484
$code .= "\ttest_warnings => 1,\n" if $opt{test_warnings};
8585
$code .= "\tstats_output => '$opt{stats_output}',\n" if $opt{stats_output};
86-
$code .= "\tpass_only => 1,\n" if $opt{pass_only};
86+
$code .= "\tpass_only => 1,\n" if $opt{pass_list};
8787
$code .= "\tdirs => [\n";
8888
$code .= join(",\n", map {"'$_'"} normalize_paths($cwd, @ARGV));
8989
$code .= "\n\t],

lib/Test2/Aggregate.pm

+47-15
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ Additions with C<extend_stats> as of the current version:
244244
245245
Modifies C<stats_output> by making it only print out a list of passing tests.
246246
Useful for creating lists of aggregateable tests.
247+
Has no effect if C<stats_output> is not defined.
247248
248249
=over 4
249250
@@ -374,14 +375,15 @@ sub _process_warnings {
374375
}
375376

376377
sub _run_tests {
377-
my $tests = shift;
378-
my $args = shift;
378+
my $tests = shift;
379+
my $args = shift;
379380

380381
my $repeat = $args->{repeat};
381382
$repeat = 1 if $repeat < 0;
382383
my (%stats, $start);
383384

384385
require Time::HiRes if $args->{stats_output};
386+
my $fh = _stats_fh($args);
385387

386388
for my $i (1 .. $repeat) {
387389
my $iter = $repeat > 1 ? "Iter: $i/$repeat - " : '';
@@ -416,10 +418,19 @@ sub _run_tests {
416418
if $args->{stats_output};
417419
$stats{$test}{pass_perc} += $result ? 100/$repeat : 0;
418420
$count++;
421+
422+
# If we have single iteration, no need to collect stats so write
423+
# per line to avoid losing them in case of SIG
424+
_print_stats($fh, \%stats, $args, $test)
425+
if $args->{stats_output} && $repeat == 1;
419426
}
420427
}
421428

422-
_print_stats(\%stats, $args) if $args->{stats_output};
429+
if ($args->{stats_output}) {
430+
# Write all stats for many iterations
431+
_print_stats($fh, \%stats, $args) if $repeat > 1;
432+
_close_stats($fh, $args);
433+
}
423434
$args->{stats} = \%stats;
424435
}
425436

@@ -434,8 +445,10 @@ sub _override {
434445
return $override;
435446
}
436447

437-
sub _print_stats {
438-
my ($stats, $args) = @_;
448+
sub _stats_fh {
449+
my $args = shift;
450+
451+
return unless $args->{stats_output};
439452

440453
unless (-e $args->{stats_output}) {
441454
my @create = mkpath($args->{stats_output});
@@ -453,23 +466,41 @@ sub _print_stats {
453466
open($fh, '>', $file) or die "Can't open > $file: $!";
454467
}
455468

456-
my $total = 0;
469+
$args->{total_time} = 0;
457470
my $extra = $args->{extend_stats} ? ' TIMESTAMP' : '';
458471
print $fh "TIME PASS%$extra TEST\n" unless $args->{pass_only};
459472

460-
foreach my $test (sort {$stats->{$b}->{time}<=>$stats->{$a}->{time}} keys %$stats) {
473+
return $fh;
474+
}
475+
476+
sub _close_stats {
477+
my ($fh, $args) = @_;
478+
479+
return unless $fh;
480+
481+
printf $fh "TOTAL TIME: %.1f sec\n", $args->{total_time}
482+
unless $args->{pass_only};
483+
close $fh unless $args->{stats_output} =~ /^-$/;
484+
}
485+
486+
sub _print_stats {
487+
my ($fh, $stats, $args, $test) = @_;
488+
489+
return unless $fh;
490+
491+
my @tests = $test ? $test : keys %$stats;
492+
my $extra = '';
493+
494+
foreach my $test (sort {$stats->{$b}->{time}<=>$stats->{$a}->{time}} @tests) {
461495
if ($args->{pass_only}) {
462496
print $fh "$test\n" if $stats->{$test}->{pass_perc} > 99;
463497
next;
464498
}
465499
$extra = ' '.$stats->{$test}->{timestamp} if $args->{extend_stats};
466-
$total += $stats->{$test}->{time};
500+
$args->{total_time} += $stats->{$test}->{time};
467501
printf $fh "%.2f %d$extra $test\n",
468502
$stats->{$test}->{time}, $stats->{$test}->{pass_perc};
469503
}
470-
471-
printf $fh "TOTAL TIME: %.1f sec\n", $total unless $args->{pass_only};
472-
close $fh unless $args->{stats_output} =~ /^-$/;
473504
}
474505

475506
sub _uniq {
@@ -603,10 +634,11 @@ under the aggregator with the C<lists> option:
603634
lists => ['pass/name_of_file.txt']
604635
);
605636
606-
If the run does not complete, try fewer tests by choosing just a subdirectory. If
607-
that's not possible, you'll probably have to go to the more manual method of getting
608-
a full list of your tests (C<find t -name '*.t' E<gt> all.lst>) then trying to run
609-
parts of it, again with the C<lists> option.
637+
If the run does not complete (e.g. signal 11 on some test), try fewer tests by
638+
choosing just a subdirectory. If that's not possible, you'll probably have to go
639+
to the more manual method of getting a full list of your tests
640+
(C<find t -name '*.t' E<gt> all.lst>) then trying to run parts of it, again with
641+
the C<lists> option.
610642
611643
After you have a starting point, you can try see if there is an obvious reason some
612644
tests fail and address it to add them back to the pass list. You can even try adding

t/errors.t

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ is(scalar(keys %$stats), 1, 'Only 1 subtest ran');
1616
is(
1717
intercept {
1818
Test2::Aggregate::run_tests(
19-
dirs => ['xt/failing'],
20-
root => $root
19+
dirs => ['xt/failing'],
20+
root => $root,
21+
stats_output => '-',
22+
pass_only => 1
2123
)
2224
},
2325
array {

t/stats.t

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ my $pattrn = 'stats.t.*txt';
99
my $tmpdir = File::Temp->newdir;
1010

1111
plan skip_all => "Cannot create temp directory" unless -e $tmpdir;
12-
plan(15);
12+
plan(17);
1313

1414
foreach my $extend (0 .. 1) {
1515
stdout_like(sub {
@@ -48,6 +48,7 @@ like(find($tmpdir, $pattrn), [qr/$pattrn/], "Found stats file");
4848
Test2::Aggregate::run_tests(
4949
dirs => ['xt/aggregate'],
5050
root => $root,
51+
repeat => 2,
5152
stats_output => "$tmpdir/tmp1$timest"
5253
);
5354

0 commit comments

Comments
 (0)