diff --git a/run-tests.php b/run-tests.php index 06a1eb808a6..0d0b683becd 100755 --- a/run-tests.php +++ b/run-tests.php @@ -1851,11 +1851,16 @@ function run_test(string $php, $file, array $env): string $skipCache = new SkipCache($enableSkipCache, $cfg['keep']['skip']); } + $orig_php = $php; + $php = escapeshellarg($php); + + $retriable = true; + $retried = false; +retry: + $temp_filenames = null; $org_file = $file; - $orig_php = $php; - $php = escapeshellarg($php); $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE'] ?? null; $phpdbg = $env['TEST_PHPDBG_EXECUTABLE'] ?? null; @@ -1891,8 +1896,11 @@ TEST $file $tested = $test->getName(); - if ($num_repeats > 1 && $test->hasSection('FILE_EXTERNAL')) { - return skip_test($tested, $tested_file, $shortname, 'Test with FILE_EXTERNAL might not be repeatable'); + if ($test->hasSection('FILE_EXTERNAL')) { + $retriable = false; + if ($num_repeats > 1) { + return skip_test($tested, $tested_file, $shortname, 'Test with FILE_EXTERNAL might not be repeatable'); + } } if ($test->hasSection('CAPTURE_STDIO')) { @@ -1918,6 +1926,7 @@ TEST $file } $php = escapeshellarg($php_cgi) . ' -C '; $uses_cgi = true; + $retriable = false; if ($num_repeats > 1) { return skip_test($tested, $tested_file, $shortname, 'CGI does not support --repeat'); } @@ -1935,20 +1944,18 @@ TEST $file } else { return skip_test($tested, $tested_file, $shortname, 'phpdbg not available'); } + $retriable = false; if ($num_repeats > 1) { return skip_test($tested, $tested_file, $shortname, 'phpdbg does not support --repeat'); } } - if ($num_repeats > 1) { - if ($test->hasSection('CLEAN')) { - return skip_test($tested, $tested_file, $shortname, 'Test with CLEAN might not be repeatable'); - } - if ($test->hasSection('STDIN')) { - return skip_test($tested, $tested_file, $shortname, 'Test with STDIN might not be repeatable'); - } - if ($test->hasSection('CAPTURE_STDIO')) { - return skip_test($tested, $tested_file, $shortname, 'Test with CAPTURE_STDIO might not be repeatable'); + foreach (['CLEAN', 'STDIN', 'CAPTURE_STDIO'] as $section) { + if ($test->hasSection($section)) { + $retriable = false; + if ($num_repeats > 1) { + return skip_test($tested, $tested_file, $shortname, "Test with $section might not be repeatable"); + } } } @@ -2140,8 +2147,11 @@ TEST $file } settings2array(preg_split("/[\n\r]+/", $ini), $ini_settings); - if ($num_repeats > 1 && isset($ini_settings['opcache.opt_debug_level'])) { - return skip_test($tested, $tested_file, $shortname, 'opt_debug_level tests are not repeatable'); + if (isset($ini_settings['opcache.opt_debug_level'])) { + $retriable = false; + if ($num_repeats > 1) { + return skip_test($tested, $tested_file, $shortname, 'opt_debug_level tests are not repeatable'); + } } } @@ -2640,6 +2650,10 @@ COMMAND $cmd $wanted_re = null; } + if (!$passed && !$retried && $retriable && error_may_be_retried($output)) { + $retried = true; + goto retry; + } if ($passed) { if (!$cfg['keep']['php'] && !$leaked) { @@ -2671,6 +2685,9 @@ COMMAND $cmd // XLEAK with ASAN completely disables LSAN so the test is expected to pass $warn = true; $info = " (warn: XLEAK section but test passes)"; + } elseif ($retried) { + $warn = true; + $info = " (warn: Test passed on retry attempt)"; } else { show_result("PASS", $tested, $tested_file, '', $temp_filenames); $junit->markTestAs('PASS', $shortname, $tested); @@ -2815,6 +2832,11 @@ SH; return $restype[0] . 'ED'; } +function error_may_be_retried(string $output): bool +{ + return preg_match('((timed out)|(connection refused))i', $output) === 1; +} + function expectf_to_regex(?string $wanted): string { $wanted_re = $wanted ?? '';