diff --git a/dev/tools/cpan_random_tester.pl b/dev/tools/cpan_random_tester.pl index 4dbd964a2..cc46243b6 100644 --- a/dev/tools/cpan_random_tester.pl +++ b/dev/tools/cpan_random_tester.pl @@ -504,7 +504,14 @@ sub parse_all_module_results { ($mod = $dist) =~ s/-[\d.]+$//; # strip version $mod =~ s/-/::/g; # Foo-Bar → Foo::Bar } - next if $seen{$mod}++; + # jcpan can install a missing test prerequisite and retry this same + # distribution. The later test block is authoritative, so replace an + # earlier result instead of preserving a stale first-attempt failure. + if ($seen{$mod}) { + @results = grep { ($_->{module} // '') ne $mod } @results; + delete $seen{$mod}; + } + $seen{$mod} = 1; my %r = ( module => $mod, @@ -838,7 +845,13 @@ sub finish_streamed_test_block { ($mod = $dist) =~ s/-[\d.]+$//; $mod =~ s/-/::/g; } - return if $seen->{$mod}++; + # Keep the final result when jcpan retries a distribution after installing + # a missing test prerequisite. The first attempt is not authoritative. + if ($seen->{$mod}) { + @$results = grep { ($_->{module} // '') ne $mod } @$results; + delete $seen->{$mod}; + } + $seen->{$mod} = 1; my %r = ( module => $mod, diff --git a/dev/tools/tests/cpan_random_tester_parser.t b/dev/tools/tests/cpan_random_tester_parser.t index 5061e82c0..04550d1b2 100644 --- a/dev/tools/tests/cpan_random_tester_parser.t +++ b/dev/tools/tests/cpan_random_tester_parser.t @@ -96,6 +96,45 @@ is_deeply( 'streaming parser attributes resumed-parent build failure correctly', ); +my $retry_after_missing_prerequisite = <<'LOG'; +Running test for module 'Emoji::NationalFlag' +Checksum for /tmp/cpan/sources/authors/id/P/PU/PUNYTAN/Emoji-NationalFlag-0.01.tar.gz ok +Running Build test for PUNYTAN/Emoji-NationalFlag-0.01.tar.gz +Can't locate Locale/Country.pm in @INC +Result: FAIL +---- Unsatisfied dependencies detected during ---- + Locale::Country [test_requires] +Running test for module 'Locale::Country' +Checksum for /tmp/cpan/sources/authors/id/S/SB/SBECK/Locale-Codes-3.90.tar.gz ok +Running Build test for PUNYTAN/Emoji-NationalFlag-0.01.tar.gz +t/basic.t .. ok +All tests successful. +Files=2, Tests=3 +Result: PASS +Build test -- OK +LOG + +my @retry_results = parse_all_module_results($retry_after_missing_prerequisite); +is_deeply( + [map { $_->{module} } @retry_results], + ['Emoji::NationalFlag'], + 'retry replaces the earlier module result', +); +is($retry_results[0]{status}, 'PASS', 'successful retry replaces the stale failure'); +is($retry_results[0]{tests}, 3, 'successful retry retains its test count'); + +my ($retry_log_fh, $retry_log_path) = tempfile(); +print {$retry_log_fh} $retry_after_missing_prerequisite; +close $retry_log_fh or die "cannot close $retry_log_path: $!"; +my @streamed_retry_results = parse_all_module_results_from_file($retry_log_path); +is_deeply( + [map { $_->{module} } @streamed_retry_results], + ['Emoji::NationalFlag'], + 'streaming parser keeps only the retried module result', +); +is($streamed_retry_results[0]{status}, 'PASS', + 'streaming parser uses the successful retry result'); + my %slow = ('Image::ExifTool' => 3600); is_deeply( [effective_timeout_limits('Image::ExifTool', 120, 600, 300, \%slow)],