diff --git a/docs/source/miscellaneous/writing-tests.rst b/docs/source/miscellaneous/writing-tests.rst index 8e17674ae481..0373ff64bf7c 100644 --- a/docs/source/miscellaneous/writing-tests.rst +++ b/docs/source/miscellaneous/writing-tests.rst @@ -683,6 +683,9 @@ An alternative to have a ``--CONFLICTS--`` section is to add a file named ``CONF directory containing the tests. The contents of the ``CONFLICTS`` file must have the same format as the contents of the ``--CONFLICTS--`` section. +Unlike a ``SCOPED_CONFLICTS`` file, a ``CONFLICTS`` file also prevents tests in its own directory +from running concurrently, regardless of whether the directory has a ``MAX_CONCURRENCY`` file. + **Required:** No. **Format:** One conflict key per line. Comment lines starting with # are also allowed. @@ -696,6 +699,33 @@ Example 1 (snippet): Example 1 (full): :ref:`conflicts_1.phpt` +``SCOPED_CONFLICTS`` file +------------------------- + +**Description:** This file is only relevant for parallel test execution. It specifies directory +conflict keys that tests in the same directory may share. Tests outside the directory that use the +same key remain mutually exclusive with the directory. Use this when a directory may run internally +in parallel but must not overlap another user of the same shared resource. + +This does not limit concurrency within the directory. Add a ``MAX_CONCURRENCY`` file separately if a +numerical cap is also required. + +**Required:** No. + +**Format:** One conflict key per line. Comment lines starting with # are also allowed. + +``MAX_CONCURRENCY`` file +------------------------ + +**Description:** This file is only relevant for parallel test execution. It limits how many workers +may simultaneously execute tests from its directory. It does not change the behavior of +``CONFLICTS`` or ``SCOPED_CONFLICTS``. This allows a bounded amount of parallelism for tests that +are safe to overlap but would otherwise contend excessively for a shared resource. + +**Required:** No. + +**Format:** A positive integer. Comment lines starting with # are also allowed. + ``--WHITESPACE_SENSITIVE--`` ---------------------------- diff --git a/ext/curl/tests/CONFLICTS b/ext/curl/tests/CONFLICTS deleted file mode 100644 index 0702cb5bfbb0..000000000000 --- a/ext/curl/tests/CONFLICTS +++ /dev/null @@ -1 +0,0 @@ -all diff --git a/ext/curl/tests/MAX_CONCURRENCY b/ext/curl/tests/MAX_CONCURRENCY new file mode 100644 index 000000000000..b8626c4cff28 --- /dev/null +++ b/ext/curl/tests/MAX_CONCURRENCY @@ -0,0 +1 @@ +4 diff --git a/ext/curl/tests/bug48203_multi.phpt b/ext/curl/tests/bug48203_multi.phpt index ec9748517ab8..db8cfde6a548 100644 --- a/ext/curl/tests/bug48203_multi.phpt +++ b/ext/curl/tests/bug48203_multi.phpt @@ -13,7 +13,7 @@ if (curl_version()['version_number'] === 0x080a00) { --CLEAN-- - + --EXPECTF-- Warning: curl_multi_add_handle(): CURLOPT_STDERR resource has gone away, resetting to stderr in %s on line %d %A diff --git a/ext/curl/tests/bug54798-unix.phpt b/ext/curl/tests/bug54798-unix.phpt index 6bbca3375ed5..1634e39c8b30 100644 --- a/ext/curl/tests/bug54798-unix.phpt +++ b/ext/curl/tests/bug54798-unix.phpt @@ -12,7 +12,7 @@ if(substr(PHP_OS, 0, 3) == 'WIN' ) { --CLEAN-- - + --EXPECTF-- %a %aOk for CURLOPT_STDERR diff --git a/ext/openssl/tests/MAX_CONCURRENCY b/ext/openssl/tests/MAX_CONCURRENCY new file mode 100644 index 000000000000..f599e28b8ab0 --- /dev/null +++ b/ext/openssl/tests/MAX_CONCURRENCY @@ -0,0 +1 @@ +10 diff --git a/ext/openssl/tests/CONFLICTS b/ext/openssl/tests/SCOPED_CONFLICTS similarity index 100% rename from ext/openssl/tests/CONFLICTS rename to ext/openssl/tests/SCOPED_CONFLICTS diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc index b7e866954fcf..0c3c49db7259 100644 --- a/ext/openssl/tests/ServerClientTestCase.inc +++ b/ext/openssl/tests/ServerClientTestCase.inc @@ -24,7 +24,7 @@ function phpt_has_sslv3() { if (!is_null($result)) { return $result; } - $server = @stream_socket_server('sslv3://127.0.0.1:10013'); + $server = @stream_socket_server('sslv3://127.0.0.1:0'); if ($result = !!$server) { fclose($server); } diff --git a/ext/openssl/tests/gh20802.phpt b/ext/openssl/tests/gh20802.phpt index 786679c41c6a..e078f461fc44 100644 --- a/ext/openssl/tests/gh20802.phpt +++ b/ext/openssl/tests/gh20802.phpt @@ -26,7 +26,7 @@ $serverCode = <<<'CODE' ] ] ]); - $server = stream_socket_server('tls://127.0.0.1:12443', $errno, $errstr, $flags, $ctx); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); phpt_notify_server_start($server); stream_socket_accept($server, 3); CODE; @@ -42,7 +42,7 @@ $ctx = stream_context_create([ 'verify_peer' => false ] ]); - @stream_socket_client("tls://127.0.0.1:12443", $errno, $errstr, 1, $flags, $ctx); + @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 1, $flags, $ctx); CODE; include 'CertificateGenerator.inc'; diff --git a/ext/openssl/tests/openssl_cms_verify_der.phpt b/ext/openssl/tests/openssl_cms_verify_der.phpt index b7267cbdfa33..84e1b70c9f2b 100644 --- a/ext/openssl/tests/openssl_cms_verify_der.phpt +++ b/ext/openssl/tests/openssl_cms_verify_der.phpt @@ -14,7 +14,7 @@ if ($contentfile === false) { die("failed to get a temporary filename!"); } -$pkcsfile = __DIR__ . "/openssl_cms_verify__pkcsfile.tmp"; +$pkcsfile = __DIR__ . "/openssl_cms_verify_der__pkcsfile.tmp"; $eml = __DIR__ . "/signed.eml"; $wrong = "wrong"; $empty = ""; @@ -45,7 +45,7 @@ if (file_exists($contentfile)) { ?> --CLEAN-- --EXPECT-- bool(false) diff --git a/ext/openssl/tests/openssl_pkcs12_export_to_file_error.phpt b/ext/openssl/tests/openssl_pkcs12_export_to_file_error.phpt index d98f6d15fef1..d95f9a1b3cfd 100644 --- a/ext/openssl/tests/openssl_pkcs12_export_to_file_error.phpt +++ b/ext/openssl/tests/openssl_pkcs12_export_to_file_error.phpt @@ -4,7 +4,7 @@ openssl_pkcs12_export_to_file() error tests openssl --FILE-- --CLEAN-- --FILE-- run($clientCode, $serverCode); ?> --CLEAN-- --EXPECTF-- string(%d) "-----BEGIN SSL SESSION PARAMETERS----- diff --git a/ext/openssl/tests/session_resumption_invalid_session_import.phpt b/ext/openssl/tests/session_resumption_invalid_session_import.phpt index a9c5b65f2051..efd49e7c693b 100644 --- a/ext/openssl/tests/session_resumption_invalid_session_import.phpt +++ b/ext/openssl/tests/session_resumption_invalid_session_import.phpt @@ -15,9 +15,5 @@ try { echo $e->getMessage() . "\n"; } ?> ---CLEAN-- - --EXPECT-- Failed to import session data diff --git a/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt b/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt index bdc3d2ce1bf6..12d5a13c3755 100644 --- a/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt +++ b/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt @@ -8,7 +8,7 @@ if (!function_exists("proc_open")) die("skip no proc_open"); ?> --FILE-- run($clientCode, $serverCode); ?> --CLEAN-- --EXPECTF-- Client first connection resumed: no diff --git a/ext/openssl/tests/stream_server_reneg_limit.phpt b/ext/openssl/tests/stream_server_reneg_limit.phpt index d84906c81ca7..a9e147404690 100644 --- a/ext/openssl/tests/stream_server_reneg_limit.phpt +++ b/ext/openssl/tests/stream_server_reneg_limit.phpt @@ -25,7 +25,7 @@ $certFile = __DIR__ . DIRECTORY_SEPARATOR . 'stream_server_reneg_limit.pem.tmp'; $serverCode = <<<'CODE' $printed = false; - $serverUri = "ssl://127.0.0.1:64321"; + $serverUri = "ssl://127.0.0.1:0"; $serverFlags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '%s', @@ -42,7 +42,7 @@ $serverCode = <<<'CODE' ]]); $server = stream_socket_server($serverUri, $errno, $errstr, $serverFlags, $serverCtx); - phpt_notify(); + phpt_notify(message: stream_socket_get_name($server, false)); $clients = []; while (1) { @@ -71,9 +71,9 @@ CODE; $serverCode = sprintf($serverCode, $certFile); $clientCode = <<<'CODE' - phpt_wait(); + $addr = trim(phpt_wait()); - $cmd = 'openssl s_client -connect 127.0.0.1:64321'; + $cmd = 'openssl s_client -connect ' . escapeshellarg($addr); $descriptorSpec = [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]]; $process = proc_open($cmd, $descriptorSpec, $pipes); diff --git a/run-tests.php b/run-tests.php index 998e7e24c337..f0cc66ddf1c9 100755 --- a/run-tests.php +++ b/run-tests.php @@ -1376,17 +1376,38 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te // Each test may specify a list of conflict keys. While a test that conflicts with // key K is running, no other test that conflicts with K may run. Conflict keys are - // specified either in the --CONFLICTS-- section, or CONFLICTS file inside a directory. + // specified either in the --CONFLICTS-- section, or a CONFLICTS or SCOPED_CONFLICTS + // file inside a directory. $dirConflictsWith = []; + $dirScopedConflictsWith = []; $fileConflictsWith = []; + $fileConflictGroups = []; + // A MAX_CONCURRENCY file limits how many workers may run tests from that directory. + $dirMaxConcurrency = []; + $fileConcurrencyDirectory = []; $sequentialTests = []; foreach ($test_files as $i => $file) { + $dir = dirname($file); + if (!array_key_exists($dir, $dirMaxConcurrency)) { + $maxConcurrencyFile = $dir . '/MAX_CONCURRENCY'; + $dirMaxConcurrency[$dir] = null; + if (file_exists($maxConcurrencyFile)) { + $dirMaxConcurrency[$dir] = parse_max_concurrency( + file_get_contents($maxConcurrencyFile), + $maxConcurrencyFile, + ); + } + } + if ($dirMaxConcurrency[$dir] !== null) { + $fileConcurrencyDirectory[$file] = $dir; + } + $contents = file_get_contents($file); + $conflictGroups = []; if (preg_match('/^--CONFLICTS--(.+?)^--/ms', $contents, $matches)) { $conflicts = parse_conflicts($matches[1]); } else { // Cache per-directory conflicts in a separate map, so we compute these only once. - $dir = dirname($file); if (!isset($dirConflictsWith[$dir])) { $dirConflicts = []; if (file_exists($dir . '/CONFLICTS')) { @@ -1396,6 +1417,22 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te $dirConflictsWith[$dir] = $dirConflicts; } $conflicts = $dirConflictsWith[$dir]; + + if (!isset($dirScopedConflictsWith[$dir])) { + $dirScopedConflicts = []; + $scopedConflictsFile = $dir . '/SCOPED_CONFLICTS'; + if (file_exists($scopedConflictsFile)) { + $dirScopedConflicts = parse_conflicts(file_get_contents($scopedConflictsFile)); + } + $dirScopedConflictsWith[$dir] = $dirScopedConflicts; + } + foreach ($dirScopedConflictsWith[$dir] as $conflictKey) { + if (in_array($conflictKey, $conflicts, true)) { + error("Conflict key '$conflictKey' is listed in both CONFLICTS and SCOPED_CONFLICTS in $dir"); + } + $conflicts[] = $conflictKey; + $conflictGroups[$conflictKey] = $dir; + } } // For tests conflicting with "all", no other tests may run in parallel. We'll run these @@ -1406,6 +1443,7 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te } $fileConflictsWith[$file] = $conflicts; + $fileConflictGroups[$file] = $conflictGroups; } // Some tests assume that they are executed in a certain order. We will be popping from @@ -1504,10 +1542,16 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te $rawMessageBuffers = []; $testsInProgress = 0; - // Map from conflict key to worker ID. + // Maps conflict keys to worker IDs and their optional internally compatible group. $activeConflicts = []; // Tests waiting due to conflicts. Map from conflict key to array. $waitingTests = []; + // Maps capped directories to the workers currently running tests from them. + $activeConcurrencyDirectories = []; + // Maps workers to the capped directories acquired for their current batch. + $workerConcurrencyDirectories = []; + // Tests waiting for capacity in a capped directory. + $waitingConcurrencyTests = []; escape: while ($test_files || $sequentialTests || $testsInProgress > 0) { @@ -1546,15 +1590,28 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te switch ($message["type"]) { case "tests_finished": $testsInProgress--; - foreach ($activeConflicts as $key => $workerId) { - if ($workerId === $i) { - unset($activeConflicts[$key]); - if (isset($waitingTests[$key])) { - while ($test = array_pop($waitingTests[$key])) { - $test_files[] = $test; - } - unset($waitingTests[$key]); + foreach ($workerConcurrencyDirectories[$i] ?? [] as $dir) { + unset($activeConcurrencyDirectories[$dir][$i]); + if (isset($waitingConcurrencyTests[$dir])) { + while ($test = array_pop($waitingConcurrencyTests[$dir])) { + $test_files[] = $test; + } + unset($waitingConcurrencyTests[$dir]); + } + } + unset($workerConcurrencyDirectories[$i]); + foreach ($activeConflicts as $key => $activeGroups) { + unset($activeGroups[$i]); + if ($activeGroups) { + $activeConflicts[$key] = $activeGroups; + continue; + } + unset($activeConflicts[$key]); + if (isset($waitingTests[$key])) { + while ($test = array_pop($waitingTests[$key])) { + $test_files[] = $test; } + unset($waitingTests[$key]); } } $junit->mergeResults($message["junit"]); @@ -1571,24 +1628,57 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te // - If this is running a small enough number of tests, // reduce the batch size to give batches to more workers. $files = []; + $batchConcurrencyDirectories = []; $maxBatchSize = $valgrind ? 1 : ($shuffle ? 4 : 32); $averageFilesPerWorker = max(1, (int) ceil($totalFileCount / count($workerProcs))); $batchSize = min($maxBatchSize, $averageFilesPerWorker); - while (count($files) <= $batchSize && $file = array_pop($test_files)) { + while (count($files) < $batchSize && $file = array_pop($test_files)) { foreach ($fileConflictsWith[$file] as $conflictKey) { - if (isset($activeConflicts[$conflictKey])) { + $conflictGroup = $fileConflictGroups[$file][$conflictKey] ?? null; + if ( + isset($activeConflicts[$conflictKey]) + && has_active_conflict( + $activeConflicts[$conflictKey], + $conflictGroup, + ) + ) { $waitingTests[$conflictKey][] = $file; continue 2; } } + $concurrencyDirectory = $fileConcurrencyDirectory[$file] ?? null; + if ( + $concurrencyDirectory !== null + && !isset($batchConcurrencyDirectories[$concurrencyDirectory]) + && count($activeConcurrencyDirectories[$concurrencyDirectory] ?? []) + >= $dirMaxConcurrency[$concurrencyDirectory] + ) { + $waitingConcurrencyTests[$concurrencyDirectory][] = $file; + continue; + } $files[] = $file; + if ($concurrencyDirectory !== null) { + $batchConcurrencyDirectories[$concurrencyDirectory] = true; + } } if ($files) { foreach ($files as $file) { foreach ($fileConflictsWith[$file] as $conflictKey) { - $activeConflicts[$conflictKey] = $i; + $conflictGroup = $fileConflictGroups[$file][$conflictKey] ?? null; + if ( + array_key_exists($i, $activeConflicts[$conflictKey] ?? []) + && $activeConflicts[$conflictKey][$i] !== $conflictGroup + ) { + $activeConflicts[$conflictKey][$i] = null; + } else { + $activeConflicts[$conflictKey][$i] = $conflictGroup; + } } } + $workerConcurrencyDirectories[$i] = array_keys($batchConcurrencyDirectories); + foreach ($workerConcurrencyDirectories[$i] as $dir) { + $activeConcurrencyDirectories[$dir][$i] = true; + } $testsInProgress++; send_message($workerSocks[$i], [ "type" => "run_tests", @@ -3267,6 +3357,35 @@ function parse_conflicts(string $text): array return array_map('trim', explode("\n", trim($text))); } +/** + * @param array $activeGroups + */ +function has_active_conflict(array $activeGroups, ?string $candidateGroup): bool +{ + if ($candidateGroup === null) { + return true; + } + foreach ($activeGroups as $activeGroup) { + if ($activeGroup !== $candidateGroup) { + return true; + } + } + return false; +} + +function parse_max_concurrency(string $text, string $file): int +{ + // Strip comments + $text = trim(preg_replace('/#.*/', '', $text)); + if ( + preg_match('/^[1-9][0-9]*$/D', $text) !== 1 + || (string) (int) $text !== $text + ) { + error("Invalid positive integer in $file"); + } + return (int) $text; +} + function show_result( string $result, string $tested, diff --git a/tests/run-test/max_concurrency_conflicts.phpt b/tests/run-test/max_concurrency_conflicts.phpt new file mode 100644 index 000000000000..0aa7863eb42d --- /dev/null +++ b/tests/run-test/max_concurrency_conflicts.phpt @@ -0,0 +1,106 @@ +--TEST-- +MAX_CONCURRENCY does not weaken directory conflicts +--ENV-- +TEST_PHP_FORK_SERVER=0 +--FILE-- + + --EXPECT-- + ok + PHPT); +} + +$root = __DIR__ . '/max_concurrency_conflicts_' . getmypid(); +$results = $root . '/results.txt'; +mkdir($root); +file_put_contents($root . '/CONFLICTS', "shared\n"); +file_put_contents($root . '/MAX_CONCURRENCY', "2\n"); + +$rootExpression = var_export($root, true); +$testCode = <<<'PHP' + $root = %s; + $marker = $root . '/active-' . getmypid(); + file_put_contents($marker, ''); + $deadline = microtime(true) + 0.5; + do { + $overlap = count(glob($root . '/active-*')) > 1; + if ($overlap) { + break; + } + usleep(10_000); + } while (microtime(true) < $deadline); + unlink($marker); + echo $overlap ? "overlap\n" : "ok\n"; + PHP; +$testCode = sprintf($testCode, $rootExpression); +writeParallelTest($root . '/one.phpt', 'conflict one', $testCode); +writeParallelTest($root . '/two.phpt', 'conflict two', $testCode); + +$command = [ + getenv('TEST_PHP_EXECUTABLE'), + dirname(__DIR__, 2) . '/run-tests.php', + '-q', + '-j2', + '-W', + $results, + $root . '/one.phpt', + $root . '/two.phpt', +]; +$environment = [ + 'PATH' => getenv('PATH'), + 'TEST_PHP_EXECUTABLE' => getenv('TEST_PHP_EXECUTABLE'), + 'TEST_PHP_FORK_SERVER' => '0', +]; +foreach (['SystemRoot', 'TEMP', 'TMPDIR'] as $name) { + if (($value = getenv($name)) !== false) { + $environment[$name] = $value; + } +} +$process = proc_open( + $command, + [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['redirect', 1], + ], + $pipes, + null, + $environment, +); +fclose($pipes[0]); +$output = stream_get_contents($pipes[1]); +fclose($pipes[1]); +$exitCode = proc_close($process); + +$resultLines = file($results, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); +sort($resultLines); +foreach ($resultLines as $resultLine) { + [$status, $file] = explode("\t", $resultLine, 2); + echo "$status ", basename($file), "\n"; +} +var_dump($exitCode); +if ($exitCode !== 0) { + echo $output; +} +?> +--CLEAN-- + +--EXPECT-- +PASSED one.phpt +PASSED two.phpt +int(0) diff --git a/tests/run-test/scoped_conflicts.phpt b/tests/run-test/scoped_conflicts.phpt new file mode 100644 index 000000000000..fd66e9fa52a5 --- /dev/null +++ b/tests/run-test/scoped_conflicts.phpt @@ -0,0 +1,145 @@ +--TEST-- +Scoped directory conflicts allow internal concurrency +--ENV-- +TEST_PHP_FORK_SERVER=0 +--FILE-- + + --EXPECT-- + ok + PHPT); +} + +$root = __DIR__ . '/scoped_conflicts_' . getmypid(); +$group = $root . '/a-group'; +$external = $root . '/z-external'; +$results = $root . '/results.txt'; +mkdir($group, recursive: true); +mkdir($external); +file_put_contents($group . '/SCOPED_CONFLICTS', "shared\n"); +file_put_contents($group . '/MAX_CONCURRENCY', "2\n"); + +$rootExpression = var_export($root, true); +$groupCode = <<<'PHP' + $root = %s; + $marker = $root . '/active-group-' . getmypid(); + file_put_contents($marker, ''); + $deadline = microtime(true) + 10; + do { + $groupIsParallel = count(glob($root . '/active-group-*')) === 2; + if ($groupIsParallel) { + break; + } + usleep(10_000); + } while (microtime(true) < $deadline); + if ($groupIsParallel) { + // Give the peer time to observe both markers before either is removed. + usleep(100_000); + } + $externalOverlaps = file_exists($root . '/active-external'); + unlink($marker); + echo $groupIsParallel && !$externalOverlaps ? "ok\n" : "not isolated\n"; + PHP; +$groupCode = sprintf($groupCode, $rootExpression); +writeParallelTest($group . '/one.phpt', 'scoped conflict one', $groupCode); +writeParallelTest($group . '/two.phpt', 'scoped conflict two', $groupCode); + +$externalCode = << + --EXPECT-- + ok + PHPT); + +$command = [ + getenv('TEST_PHP_EXECUTABLE'), + dirname(__DIR__, 2) . '/run-tests.php', + '-q', + '-j3', + '-W', + $results, + $group . '/one.phpt', + $group . '/two.phpt', + $external . '/external.phpt', +]; +$environment = [ + 'PATH' => getenv('PATH'), + 'TEST_PHP_EXECUTABLE' => getenv('TEST_PHP_EXECUTABLE'), + 'TEST_PHP_FORK_SERVER' => '0', +]; +foreach (['SystemRoot', 'TEMP', 'TMPDIR'] as $name) { + if (($value = getenv($name)) !== false) { + $environment[$name] = $value; + } +} +$process = proc_open( + $command, + [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['redirect', 1], + ], + $pipes, + null, + $environment, +); +fclose($pipes[0]); +$output = stream_get_contents($pipes[1]); +fclose($pipes[1]); +$exitCode = proc_close($process); + +$resultLines = file($results, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); +sort($resultLines); +foreach ($resultLines as $resultLine) { + [$status, $file] = explode("\t", $resultLine, 2); + echo "$status ", basename($file), "\n"; +} +var_dump($exitCode); +if ($exitCode !== 0) { + echo $output; +} +?> +--CLEAN-- + +--EXPECT-- +PASSED one.phpt +PASSED two.phpt +PASSED external.phpt +int(0) diff --git a/tests/security/MAX_CONCURRENCY b/tests/security/MAX_CONCURRENCY new file mode 100644 index 000000000000..f599e28b8ab0 --- /dev/null +++ b/tests/security/MAX_CONCURRENCY @@ -0,0 +1 @@ +10 diff --git a/tests/security/CONFLICTS b/tests/security/SCOPED_CONFLICTS similarity index 100% rename from tests/security/CONFLICTS rename to tests/security/SCOPED_CONFLICTS diff --git a/tests/security/open_basedir.inc b/tests/security/open_basedir.inc index 21338ef1cf55..abb2b3140812 100644 --- a/tests/security/open_basedir.inc +++ b/tests/security/open_basedir.inc @@ -40,7 +40,7 @@ function recursive_delete_directory($directory) { if ($item != '.') { if ($item != '..') { $path = ($directory.'/'.$item); - if (is_dir($path) == TRUE) { + if (is_dir($path) == TRUE && !is_link($path)) { recursive_delete_directory($path); } else { @chmod($path, 0777); @@ -57,9 +57,24 @@ function recursive_delete_directory($directory) { return TRUE; } +function open_basedir_test_directory() { + foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { + if (!isset($frame["file"]) || $frame["file"] === __FILE__) { + continue; + } + + $test = preg_replace('/(?:\.clean)?\.php$/', '', basename($frame["file"])); + return __DIR__."/open_basedir_test_".$test; + } + + return __DIR__."/open_basedir_test"; +} + function create_directories() { delete_directories(); - $directory = getcwd(); + $directory = open_basedir_test_directory(); + mkdir($directory); + chdir($directory); var_dump(mkdir($directory."/test")); var_dump(mkdir($directory."/test/ok")); @@ -69,8 +84,7 @@ function create_directories() { } function delete_directories() { - $directory = (getcwd()."/test"); - recursive_delete_directory($directory); + recursive_delete_directory(open_basedir_test_directory()); } function test_open_basedir_error($function) { @@ -87,12 +101,15 @@ function test_open_basedir_error($function) { } function test_open_basedir_before($function, $change = TRUE) { - global $savedDirectory; + global $initdir, $savedDirectory; echo "*** Testing open_basedir configuration [$function] ***\n"; + create_directories(); $directory = getcwd(); + if (isset($initdir)) { + $initdir = $directory; + } $savedDirectory = $directory; var_dump(chdir($directory)); - create_directories(); // Optionally change directory if ($change == TRUE) {