Skip to content

Commit 8e208dc

Browse files
committed
run-tests: bypassed the shell for test subprocesses
1 parent 9ed85af commit 8e208dc

3 files changed

Lines changed: 168 additions & 18 deletions

File tree

run-tests.php

Lines changed: 151 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ function main(): void
148148
$exts_skipped, $exts_tested, $exts_to_test, $failed_tests_file,
149149
$ignored_by_ext, $ini_overwrites, $colorize,
150150
$log_format, $no_clean, $no_file_cache,
151-
$pass_options, $php, $php_cgi, $preload,
151+
$pass_options, $pass_options_args, $php, $php_cgi, $preload,
152152
$result_tests_file, $slow_min_ms, $start_time,
153153
$temp_source, $temp_target, $test_cnt,
154154
$test_files, $test_idx, $test_results, $testfile,
@@ -328,6 +328,7 @@ function main(): void
328328
$failed_tests_file = false;
329329
$pass_option_n = false;
330330
$pass_options = '';
331+
$pass_options_args = [];
331332

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

@@ -472,11 +473,13 @@ function main(): void
472473
case 'n':
473474
if (!$pass_option_n) {
474475
$pass_options .= ' -n';
476+
$pass_options_args[] = '-n';
475477
}
476478
$pass_option_n = true;
477479
break;
478480
case 'e':
479481
$pass_options .= ' -e';
482+
$pass_options_args[] = '-e';
480483
break;
481484
case '--preload':
482485
$preload = true;
@@ -682,8 +685,13 @@ function main(): void
682685
if ($conf_passed !== null) {
683686
if (IS_WINDOWS) {
684687
$pass_options .= " -c " . escapeshellarg($conf_passed);
688+
$pass_options_args[] = '-c';
689+
$pass_options_args[] = $conf_passed;
685690
} else {
686-
$pass_options .= " -c '" . realpath($conf_passed) . "'";
691+
$configurationFile = realpath($conf_passed);
692+
$pass_options .= " -c '" . $configurationFile . "'";
693+
$pass_options_args[] = '-c';
694+
$pass_options_args[] = (string) $configurationFile;
687695
}
688696
}
689697

@@ -1173,19 +1181,20 @@ function error_report(string $testname, string $logname, string $tested): void
11731181
* @return false|string
11741182
*/
11751183
function system_with_timeout(
1176-
string $commandline,
1184+
string|array $commandline,
11771185
?array $env = null,
11781186
?string $stdin = null,
11791187
bool $captureStdIn = true,
11801188
bool $captureStdOut = true,
1181-
bool $captureStdErr = true
1189+
bool $captureStdErr = true,
1190+
bool $mergeStdErr = false
11821191
) {
11831192
global $valgrind;
11841193

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

@@ -1204,7 +1213,9 @@ function system_with_timeout(
12041213
$descriptorspec[1] = ['pipe', 'w'];
12051214
}
12061215
if ($captureStdErr) {
1207-
$descriptorspec[2] = ['pipe', 'w'];
1216+
$descriptorspec[2] = $mergeStdErr
1217+
? ['redirect', 1]
1218+
: ['pipe', 'w'];
12081219
}
12091220
$proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, ['suppress_errors' => true]);
12101221

@@ -1277,6 +1288,49 @@ function system_with_timeout(
12771288
return $data;
12781289
}
12791290

1291+
function can_run_with_structured_test_command(TestFile $test): bool
1292+
{
1293+
global $preload, $valgrind;
1294+
1295+
return !$valgrind
1296+
&& !$preload
1297+
&& !$test->hasAnySections(
1298+
'ARGS',
1299+
'CAPTURE_STDIO',
1300+
'DEFLATE_POST',
1301+
'GZIP_POST',
1302+
'POST',
1303+
'POST_RAW',
1304+
'PUT',
1305+
);
1306+
}
1307+
1308+
function create_structured_test_command(
1309+
string $php,
1310+
array $sapiOptionArgs,
1311+
array $passOptionArgs,
1312+
array $iniSettings,
1313+
string $testFile,
1314+
int $numRepeats
1315+
): array {
1316+
$command = [
1317+
$php,
1318+
...$sapiOptionArgs,
1319+
...$passOptionArgs,
1320+
];
1321+
if ($numRepeats > 1) {
1322+
$command[] = '--repeat';
1323+
$command[] = (string) $numRepeats;
1324+
}
1325+
1326+
return [
1327+
...$command,
1328+
...settings2arguments($iniSettings),
1329+
'-f',
1330+
$testFile,
1331+
];
1332+
}
1333+
12801334
function run_all_tests(array $test_files, array $env, ?string $redir_tested = null): void
12811335
{
12821336
global $test_results, $failed_tests_file, $result_tests_file, $php, $test_idx, $file_cache, $shuffle;
@@ -1837,7 +1891,7 @@ function skip_test(string $tested, string $tested_file, string $shortname, strin
18371891
function run_test(string $php, $file, array $env): string
18381892
{
18391893
global $log_format, $ini_overwrites, $PHP_FAILED_TESTS;
1840-
global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;
1894+
global $pass_options, $pass_options_args, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;
18411895
global $valgrind, $temp_source, $temp_target, $cfg, $environment;
18421896
global $no_clean;
18431897
global $SHOW_ONLY_GROUPS;
@@ -1859,6 +1913,9 @@ function run_test(string $php, $file, array $env): string
18591913
$skipCache = new SkipCache($enableSkipCache, $cfg['keep']['skip']);
18601914
}
18611915

1916+
$originalPhpExecutable = $php;
1917+
$phpExecutable = $php;
1918+
$sapiOptionArgs = [];
18621919
$php = escapeshellarg($php);
18631920
$orig_php = $php;
18641921

@@ -1930,6 +1987,8 @@ function run_test(string $php, $file, array $env): string
19301987
if (!$php_cgi) {
19311988
return skip_test($tested, $tested_file, $shortname, 'CGI not available');
19321989
}
1990+
$phpExecutable = $php_cgi;
1991+
$sapiOptionArgs[] = '-C';
19331992
$php = escapeshellarg($php_cgi) . ' -C ';
19341993
$uses_cgi = true;
19351994
if ($num_repeats > 1) {
@@ -1939,13 +1998,17 @@ function run_test(string $php, $file, array $env): string
19391998

19401999
/* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */
19412000
$extra_options = '';
2001+
$extraOptionArgs = [];
19422002
if ($test->hasSection('PHPDBG')) {
19432003
if (isset($phpdbg)) {
2004+
$phpExecutable = $phpdbg;
2005+
$sapiOptionArgs[] = '-qIb';
19442006
$php = escapeshellarg($phpdbg) . ' -qIb';
19452007

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

2165+
$orig_ini_settings_args = settings2arguments($ini_settings);
21022166
$orig_ini_settings = settings2params($ini_settings);
21032167

21042168
if ($file_cache !== null) {
@@ -2146,6 +2210,7 @@ function run_test(string $php, $file, array $env): string
21462210
}
21472211
}
21482212

2213+
$testIniSettings = $ini_settings;
21492214
$ini_settings = settings2params($ini_settings);
21502215

21512216
$env['TEST_PHP_EXTRA_ARGS'] = $pass_options . ' ' . $ini_settings;
@@ -2156,8 +2221,6 @@ function run_test(string $php, $file, array $env): string
21562221

21572222
if ($test->sectionNotEmpty('SKIPIF')) {
21582223
show_file_block('skip', $test->getSection('SKIPIF'));
2159-
$extra = !IS_WINDOWS ?
2160-
"unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : "";
21612224

21622225
if ($valgrind) {
21632226
$env['USE_ZEND_ALLOC'] = '0';
@@ -2167,7 +2230,22 @@ function run_test(string $php, $file, array $env): string
21672230
$junit->startTimer($shortname);
21682231

21692232
$startTime = microtime(true);
2170-
$commandLine = "$extra $php $pass_options $extra_options -q $orig_ini_settings $no_file_cache -d display_errors=1 -d display_startup_errors=0";
2233+
$commandLine = [
2234+
$phpExecutable,
2235+
...$sapiOptionArgs,
2236+
...$pass_options_args,
2237+
...$extraOptionArgs,
2238+
'-q',
2239+
...$orig_ini_settings_args,
2240+
'-d',
2241+
'opcache.file_cache=',
2242+
'-d',
2243+
'opcache.file_cache_only=0',
2244+
'-d',
2245+
'display_errors=1',
2246+
'-d',
2247+
'display_startup_errors=0',
2248+
];
21712249
$output = $skipCache->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $env);
21722250

21732251
$time = microtime(true) - $startTime;
@@ -2498,7 +2576,26 @@ function run_test(string $php, $file, array $env): string
24982576
$startTime = $hrtime[0] * 1000000000 + $hrtime[1];
24992577

25002578
$stdin = $test->hasSection('STDIN') ? $test->getSection('STDIN') : null;
2501-
$out = system_with_timeout($cmd, $env, $stdin, $captureStdIn, $captureStdOut, $captureStdErr);
2579+
$useStructuredCommand = can_run_with_structured_test_command($test);
2580+
$testCommand = $useStructuredCommand
2581+
? create_structured_test_command(
2582+
$phpExecutable,
2583+
$sapiOptionArgs,
2584+
$pass_options_args,
2585+
$testIniSettings,
2586+
$test_file,
2587+
$num_repeats,
2588+
)
2589+
: $cmd;
2590+
$out = system_with_timeout(
2591+
$testCommand,
2592+
$env,
2593+
$stdin,
2594+
$captureStdIn,
2595+
$captureStdOut,
2596+
$captureStdErr,
2597+
$useStructuredCommand && $captureStdOut && $captureStdErr,
2598+
);
25022599

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

25222619
if (!$no_clean) {
2523-
$extra = !IS_WINDOWS ?
2524-
"unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;" : "";
2525-
$clean_output = system_with_timeout("$extra $orig_php $pass_options -q $orig_ini_settings $no_file_cache \"$test_clean\"", $env);
2620+
$cleanCommand = [
2621+
$originalPhpExecutable,
2622+
...$pass_options_args,
2623+
'-q',
2624+
...$orig_ini_settings_args,
2625+
'-d',
2626+
'opcache.file_cache=',
2627+
'-d',
2628+
'opcache.file_cache_only=0',
2629+
$test_clean,
2630+
];
2631+
$cleanEnv = $env;
2632+
if (!IS_WINDOWS) {
2633+
unset(
2634+
$cleanEnv['REQUEST_METHOD'],
2635+
$cleanEnv['QUERY_STRING'],
2636+
$cleanEnv['PATH_TRANSLATED'],
2637+
$cleanEnv['SCRIPT_FILENAME'],
2638+
);
2639+
}
2640+
$clean_output = system_with_timeout($cleanCommand, $cleanEnv);
25262641
}
25272642

25282643
if (!$cfg['keep']['clean']) {
@@ -3033,6 +3148,20 @@ function settings2params(array $ini_settings): string
30333148
return $settings;
30343149
}
30353150

3151+
function settings2arguments(array $ini_settings): array
3152+
{
3153+
$arguments = [];
3154+
3155+
foreach ($ini_settings as $name => $value) {
3156+
foreach ((array) $value as $item) {
3157+
$arguments[] = '-d';
3158+
$arguments[] = "$name=$item";
3159+
}
3160+
}
3161+
3162+
return $arguments;
3163+
}
3164+
30363165
function compute_summary(): void
30373166
{
30383167
global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results;
@@ -3612,12 +3741,12 @@ public function __construct(bool $enable, bool $keepFile)
36123741
$this->keepFile = $keepFile;
36133742
}
36143743

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

36223751
if (isset($this->skips[$key][$code])) {
36233752
$this->hits++;
@@ -3628,7 +3757,12 @@ public function checkSkip(string $php, string $code, string $checkFile, string $
36283757
}
36293758

36303759
save_text($checkFile, $code, $tempFile);
3631-
$result = trim(system_with_timeout("$php \"$checkFile\"", $env));
3760+
if (is_array($command)) {
3761+
$command[] = $checkFile;
3762+
} else {
3763+
$command .= " \"$checkFile\"";
3764+
}
3765+
$result = trim(system_with_timeout($command, $env));
36323766
if (strpos($result, 'nocache') === 0) {
36333767
$result = '';
36343768
} else if ($this->enable) {

tests/basic/req60524-win.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ if(PHP_OS_FAMILY !== "Windows")
1010
--FILE--
1111
<?php echo sys_get_temp_dir(); ?>
1212
--EXPECT--
13-
C:\\Windows
13+
C:\Windows
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
CLEAN does not inherit request environment variables on POSIX
3+
--FILE--
4+
<?php
5+
?>
6+
--CLEAN--
7+
<?php
8+
if (PHP_OS_FAMILY !== 'Windows') {
9+
foreach (['REQUEST_METHOD', 'QUERY_STRING', 'PATH_TRANSLATED', 'SCRIPT_FILENAME'] as $name) {
10+
if (getenv($name) !== false) {
11+
echo "$name was inherited\n";
12+
}
13+
}
14+
}
15+
?>
16+
--EXPECT--

0 commit comments

Comments
 (0)