$repeat, $result_tests_file, $slow_min_ms, $start_time, $switch,
$temp_source, $temp_target, $test_cnt, $test_dirs,
$test_files, $test_idx, $test_list, $test_results, $testfile,
- $user_tests, $valgrind, $sum_results, $shuffle, $file_cache;
+ $user_tests, $valgrind, $sum_results, $shuffle, $file_cache, $num_repeats;
// Parallel testing
global $workers, $workerID;
global $context_line_count;
$shuffle = false;
$workers = null;
$context_line_count = 3;
+ $num_repeats = 1;
$cfgtypes = ['show', 'keep'];
$cfgfiles = ['skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem'];
. ':print_suppressions=0';
}
break;
+ case '--repeat':
+ $num_repeats = (int) $argv[++$i];
+ $environment['SKIP_REPEAT'] = 1;
+ break;
//case 'w'
case '-':
// repeat check with full switch
}
}
+function skip_test(string $tested, string $tested_file, string $shortname, string $reason) {
+ show_result('SKIP', $tested, $tested_file, "reason: $reason");
+ junit_init_suite(junit_get_suitename_for($shortname));
+ junit_mark_test_as('SKIP', $shortname, $tested, 0, $reason);
+ return 'SKIPPED';
+}
+
//
// Run an individual test case.
//
global $no_file_cache;
global $slow_min_ms;
global $preload, $file_cache;
+ global $num_repeats;
// Parallel testing
global $workerID;
$temp_filenames = null;
}
}
+ $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file);
+ $tested_file = $shortname;
+ $tested = trim($section_text['TEST']);
+
// the redirect section allows a set of tests to be reused outside of
// a given test dir
if ($bork_info === null) {
unset($section_text['FILEEOF']);
}
+ if ($num_repeats > 1 && isset($section_text['FILE_EXTERNAL'])) {
+ return skip_test($tested, $tested_file, $shortname, 'Test with FILE_EXTERNAL might not be repeatable');
+ }
+
foreach (['FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX'] as $prefix) {
$key = $prefix . '_EXTERNAL';
}
fclose($fp);
- $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file);
- $tested_file = $shortname;
-
if ($bork_info !== null) {
show_result("BORK", $bork_info, $tested_file);
$PHP_FAILED_TESTS['BORKED'][] = [
$cmdRedirect = '';
}
- $tested = trim($section_text['TEST']);
-
/* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */
if (array_key_exists('CGI', $section_text) || !empty($section_text['GET']) || !empty($section_text['POST']) || !empty($section_text['GZIP_POST']) || !empty($section_text['DEFLATE_POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['PUT']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {
if (isset($php_cgi)) {
} elseif (file_exists(dirname($php) . "/php-cgi")) {
$php = realpath(dirname($php) . "/php-cgi") . ' -C ';
} else {
- show_result('SKIP', $tested, $tested_file, "reason: CGI not available");
-
- junit_init_suite(junit_get_suitename_for($shortname));
- junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available');
- return 'SKIPPED';
+ return skip_test($tested, $tested_file, $shortname, 'CGI not available');
}
}
+ if ($num_repeats > 1) {
+ return skip_test($tested, $tested_file, $shortname, 'CGI does not support --repeat');
+ }
$uses_cgi = true;
}
// be run straight away. For example, EXTENSIONS, SKIPIF, CLEAN.
$extra_options = '-rr';
} else {
- show_result('SKIP', $tested, $tested_file, "reason: phpdbg not available");
+ return skip_test($tested, $tested_file, $shortname, 'phpdbg not available');
+ }
+ if ($num_repeats > 1) {
+ return skip_test($tested, $tested_file, $shortname, 'phpdbg does not support --repeat');
+ }
+ }
- junit_init_suite(junit_get_suitename_for($shortname));
- junit_mark_test_as('SKIP', $shortname, $tested, 0, 'phpdbg not available');
- return 'SKIPPED';
+ if ($num_repeats > 1) {
+ if (array_key_exists('CLEAN', $section_text)) {
+ return skip_test($tested, $tested_file, $shortname, 'Test with CLEAN might not be repeatable');
+ }
+ if (array_key_exists('STDIN', $section_text)) {
+ return skip_test($tested, $tested_file, $shortname, 'Test with STDIN might not be repeatable');
+ }
+ if (array_key_exists('CAPTURE_STDIO', $section_text)) {
+ return skip_test($tested, $tested_file, $shortname, 'Test with CAPTURE_STDIO might not be repeatable');
}
}
// even though all the files are re-created.
$ini_settings['opcache.validate_timestamps'] = '0';
}
+ } else if ($num_repeats > 1) {
+ // Make sure warnings still show up on the second run.
+ $ini_settings['opcache.record_warnings'] = '1';
}
// Any special ini settings
$replacement = IS_WINDOWS ? '"' . PHP_BINARY . ' -r \"while ($in = fgets(STDIN)) echo $in;\" > $1"' : 'tee $1 >/dev/null';
$section_text['INI'] = preg_replace('/{MAIL:(\S+)}/', $replacement, $section_text['INI']);
settings2array(preg_split("/[\n\r]+/", $section_text['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');
+ }
}
$ini_settings = settings2params($ini_settings);
$env['CONTENT_TYPE'] = '';
$env['CONTENT_LENGTH'] = '';
- $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args$cmdRedirect";
+ $repeat_option = $num_repeats > 1 ? "--repeat $num_repeats" : "";
+ $cmd = "$php $pass_options $repeat_option $ini_settings -f \"$test_file\" $args$cmdRedirect";
}
if ($valgrind) {
}
}
+ if ($num_repeats > 1) {
+ // In repeat mode, retain the output before the first execution,
+ // and of the last execution. Do this early, because the trimming below
+ // makes the newline handling complicated.
+ $separator1 = "Executing for the first time...\n";
+ $separator1_pos = strpos($out, $separator1);
+ if ($separator1_pos !== false) {
+ $separator2 = "Finished execution, repeating...\n";
+ $separator2_pos = strrpos($out, $separator2);
+ if ($separator2_pos !== false) {
+ $out = substr($out, 0, $separator1_pos)
+ . substr($out, $separator2_pos + strlen($separator2));
+ } else {
+ $out = substr($out, 0, $separator1_pos)
+ . substr($out, $separator1_pos + strlen($separator1));
+ }
+ }
+ }
+
// Does the output match what is expected?
$output = preg_replace("/\r\n/", "\n", trim($out));
{14, 1, "ri"},
{14, 1, "rextinfo"},
{15, 0, "ini"},
+ /* Internal testing option -- may be changed or removed without notice,
+ * including in patch releases. */
+ {16, 1, "repeat"},
{'-', 0, NULL} /* end of args */
};
static php_stream *s_in_process = NULL;
-static void cli_register_file_handles(void) /* {{{ */
+static void cli_register_file_handles(zend_bool no_close) /* {{{ */
{
php_stream *s_in, *s_out, *s_err;
php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
return;
}
-#if PHP_DEBUG
- /* do not close stdout and stderr */
- s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
- s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
-#endif
+ if (no_close) {
+ s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+ s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+ s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+ }
s_in_process = s_in;
int interactive=0;
const char *param_error=NULL;
int hide_argv = 0;
+ int num_repeats = 1;
+ pid_t pid = getpid();
zend_try {
case 15:
behavior = PHP_MODE_SHOW_INI_CONFIG;
break;
+ case 16:
+ num_repeats = atoi(php_optarg);
+ break;
default:
break;
}
fflush(stdout);
}
+ if (num_repeats > 1) {
+ fprintf(stdout, "Executing for the first time...\n");
+ fflush(stdout);
+ }
+
+do_repeat:
/* only set script_file if not set already and not in direct mode and not at end of parameter list */
if (argc > php_optind
&& !script_file
switch (behavior) {
case PHP_MODE_STANDARD:
if (strcmp(file_handle.filename, "Standard input code")) {
- cli_register_file_handles();
+ cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
}
if (interactive && cli_shell_callbacks.cli_shell_run) {
}
break;
case PHP_MODE_CLI_DIRECT:
- cli_register_file_handles();
+ cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
zend_eval_string_ex(exec_direct, NULL, "Command line code", 1);
break;
size_t len, index = 0;
zval argn, argi;
- cli_register_file_handles();
+ cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
if (exec_begin) {
zend_eval_string_ex(exec_begin, NULL, "Command line begin code", 1);
if (translated_path) {
free(translated_path);
}
+ /* Don't repeat fork()ed processes. */
+ if (--num_repeats && pid == getpid()) {
+ fprintf(stdout, "Finished execution, repeating...\n");
+ fflush(stdout);
+ goto do_repeat;
+ }
return EG(exit_status);
err:
sapi_deactivate();