diff --git a/Zend/tests/array_unpack/already_occupied.phpt b/Zend/tests/array_unpack/already_occupied.phpt index b2febe0021566..0448d0c41f846 100644 --- a/Zend/tests/array_unpack/already_occupied.phpt +++ b/Zend/tests/array_unpack/already_occupied.phpt @@ -1,7 +1,7 @@ --TEST-- Appending to an array via unpack may fail ---SKIPIF-- - +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- 0] === [0x100000000 => 0]); diff --git a/Zend/tests/bug70173.phpt b/Zend/tests/bug70173.phpt index 767973449afe4..035eecfed7827 100644 --- a/Zend/tests/bug70173.phpt +++ b/Zend/tests/bug70173.phpt @@ -1,9 +1,7 @@ --TEST-- Bug #70173 (ZVAL_COPY_VALUE_EX broken for 32bit Solaris Sparc) ---SKIPIF-- - +--PLATFORM-- +bits: 32 --FILE-- --INI-- memory_limit=1G diff --git a/Zend/tests/bug77660.phpt b/Zend/tests/bug77660.phpt index 94af1f9e2c526..4e0d499066fa3 100644 --- a/Zend/tests/bug77660.phpt +++ b/Zend/tests/bug77660.phpt @@ -1,7 +1,7 @@ --TEST-- Bug #77660 (Segmentation fault on break 2147483648) ---SKIPIF-- - +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- --FILE-- diff --git a/Zend/tests/decrement_001.phpt b/Zend/tests/decrement_001.phpt index 6382491799563..171310ed88563 100644 --- a/Zend/tests/decrement_001.phpt +++ b/Zend/tests/decrement_001.phpt @@ -1,7 +1,7 @@ --TEST-- decrementing different variables ---SKIPIF-- - +--PLATFORM-- +bits: 32 --INI-- precision=14 --FILE-- diff --git a/Zend/tests/decrement_001_64bit.phpt b/Zend/tests/decrement_001_64bit.phpt index b776bc5dd3896..1ffb6b7d85c77 100644 --- a/Zend/tests/decrement_001_64bit.phpt +++ b/Zend/tests/decrement_001_64bit.phpt @@ -1,7 +1,7 @@ --TEST-- decrementing different variables ---SKIPIF-- - +--PLATFORM-- +bits: 64 --INI-- precision=14 --FILE-- diff --git a/Zend/tests/double_to_string.phpt b/Zend/tests/double_to_string.phpt index 808024b0e568c..cd0451b66274a 100644 --- a/Zend/tests/double_to_string.phpt +++ b/Zend/tests/double_to_string.phpt @@ -2,8 +2,8 @@ double to string conversion tests --INI-- precision=14 ---SKIPIF-- - +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --INI-- precision=14 --FILE-- diff --git a/Zend/tests/dval_to_lval_32.phpt b/Zend/tests/dval_to_lval_32.phpt index 89acf9076ad75..fba631a36dd16 100644 --- a/Zend/tests/dval_to_lval_32.phpt +++ b/Zend/tests/dval_to_lval_32.phpt @@ -1,10 +1,7 @@ --TEST-- zend_dval_to_lval preserves low bits (32 bit long) ---SKIPIF-- - +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 32 --INI-- precision=14 --FILE-- diff --git a/Zend/tests/int_overflow_32bit.phpt b/Zend/tests/int_overflow_32bit.phpt index 15dce6eea7dcf..c74a803667f9a 100644 --- a/Zend/tests/int_overflow_32bit.phpt +++ b/Zend/tests/int_overflow_32bit.phpt @@ -1,7 +1,7 @@ --TEST-- testing integer overflow (32bit) ---SKIPIF-- - +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +# Internal function return types are only checked in debug builds +debug: true --INI-- opcache.jit=0 --FILE-- diff --git a/Zend/tests/type_declarations/scalar_return_basic.phpt b/Zend/tests/type_declarations/scalar_return_basic.phpt index 0cdce348c580c..992558f7ef8eb 100644 --- a/Zend/tests/type_declarations/scalar_return_basic.phpt +++ b/Zend/tests/type_declarations/scalar_return_basic.phpt @@ -1,7 +1,7 @@ --TEST-- Return scalar type basics ---SKIPIF-- - +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- 0) print "skip Running on 64-bit target"; ?> +--PLATFORM-- +bits: 32 --FILE-- +--PLATFORM-- +bits: 64 --FILE-- "worker$workerID"] : []; + $ext_params = []; + settings2array($ini_overwrites, $ext_params); + $ext_params = settings2params($ext_params); // Additional required extensions if ($test->hasSection('EXTENSIONS')) { - $ext_params = []; - settings2array($ini_overwrites, $ext_params); - $ext_params = settings2params($ext_params); $extensions = preg_split("/[\n\r]+/", trim($test->getSection('EXTENSIONS'))); - [$ext_dir, $loaded] = $skipCache->getExtensions("$orig_php $pass_options $extra_options $ext_params $no_file_cache"); + [$ext_dir, $loaded] = $skipChecker->getExtensions("$orig_php $pass_options $extra_options $ext_params $no_file_cache"); $ext_prefix = IS_WINDOWS ? "php_" : ""; $missing = []; foreach ($extensions as $req_ext) { @@ -2168,7 +2168,7 @@ function run_test(string $php, $file, array $env): string $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); + $output = $skipChecker->checkSkip($commandLine, $test->getSection('SKIPIF'), $test_skipif, $temp_skipif, $env); $time = microtime(true) - $startTime; $junit->stopTimer($shortname); @@ -2223,6 +2223,32 @@ function run_test(string $php, $file, array $env): string } } + if ($test->sectionNotEmpty('PLATFORM')) { + try { + $status = $skipChecker->checkPlatform( + "$orig_php $pass_options $extra_options $ext_params $no_file_cache", + $test->getSection('PLATFORM') + ); + if ($status !== null) { + show_result('SKIP', $tested, $tested_file, $status, $temp_filenames); + $junit->markTestAs('SKIP', $shortname, $tested, null, $status); + return 'SKIPPED'; + } + } catch (Throwable $ex) { + show_result("BORK", $ex->getMessage(), $tested_file, 'reason: error parsing PLATFORM', $temp_filenames); + $PHP_FAILED_TESTS['BORKED'][] = [ + 'name' => $file, + 'test_name' => '', + 'output' => '', + 'diff' => '', + 'info' => "{$ex->getMessage()} [$file]", + ]; + + $junit->markTestAs('BORK', $shortname, $tested, null, $ex->getMessage()); + return 'BORKED'; + } + } + if (!extension_loaded("zlib") && $test->hasAnySections("GZIP_POST", "DEFLATE_POST")) { $message = "ext/zlib required"; show_result('SKIP', $tested, $tested_file, "reason: $message", $temp_filenames); @@ -3524,6 +3550,8 @@ public function markTestAs( private function record(string $suite, string $param, $value = 1): void { + $this->initSuite($suite); + $this->rootSuite[$param] += $value; $this->suites[$suite][$param] += $value; } @@ -3657,17 +3685,17 @@ private function mergeSuites(array &$dest, array $source): void } } -class SkipCache +class SkipChecker { private bool $keepFile; private array $skips = []; - private array $extensions = []; + private array $info = []; private int $hits = 0; private int $misses = 0; - private int $extHits = 0; - private int $extMisses = 0; + private int $infoHits = 0; + private int $infoMisses = 0; public function __construct(bool $keepFile) { @@ -3707,23 +3735,113 @@ public function checkSkip(string $php, string $code, string $checkFile, string $ public function getExtensions(string $php): array { - if (isset($this->extensions[$php])) { - $this->extHits++; - return $this->extensions[$php]; + $info = $this->getPhpInfo($php); + + return [$info['dir'], $info['ext']]; + } + + public function checkPlatform(string $php, string $reqs): ?string + { + $info = $this->getPhpInfo($php); + + $failed = []; + foreach(preg_split('/\r?\n/', $reqs) as $line) { + $line = trim($line); + + // Ignore empty lines and #comments + if ($line === '' || $line[0] === '#') { + continue; + } + + if (!preg_match('/^(\w*?)\s*:\s*(\S.+)$/', $line, $matches)) { + throw new Exception("Invalid platform requirement: $line"); + } + list( , $req, $value) = $matches; + switch ($req) { + case 'bits': + if ($value !== '32' && $value !== '64') { + throw new Exception("Invalid bits requirement: $value"); + } + if ($value != $info['bits']) { + $failed[] = "$value bits build required"; + } + break; + case 'debug': + $value = $this->parseBool($req, $value); + if ($value !== $info['debug']) { + $failed[] = ($value ? '' : 'non-') . 'debug build required'; + } + break; + case 'zts': + $value = $this->parseBool($req, $value); + if ($value !== $info['zts']) { + $failed[] = ($value ? '' : 'non-') . 'ZTS build required'; + } + break; + case 'os': + $os = ltrim($value, '! '); + $inverse = $os !== $value; + $match = strcasecmp($os, PHP_OS_FAMILY) === 0; + if ($inverse) { + $match = !$match; + } + if (!$match) { + $failed[] = $inverse + ? 'this test is incompatible with ' . PHP_OS_FAMILY + : "this test is for $os only"; + } + break; + default: + throw new Exception("Invalid platform requirement: $line"); + } + } + + return $failed ? implode(', ', $failed) : null; + } + + private function parseBool(string $param, string $value): bool + { + return match ($value) { + 'true' => true, + 'false' => false, + default => throw new Exception("Invalid boolean value for platform requirement $param: '$value'") + }; + } + + /** + * Returns system information for the given PHP executable. It's not always the same as the one running this code + */ + private function getPhpInfo(string $php): array + { + if (isset($this->info[$php])) { + $this->infoHits++; + return $this->info[$php]; + } + + $code = "echo json_encode([" + . "'ext' => get_loaded_extensions()," + . "'dir' => ini_get('extension_dir')," + . "'bits' => PHP_INT_SIZE * 8," + . "'debug' => (bool)PHP_DEBUG," + . "'zts' => (bool)PHP_ZTS," + . "]);"; + $code = escapeshellarg($code); + $data = `$php -d display_errors=0 -r $code`; + $info = json_decode($data, true); + + if (!is_array($info) || array_keys($info) !== ['ext', 'dir', 'bits', 'debug', 'zts']) { + throw new Exception("Unexpected information returned for PHP '$php':\n\n$data"); } - $extDir = `$php -d display_errors=0 -r "echo ini_get('extension_dir');"`; - $extensions = explode(",", `$php -d display_errors=0 -r "echo implode(',', get_loaded_extensions());"`); - $extensions = array_map('strtolower', $extensions); - if (in_array('zend opcache', $extensions)) { - $extensions[] = 'opcache'; + $info['ext'] = array_map('strtolower', $info['ext']); + if (in_array('zend opcache', $info['ext'])) { + $info['ext'][] = 'opcache'; } - $result = [$extDir, $extensions]; - $this->extensions[$php] = $result; - $this->extMisses++; + $this->info[$php] = $info; + $this->infoMisses++; - return $result; + return $info; } // public function __destruct() @@ -3802,7 +3920,7 @@ class TestFile 'CAPTURE_STDIO', 'STDIN', 'CGI', 'PHPDBG', 'INI', 'ENV', 'EXTENSIONS', 'SKIPIF', 'XFAIL', 'XLEAK', 'CLEAN', - 'CREDITS', 'DESCRIPTION', 'CONFLICTS', 'WHITESPACE_SENSITIVE', + 'CREDITS', 'DESCRIPTION', 'CONFLICTS', 'WHITESPACE_SENSITIVE', 'PLATFORM' ]; public function __construct(string $fileName, bool $inRedirect)