Skip to content
183 changes: 163 additions & 20 deletions run-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function main(): void
$exts_skipped, $exts_tested, $exts_to_test, $failed_tests_file,
$ignored_by_ext, $ini_overwrites, $colorize,
$log_format, $no_clean, $no_file_cache,
$pass_options, $php, $php_cgi, $preload,
$pass_options, $pass_options_args, $php, $php_cgi, $preload,
$result_tests_file, $slow_min_ms, $start_time,
$temp_source, $temp_target, $test_cnt,
$test_files, $test_idx, $test_results, $testfile,
Expand Down Expand Up @@ -328,6 +328,7 @@ function main(): void
$failed_tests_file = false;
$pass_option_n = false;
$pass_options = '';
$pass_options_args = [];

$output_file = INIT_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt';

Expand Down Expand Up @@ -472,11 +473,13 @@ function main(): void
case 'n':
if (!$pass_option_n) {
$pass_options .= ' -n';
$pass_options_args[] = '-n';
Comment thread
NickSdot marked this conversation as resolved.
Resolved
}
$pass_option_n = true;
break;
case 'e':
$pass_options .= ' -e';
$pass_options_args[] = '-e';
break;
case '--preload':
$preload = true;
Expand Down Expand Up @@ -682,8 +685,13 @@ function main(): void
if ($conf_passed !== null) {
if (IS_WINDOWS) {
$pass_options .= " -c " . escapeshellarg($conf_passed);
$pass_options_args[] = '-c';
$pass_options_args[] = $conf_passed;
} else {
$pass_options .= " -c '" . realpath($conf_passed) . "'";
$configurationFile = realpath($conf_passed);
$pass_options .= " -c '" . $configurationFile . "'";
$pass_options_args[] = '-c';
$pass_options_args[] = (string) $configurationFile;
}
}

Expand Down Expand Up @@ -1173,19 +1181,20 @@ function error_report(string $testname, string $logname, string $tested): void
* @return false|string
*/
function system_with_timeout(
string $commandline,
string|array $commandline,
?array $env = null,
?string $stdin = null,
bool $captureStdIn = true,
bool $captureStdOut = true,
bool $captureStdErr = true
bool $captureStdErr = true,
bool $mergeStdErr = false
) {
global $valgrind;

// when proc_open cmd is passed as a string (without bypass_shell=true option) the cmd goes thru shell
// and on Windows quotes are discarded, this is a fix to honor the quotes and allow values containing
// spaces like '"C:\Program Files\PHP\php.exe"' to be passed as 1 argument correctly
if (IS_WINDOWS) {
if (IS_WINDOWS && is_string($commandline)) {
$commandline = 'start "" /b /wait ' . $commandline . ' & exit';
}

Expand All @@ -1204,7 +1213,9 @@ function system_with_timeout(
$descriptorspec[1] = ['pipe', 'w'];
}
if ($captureStdErr) {
$descriptorspec[2] = ['pipe', 'w'];
$descriptorspec[2] = $mergeStdErr
? ['redirect', 1]
: ['pipe', 'w'];
}
$proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, ['suppress_errors' => true]);

Expand Down Expand Up @@ -1277,6 +1288,49 @@ function system_with_timeout(
return $data;
}

function can_run_with_structured_test_command(TestFile $test): bool
Comment thread
NickSdot marked this conversation as resolved.
Resolved
{
global $preload, $valgrind;

return !$valgrind
&& !$preload
&& !$test->hasAnySections(
'ARGS',
'CAPTURE_STDIO',
'DEFLATE_POST',
'GZIP_POST',
'POST',
'POST_RAW',
'PUT',
);
}

function create_structured_test_command(
string $php,
array $sapiOptionArgs,
array $passOptionArgs,
array $iniSettings,
string $testFile,
int $numRepeats
): array {
$command = [
$php,
...$sapiOptionArgs,
...$passOptionArgs,
];
if ($numRepeats > 1) {
$command[] = '--repeat';
$command[] = (string) $numRepeats;
}

return [
...$command,
...settings2arguments($iniSettings),
'-f',
$testFile,
];
}

function run_all_tests(array $test_files, array $env, ?string $redir_tested = null): void
{
global $test_results, $failed_tests_file, $result_tests_file, $php, $test_idx, $file_cache, $shuffle;
Expand Down Expand Up @@ -1837,7 +1891,7 @@ function skip_test(string $tested, string $tested_file, string $shortname, strin
function run_test(string $php, $file, array $env): string
{
global $log_format, $ini_overwrites, $PHP_FAILED_TESTS;
global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;
global $pass_options, $pass_options_args, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;
global $valgrind, $temp_source, $temp_target, $cfg, $environment;
global $no_clean;
global $SHOW_ONLY_GROUPS;
Expand All @@ -1859,6 +1913,9 @@ function run_test(string $php, $file, array $env): string
$skipCache = new SkipCache($enableSkipCache, $cfg['keep']['skip']);
}

$originalPhpExecutable = $php;
$phpExecutable = $php;
$sapiOptionArgs = [];
$php = escapeshellarg($php);
$orig_php = $php;

Expand Down Expand Up @@ -1930,6 +1987,8 @@ function run_test(string $php, $file, array $env): string
if (!$php_cgi) {
return skip_test($tested, $tested_file, $shortname, 'CGI not available');
}
$phpExecutable = $php_cgi;
$sapiOptionArgs[] = '-C';
$php = escapeshellarg($php_cgi) . ' -C ';
$uses_cgi = true;
if ($num_repeats > 1) {
Expand All @@ -1939,13 +1998,17 @@ function run_test(string $php, $file, array $env): string

/* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */
$extra_options = '';
$extraOptionArgs = [];
Comment thread
NickSdot marked this conversation as resolved.
Resolved
if ($test->hasSection('PHPDBG')) {
if (isset($phpdbg)) {
$phpExecutable = $phpdbg;
$sapiOptionArgs[] = '-qIb';
$php = escapeshellarg($phpdbg) . ' -qIb';

// Additional phpdbg command line options for sections that need to
// be run straight away. For example, EXTENSIONS, SKIPIF, CLEAN.
$extra_options = '-rr';
$extraOptionArgs[] = '-rr';
} else {
return skip_test($tested, $tested_file, $shortname, 'phpdbg not available');
}
Expand Down Expand Up @@ -2099,6 +2162,7 @@ function run_test(string $php, $file, array $env): string
//$ini_overwrites[] = 'setting=value';
settings2array($ini_overwrites, $ini_settings);

$orig_ini_settings_args = settings2arguments($ini_settings);
$orig_ini_settings = settings2params($ini_settings);

if ($file_cache !== null) {
Expand Down Expand Up @@ -2146,6 +2210,7 @@ function run_test(string $php, $file, array $env): string
}
}

$testIniSettings = $ini_settings;
$ini_settings = settings2params($ini_settings);

$env['TEST_PHP_EXTRA_ARGS'] = $pass_options . ' ' . $ini_settings;
Expand All @@ -2156,19 +2221,41 @@ function run_test(string $php, $file, array $env): string

if ($test->sectionNotEmpty('SKIPIF')) {
show_file_block('skip', $test->getSection('SKIPIF'));
$extra = !IS_WINDOWS ?
"unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : "";
$skipEnv = $env;
if (!IS_WINDOWS) {
unset(
$skipEnv['REQUEST_METHOD'],
$skipEnv['QUERY_STRING'],
$skipEnv['PATH_TRANSLATED'],
$skipEnv['SCRIPT_FILENAME'],
);
}

if ($valgrind) {
$env['USE_ZEND_ALLOC'] = '0';
$env['ZEND_DONT_UNLOAD_MODULES'] = 1;
$skipEnv['USE_ZEND_ALLOC'] = '0';
$skipEnv['ZEND_DONT_UNLOAD_MODULES'] = 1;
}

$junit->startTimer($shortname);

$startTime = microtime(true);
$commandLine = "$extra $php $pass_options $extra_options -q $orig_ini_settings $no_file_cache -d display_errors=1 -d display_startup_errors=0";
$output = $skipCache->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $env);
$commandLine = [
$phpExecutable,
...$sapiOptionArgs,
...$pass_options_args,
...$extraOptionArgs,
'-q',
...$orig_ini_settings_args,
'-d',
'opcache.file_cache=',
'-d',
'opcache.file_cache_only=0',
'-d',
'display_errors=1',
'-d',
'display_startup_errors=0',
];
$output = $skipCache->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $skipEnv);

$time = microtime(true) - $startTime;
$junit->stopTimer($shortname);
Expand Down Expand Up @@ -2498,7 +2585,26 @@ function run_test(string $php, $file, array $env): string
$startTime = $hrtime[0] * 1000000000 + $hrtime[1];

$stdin = $test->hasSection('STDIN') ? $test->getSection('STDIN') : null;
$out = system_with_timeout($cmd, $env, $stdin, $captureStdIn, $captureStdOut, $captureStdErr);
$useStructuredCommand = can_run_with_structured_test_command($test);
$testCommand = $useStructuredCommand
? create_structured_test_command(
$phpExecutable,
$sapiOptionArgs,
$pass_options_args,
$testIniSettings,
$test_file,
$num_repeats,
)
: $cmd;
$out = system_with_timeout(
$testCommand,
$env,
$stdin,
$captureStdIn,
$captureStdOut,
$captureStdErr,
$useStructuredCommand && $captureStdOut && $captureStdErr,
);

$junit->stopTimer($shortname);
$hrtime = hrtime();
Expand All @@ -2520,9 +2626,27 @@ function run_test(string $php, $file, array $env): string
save_text($test_clean, trim($test->getSection('CLEAN')), $temp_clean);

if (!$no_clean) {
$extra = !IS_WINDOWS ?
"unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : "";
$clean_output = system_with_timeout("$extra $orig_php $pass_options -q $orig_ini_settings $no_file_cache \"$test_clean\"", $env);
$cleanCommand = [
$originalPhpExecutable,
...$pass_options_args,
'-q',
...$orig_ini_settings_args,
'-d',
'opcache.file_cache=',
'-d',
'opcache.file_cache_only=0',
$test_clean,
];
$cleanEnv = $env;
if (!IS_WINDOWS) {
unset(
$cleanEnv['REQUEST_METHOD'],
$cleanEnv['QUERY_STRING'],
$cleanEnv['PATH_TRANSLATED'],
$cleanEnv['SCRIPT_FILENAME'],
);
}
$clean_output = system_with_timeout($cleanCommand, $cleanEnv);
}

if (!$cfg['keep']['clean']) {
Expand Down Expand Up @@ -3033,6 +3157,20 @@ function settings2params(array $ini_settings): string
return $settings;
}

function settings2arguments(array $ini_settings): array
{
$arguments = [];

foreach ($ini_settings as $name => $value) {
foreach ((array) $value as $item) {
$arguments[] = '-d';
$arguments[] = "$name=$item";
}
}

return $arguments;
}

function compute_summary(): void
{
global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results;
Expand Down Expand Up @@ -3612,12 +3750,12 @@ public function __construct(bool $enable, bool $keepFile)
$this->keepFile = $keepFile;
}

public function checkSkip(string $php, string $code, string $checkFile, string $tempFile, array $env): string
public function checkSkip(string|array $command, string $code, string $checkFile, string $tempFile, array $env): string
{
// Extension tests frequently use something like <?php require 'skipif.inc';
// for skip checks. This forces us to cache per directory to avoid pollution.
$dir = dirname($checkFile);
$key = "$php => $dir";
$key = (is_array($command) ? implode("\0", $command) : $command) . " => $dir";

if (isset($this->skips[$key][$code])) {
$this->hits++;
Expand All @@ -3628,7 +3766,12 @@ public function checkSkip(string $php, string $code, string $checkFile, string $
}

save_text($checkFile, $code, $tempFile);
$result = trim(system_with_timeout("$php \"$checkFile\"", $env));
if (is_array($command)) {
$command[] = $checkFile;
} else {
$command .= " \"$checkFile\"";
}
$result = trim(system_with_timeout($command, $env));
if (strpos($result, 'nocache') === 0) {
$result = '';
} else if ($this->enable) {
Expand Down
2 changes: 1 addition & 1 deletion tests/basic/req60524-win.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ if(PHP_OS_FAMILY !== "Windows")
--FILE--
<?php echo sys_get_temp_dir(); ?>
--EXPECT--
C:\\Windows
C:\Windows
26 changes: 26 additions & 0 deletions tests/run-test/clean_environment.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
SKIPIF and CLEAN do not inherit request environment variables on POSIX
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== 'Windows') {
foreach (['REQUEST_METHOD', 'QUERY_STRING', 'PATH_TRANSLATED', 'SCRIPT_FILENAME'] as $name) {
if (getenv($name) !== false) {
die("$name was inherited\n");
}
}
}
?>
--FILE--
<?php
?>
--CLEAN--
<?php
if (PHP_OS_FAMILY !== 'Windows') {
foreach (['REQUEST_METHOD', 'QUERY_STRING', 'PATH_TRANSLATED', 'SCRIPT_FILENAME'] as $name) {
if (getenv($name) !== false) {
echo "$name was inherited\n";
}
}
}
?>
--EXPECT--
Loading