1#!/usr/bin/env php 2<?php 3/* 4 +----------------------------------------------------------------------+ 5 | PHP Version 7 | 6 +----------------------------------------------------------------------+ 7 | Copyright (c) 1997-2018 The PHP Group | 8 +----------------------------------------------------------------------+ 9 | This source file is subject to version 3.01 of the PHP license, | 10 | that is bundled with this package in the file LICENSE, and is | 11 | available through the world-wide-web at the following url: | 12 | https://php.net/license/3_01.txt | 13 | If you did not receive a copy of the PHP license and are unable to | 14 | obtain it through the world-wide-web, please send a note to | 15 | license@php.net so we can mail you a copy immediately. | 16 +----------------------------------------------------------------------+ 17 | Authors: Ilia Alshanetsky <iliaa@php.net> | 18 | Preston L. Bannister <pbannister@php.net> | 19 | Marcus Boerger <helly@php.net> | 20 | Derick Rethans <derick@php.net> | 21 | Sander Roobol <sander@php.net> | 22 | (based on version by: Stig Bakken <ssb@php.net>) | 23 | (based on the PHP 3 test framework by Rasmus Lerdorf) | 24 +----------------------------------------------------------------------+ 25 */ 26 27/* $Id: 6e551f4bb4be63fc488bb5568aa27da773720a97 $ */ 28 29define('INIT_DIR', getcwd()); 30 31// change into the PHP source directory. 32if (getenv('TEST_PHP_SRCDIR')) { 33 @chdir(getenv('TEST_PHP_SRCDIR')); 34} 35define('TEST_PHP_SRCDIR', getcwd()); 36 37 38/* Sanity check to ensure that pcre extension needed by this script is available. 39 * In the event it is not, print a nice error message indicating that this script will 40 * not run without it. 41 */ 42 43if (!extension_loaded('pcre')) { 44 echo <<<NO_PCRE_ERROR 45 46+-----------------------------------------------------------+ 47| ! ERROR ! | 48| The test-suite requires that you have pcre extension | 49| enabled. To enable this extension either compile your PHP | 50| with --with-pcre-regex or if you've compiled pcre as a | 51| shared module load it via php.ini. | 52+-----------------------------------------------------------+ 53 54NO_PCRE_ERROR; 55exit(1); 56} 57 58if (!function_exists('proc_open')) { 59 echo <<<NO_PROC_OPEN_ERROR 60 61+-----------------------------------------------------------+ 62| ! ERROR ! | 63| The test-suite requires that proc_open() is available. | 64| Please check if you disabled it in php.ini. | 65+-----------------------------------------------------------+ 66 67NO_PROC_OPEN_ERROR; 68exit(1); 69} 70 71// If timezone is not set, use UTC. 72if (ini_get('date.timezone') == '') { 73 date_default_timezone_set('UTC'); 74} 75 76// Delete some security related environment variables 77putenv('SSH_CLIENT=deleted'); 78putenv('SSH_AUTH_SOCK=deleted'); 79putenv('SSH_TTY=deleted'); 80putenv('SSH_CONNECTION=deleted'); 81 82set_time_limit(0); 83 84ini_set('pcre.backtrack_limit', PHP_INT_MAX); 85 86// delete as much output buffers as possible 87while(@ob_end_clean()); 88if (ob_get_level()) echo "Not all buffers were deleted.\n"; 89 90error_reporting(E_ALL); 91 92$environment = $_ENV ?? array(); 93// Note: php.ini-development sets variables_order="GPCS" not "EGPCS", in which case $_ENV is NOT populated. 94// detect and handle this case, or die or warn 95if (empty($environment)) { 96 // not documented, but returns array of all environment variables 97 $environment = getenv(); 98} 99if (empty($environment['TEMP'])) { 100 $environment['TEMP'] = sys_get_temp_dir(); 101 102 if (empty($environment['TEMP'])) { 103 // for example, OpCache on Windows will fail in this case because child processes (for tests) will not get 104 // a TEMP variable, so GetTempPath() will fallback to c:\windows, while GetTempPath() will return %TEMP% for parent 105 // (likely a different path). The parent will initialize the OpCache in that path, and child will fail to reattach to 106 // the OpCache because it will be using the wrong path. 107 die("TEMP environment is NOT set"); 108 } else if (count($environment)==1) { 109 // not having other environment variables, only having TEMP, is probably ok, but strange and may make a 110 // difference in the test pass rate, so warn the user. 111 echo "WARNING: Only 1 environment variable will be available to tests(TEMP environment variable)".PHP_EOL; 112 } 113} 114// 115if ((substr(PHP_OS, 0, 3) == "WIN") && empty($environment["SystemRoot"])) { 116 $environment["SystemRoot"] = getenv("SystemRoot"); 117} 118 119// Don't ever guess at the PHP executable location. 120// Require the explicit specification. 121// Otherwise we could end up testing the wrong file! 122 123$php = null; 124$php_cgi = null; 125$phpdbg = null; 126 127if (getenv('TEST_PHP_EXECUTABLE')) { 128 $php = getenv('TEST_PHP_EXECUTABLE'); 129 130 if ($php=='auto') { 131 $php = TEST_PHP_SRCDIR . '/sapi/cli/php'; 132 putenv("TEST_PHP_EXECUTABLE=$php"); 133 134 if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { 135 $php_cgi = TEST_PHP_SRCDIR . '/sapi/cgi/php-cgi'; 136 137 if (file_exists($php_cgi)) { 138 putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); 139 } else { 140 $php_cgi = null; 141 } 142 } 143 } 144 $environment['TEST_PHP_EXECUTABLE'] = $php; 145} 146 147if (getenv('TEST_PHP_CGI_EXECUTABLE')) { 148 $php_cgi = getenv('TEST_PHP_CGI_EXECUTABLE'); 149 150 if ($php_cgi=='auto') { 151 $php_cgi = TEST_PHP_SRCDIR . '/sapi/cgi/php-cgi'; 152 putenv("TEST_PHP_CGI_EXECUTABLE=$php_cgi"); 153 } 154 155 $environment['TEST_PHP_CGI_EXECUTABLE'] = $php_cgi; 156} 157 158if (!getenv('TEST_PHPDBG_EXECUTABLE')) { 159 if (!strncasecmp(PHP_OS, "win", 3) && file_exists(dirname($php) . "/phpdbg.exe")) { 160 $phpdbg = realpath(dirname($php) . "/phpdbg.exe"); 161 } elseif (file_exists(dirname($php) . "/../../sapi/phpdbg/phpdbg")) { 162 $phpdbg = realpath(dirname($php) . "/../../sapi/phpdbg/phpdbg"); 163 } elseif (file_exists("./sapi/phpdbg/phpdbg")) { 164 $phpdbg = realpath("./sapi/phpdbg/phpdbg"); 165 } elseif (file_exists(dirname($php) . "/phpdbg")) { 166 $phpdbg = realpath(dirname($php) . "/phpdbg"); 167 } else { 168 $phpdbg = null; 169 } 170 if ($phpdbg) { 171 putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); 172 } 173} 174 175if (getenv('TEST_PHPDBG_EXECUTABLE')) { 176 $phpdbg = getenv('TEST_PHPDBG_EXECUTABLE'); 177 178 if ($phpdbg=='auto') { 179 $phpdbg = TEST_PHP_SRCDIR . '/sapi/phpdbg/phpdbg'; 180 putenv("TEST_PHPDBG_EXECUTABLE=$phpdbg"); 181 } 182 183 $environment['TEST_PHPDBG_EXECUTABLE'] = $phpdbg; 184} 185 186if (!function_exists("hrtime")) { 187 function hrtime(bool $as_num = false) 188 { 189 $t = microtime(true); 190 191 if ($as_num) { 192 return $t*1000000000; 193 } 194 195 $s = floor($t); 196 return array(0 => $s, 1 => ($t - $s)*1000000000); 197 } 198} 199 200function verify_config() 201{ 202 global $php; 203 204 if (empty($php) || !file_exists($php)) { 205 error('environment variable TEST_PHP_EXECUTABLE must be set to specify PHP executable!'); 206 } 207 208 if (function_exists('is_executable') && !is_executable($php)) { 209 error("invalid PHP executable specified by TEST_PHP_EXECUTABLE = $php"); 210 } 211} 212 213if (getenv('TEST_PHP_LOG_FORMAT')) { 214 $log_format = strtoupper(getenv('TEST_PHP_LOG_FORMAT')); 215} else { 216 $log_format = 'LEODS'; 217} 218 219// Check whether a detailed log is wanted. 220if (getenv('TEST_PHP_DETAILED')) { 221 $DETAILED = getenv('TEST_PHP_DETAILED'); 222} else { 223 $DETAILED = 0; 224} 225 226junit_init(); 227 228if (getenv('SHOW_ONLY_GROUPS')) { 229 $SHOW_ONLY_GROUPS = explode(",", getenv('SHOW_ONLY_GROUPS')); 230} else { 231 $SHOW_ONLY_GROUPS = array(); 232} 233 234// Check whether user test dirs are requested. 235if (getenv('TEST_PHP_USER')) { 236 $user_tests = explode (',', getenv('TEST_PHP_USER')); 237} else { 238 $user_tests = array(); 239} 240 241$exts_to_test = array(); 242$ini_overwrites = array( 243 'output_handler=', 244 'open_basedir=', 245 'disable_functions=', 246 'output_buffering=Off', 247 'error_reporting=' . (E_ALL | E_STRICT), 248 'display_errors=1', 249 'display_startup_errors=1', 250 'log_errors=0', 251 'html_errors=0', 252 'track_errors=0', 253 'report_memleaks=1', 254 'report_zend_debug=0', 255 'docref_root=', 256 'docref_ext=.html', 257 'error_prepend_string=', 258 'error_append_string=', 259 'auto_prepend_file=', 260 'auto_append_file=', 261 'ignore_repeated_errors=0', 262 'precision=14', 263 'memory_limit=128M', 264 'log_errors_max_len=0', 265 'opcache.fast_shutdown=0', 266 'opcache.file_update_protection=0', 267 'zend.assertions=1', 268 ); 269 270$no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0'; 271 272function write_information() 273{ 274 global $php, $php_cgi, $phpdbg, $php_info, $user_tests, $ini_overwrites, $pass_options, $exts_to_test, $valgrind, $no_file_cache; 275 276 // Get info from php 277 $info_file = __DIR__ . '/run-test-info.php'; 278 @unlink($info_file); 279 $php_info = '<?php echo " 280PHP_SAPI : " , PHP_SAPI , " 281PHP_VERSION : " , phpversion() , " 282ZEND_VERSION: " , zend_version() , " 283PHP_OS : " , PHP_OS , " - " , php_uname() , " 284INI actual : " , realpath(get_cfg_var("cfg_file_path")) , " 285More .INIs : " , (function_exists(\'php_ini_scanned_files\') ? str_replace("\n","", php_ini_scanned_files()) : "** not determined **"); ?>'; 286 save_text($info_file, $php_info); 287 $info_params = array(); 288 settings2array($ini_overwrites, $info_params); 289 settings2params($info_params); 290 $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; 291 define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); 292 293 if ($php_cgi && $php != $php_cgi) { 294 $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`; 295 $php_info_sep = "\n---------------------------------------------------------------------"; 296 $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; 297 } else { 298 $php_cgi_info = ''; 299 } 300 301 if ($phpdbg) { 302 $phpdbg_info = `$phpdbg $pass_options $info_params $no_file_cache -qrr "$info_file"`; 303 $php_info_sep = "\n---------------------------------------------------------------------"; 304 $phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep"; 305 } else { 306 $phpdbg_info = ''; 307 } 308 309 if (function_exists('opcache_invalidate')) { 310 opcache_invalidate($info_file, true); 311 } 312 @unlink($info_file); 313 314 // load list of enabled extensions 315 save_text($info_file, '<?php echo str_replace("Zend OPcache", "opcache", implode(",", get_loaded_extensions())); ?>'); 316 $exts_to_test = explode(',',`$php $pass_options $info_params $no_file_cache "$info_file"`); 317 // check for extensions that need special handling and regenerate 318 $info_params_ex = array( 319 'session' => array('session.auto_start=0'), 320 'tidy' => array('tidy.clean_output=0'), 321 'zlib' => array('zlib.output_compression=Off'), 322 'xdebug' => array('xdebug.default_enable=0','xdebug.mode=off'), 323 'mbstring' => array('mbstring.func_overload=0'), 324 ); 325 326 foreach($info_params_ex as $ext => $ini_overwrites_ex) { 327 if (in_array($ext, $exts_to_test)) { 328 $ini_overwrites = array_merge($ini_overwrites, $ini_overwrites_ex); 329 } 330 } 331 332 if (function_exists('opcache_invalidate')) { 333 opcache_invalidate($info_file, true); 334 } 335 @unlink($info_file); 336 337 // Write test context information. 338 echo " 339===================================================================== 340PHP : $php $php_info $php_cgi_info $phpdbg_info 341CWD : ".TEST_PHP_SRCDIR." 342Extra dirs : "; 343 foreach ($user_tests as $test_dir) { 344 echo "{$test_dir}\n "; 345 } 346 echo " 347VALGRIND : " . ($valgrind ? $valgrind->getHeader() : 'Not used') . " 348===================================================================== 349"; 350} 351 352define('PHP_QA_EMAIL', 'qa-reports@lists.php.net'); 353define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php'); 354define('QA_REPORTS_PAGE', 'http://qa.php.net/reports'); 355define('TRAVIS_CI' , (bool) getenv('TRAVIS')); 356 357function save_or_mail_results() 358{ 359 global $sum_results, $just_save_results, $failed_test_summary, 360 $PHP_FAILED_TESTS, $php, $output_file; 361 362 /* We got failed Tests, offer the user to send an e-mail to QA team, unless NO_INTERACTION is set */ 363 if (!getenv('NO_INTERACTION') && !TRAVIS_CI) { 364 $fp = fopen("php://stdin", "r+"); 365 if ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['WARNED'] || $sum_results['LEAKED']) { 366 echo "\nYou may have found a problem in PHP."; 367 } 368 echo "\nThis report can be automatically sent to the PHP QA team at\n"; 369 echo QA_REPORTS_PAGE . " and http://news.php.net/php.qa.reports\n"; 370 echo "This gives us a better understanding of PHP's behavior.\n"; 371 echo "If you don't want to send the report immediately you can choose\n"; 372 echo "option \"s\" to save it. You can then email it to ". PHP_QA_EMAIL . " later.\n"; 373 echo "Do you want to send this report now? [Yns]: "; 374 flush(); 375 376 $user_input = fgets($fp, 10); 377 $just_save_results = (strtolower($user_input[0]) == 's'); 378 } 379 380 if ($just_save_results || !getenv('NO_INTERACTION') || TRAVIS_CI) { 381 if ($just_save_results || TRAVIS_CI || strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y') { 382 /* 383 * Collect information about the host system for our report 384 * Fetch phpinfo() output so that we can see the PHP environment 385 * Make an archive of all the failed tests 386 * Send an email 387 */ 388 if ($just_save_results) { 389 $user_input = 's'; 390 } 391 392 /* Ask the user to provide an email address, so that QA team can contact the user */ 393 if (TRAVIS_CI) { 394 $user_email = 'travis at php dot net'; 395 } elseif (!strncasecmp($user_input, 'y', 1) || strlen(trim($user_input)) == 0) { 396 echo "\nPlease enter your email address.\n(Your address will be mangled so that it will not go out on any\nmailinglist in plain text): "; 397 flush(); 398 $user_email = trim(fgets($fp, 1024)); 399 $user_email = str_replace("@", " at ", str_replace(".", " dot ", $user_email)); 400 } 401 402 $failed_tests_data = ''; 403 $sep = "\n" . str_repeat('=', 80) . "\n"; 404 $failed_tests_data .= $failed_test_summary . "\n"; 405 $failed_tests_data .= get_summary(true, false) . "\n"; 406 407 if ($sum_results['FAILED']) { 408 foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { 409 $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; 410 $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']), FILE_BINARY); 411 $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']), FILE_BINARY); 412 $failed_tests_data .= $sep . "\n\n"; 413 } 414 $status = "failed"; 415 } else { 416 $status = "success"; 417 } 418 419 $failed_tests_data .= "\n" . $sep . 'BUILD ENVIRONMENT' . $sep; 420 $failed_tests_data .= "OS:\n" . PHP_OS . " - " . php_uname() . "\n\n"; 421 $ldd = $autoconf = $sys_libtool = $libtool = $compiler = 'N/A'; 422 423 if (substr(PHP_OS, 0, 3) != "WIN") { 424 /* If PHP_AUTOCONF is set, use it; otherwise, use 'autoconf'. */ 425 if (getenv('PHP_AUTOCONF')) { 426 $autoconf = shell_exec(getenv('PHP_AUTOCONF') . ' --version'); 427 } else { 428 $autoconf = shell_exec('autoconf --version'); 429 } 430 431 /* Always use the generated libtool - Mac OSX uses 'glibtool' */ 432 $libtool = shell_exec(INIT_DIR . '/libtool --version'); 433 434 /* Use shtool to find out if there is glibtool present (MacOSX) */ 435 $sys_libtool_path = shell_exec(__DIR__ . '/build/shtool path glibtool libtool'); 436 437 if ($sys_libtool_path) { 438 $sys_libtool = shell_exec(str_replace("\n", "", $sys_libtool_path) . ' --version'); 439 } 440 441 /* Try the most common flags for 'version' */ 442 $flags = array('-v', '-V', '--version'); 443 $cc_status = 0; 444 445 foreach($flags AS $flag) { 446 system(getenv('CC') . " $flag >/dev/null 2>&1", $cc_status); 447 if ($cc_status == 0) { 448 $compiler = shell_exec(getenv('CC') . " $flag 2>&1"); 449 break; 450 } 451 } 452 453 $ldd = shell_exec("ldd $php 2>/dev/null"); 454 } 455 456 $failed_tests_data .= "Autoconf:\n$autoconf\n"; 457 $failed_tests_data .= "Bundled Libtool:\n$libtool\n"; 458 $failed_tests_data .= "System Libtool:\n$sys_libtool\n"; 459 $failed_tests_data .= "Compiler:\n$compiler\n"; 460 $failed_tests_data .= "Bison:\n". shell_exec('bison --version 2>/dev/null') . "\n"; 461 $failed_tests_data .= "Libraries:\n$ldd\n"; 462 $failed_tests_data .= "\n"; 463 464 if (isset($user_email)) { 465 $failed_tests_data .= "User's E-mail: " . $user_email . "\n\n"; 466 } 467 468 $failed_tests_data .= $sep . "PHPINFO" . $sep; 469 $failed_tests_data .= shell_exec($php . ' -ddisplay_errors=stderr -dhtml_errors=0 -i 2> /dev/null'); 470 471 if (($just_save_results || !mail_qa_team($failed_tests_data, $status)) && !TRAVIS_CI) { 472 file_put_contents($output_file, $failed_tests_data); 473 474 if (!$just_save_results) { 475 echo "\nThe test script was unable to automatically send the report to PHP's QA Team\n"; 476 } 477 478 echo "Please send " . $output_file . " to " . PHP_QA_EMAIL . " manually, thank you.\n"; 479 } elseif (!getenv('NO_INTERACTION') && !TRAVIS_CI) { 480 fwrite($fp, "\nThank you for helping to make PHP better.\n"); 481 fclose($fp); 482 } 483 } 484 } 485} 486 487// Determine the tests to be run. 488 489$test_files = array(); 490$redir_tests = array(); 491$test_results = array(); 492$PHP_FAILED_TESTS = array('BORKED' => array(), 'FAILED' => array(), 'WARNED' => array(), 'LEAKED' => array(), 'XFAILED' => array(), 'SLOW' => array()); 493 494// If parameters given assume they represent selected tests to run. 495$result_tests_file= false; 496$failed_tests_file= false; 497$pass_option_n = false; 498$pass_options = ''; 499 500$output_file = INIT_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt'; 501 502$just_save_results = false; 503$valgrind = null; 504$html_output = false; 505$html_file = null; 506$temp_source = null; 507$temp_target = null; 508$temp_urlbase = null; 509$conf_passed = null; 510$no_clean = false; 511$slow_min_ms = INF; 512 513$cfgtypes = array('show', 'keep'); 514$cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem'); 515$cfg = array(); 516 517foreach($cfgtypes as $type) { 518 $cfg[$type] = array(); 519 520 foreach($cfgfiles as $file) { 521 $cfg[$type][$file] = false; 522 } 523} 524 525if (getenv('TEST_PHP_ARGS')) { 526 527 if (!isset($argc, $argv) || !$argc) { 528 $argv = array(__FILE__); 529 } 530 531 $argv = array_merge($argv, explode(' ', getenv('TEST_PHP_ARGS'))); 532 $argc = count($argv); 533} 534 535if (isset($argc) && $argc > 1) { 536 537 for ($i=1; $i<$argc; $i++) { 538 $is_switch = false; 539 $switch = substr($argv[$i],1,1); 540 $repeat = substr($argv[$i],0,1) == '-'; 541 542 while ($repeat) { 543 544 if (!$is_switch) { 545 $switch = substr($argv[$i],1,1); 546 } 547 548 $is_switch = true; 549 550 if ($repeat) { 551 foreach($cfgtypes as $type) { 552 if (strpos($switch, '--' . $type) === 0) { 553 foreach($cfgfiles as $file) { 554 if ($switch == '--' . $type . '-' . $file) { 555 $cfg[$type][$file] = true; 556 $is_switch = false; 557 break; 558 } 559 } 560 } 561 } 562 } 563 564 if (!$is_switch) { 565 $is_switch = true; 566 break; 567 } 568 569 $repeat = false; 570 571 switch($switch) { 572 case 'r': 573 case 'l': 574 $test_list = file($argv[++$i]); 575 if ($test_list) { 576 foreach($test_list as $test) { 577 $matches = array(); 578 if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { 579 $redir_tests[] = array($matches[1], $matches[2]); 580 } else if (strlen($test)) { 581 $test_files[] = trim($test); 582 } 583 } 584 } 585 if ($switch != 'l') { 586 break; 587 } 588 $i--; 589 // break left intentionally 590 case 'w': 591 $failed_tests_file = fopen($argv[++$i], 'w+t'); 592 break; 593 case 'a': 594 $failed_tests_file = fopen($argv[++$i], 'a+t'); 595 break; 596 case 'W': 597 $result_tests_file = fopen($argv[++$i], 'w+t'); 598 break; 599 case 'c': 600 $conf_passed = $argv[++$i]; 601 break; 602 case 'd': 603 $ini_overwrites[] = $argv[++$i]; 604 break; 605 case 'g': 606 $SHOW_ONLY_GROUPS = explode(",", $argv[++$i]); 607 break; 608 //case 'h' 609 case '--keep-all': 610 foreach($cfgfiles as $file) { 611 $cfg['keep'][$file] = true; 612 } 613 break; 614 //case 'l' 615 case 'm': 616 $valgrind = new RuntestsValgrind($environment); 617 break; 618 case 'n': 619 if (!$pass_option_n) { 620 $pass_options .= ' -n'; 621 } 622 $pass_option_n = true; 623 break; 624 case 'e': 625 $pass_options .= ' -e'; 626 break; 627 case '--no-clean': 628 $no_clean = true; 629 break; 630 case 'p': 631 $php = $argv[++$i]; 632 putenv("TEST_PHP_EXECUTABLE=$php"); 633 $environment['TEST_PHP_EXECUTABLE'] = $php; 634 break; 635 case 'P': 636 if(constant('PHP_BINARY')) { 637 $php = PHP_BINARY; 638 } else { 639 break; 640 } 641 putenv("TEST_PHP_EXECUTABLE=$php"); 642 $environment['TEST_PHP_EXECUTABLE'] = $php; 643 break; 644 case 'q': 645 putenv('NO_INTERACTION=1'); 646 break; 647 //case 'r' 648 case 's': 649 $output_file = $argv[++$i]; 650 $just_save_results = true; 651 break; 652 case '--set-timeout': 653 $environment['TEST_TIMEOUT'] = $argv[++$i]; 654 break; 655 case '--show-all': 656 foreach($cfgfiles as $file) { 657 $cfg['show'][$file] = true; 658 } 659 break; 660 case '--show-slow': 661 $slow_min_ms = $argv[++$i]; 662 break; 663 case '--temp-source': 664 $temp_source = $argv[++$i]; 665 break; 666 case '--temp-target': 667 $temp_target = $argv[++$i]; 668 if ($temp_urlbase) { 669 $temp_urlbase = $temp_target; 670 } 671 break; 672 case '--temp-urlbase': 673 $temp_urlbase = $argv[++$i]; 674 break; 675 case 'v': 676 case '--verbose': 677 $DETAILED = true; 678 break; 679 case 'x': 680 $environment['SKIP_SLOW_TESTS'] = 1; 681 break; 682 case '--offline': 683 $environment['SKIP_ONLINE_TESTS'] = 1; 684 break; 685 //case 'w' 686 case '-': 687 // repeat check with full switch 688 $switch = $argv[$i]; 689 if ($switch != '-') { 690 $repeat = true; 691 } 692 break; 693 case '--html': 694 $html_file = fopen($argv[++$i], 'wt'); 695 $html_output = is_resource($html_file); 696 break; 697 case '--version': 698 echo '$Id: 6e551f4bb4be63fc488bb5568aa27da773720a97 $' . "\n"; 699 exit(1); 700 701 default: 702 echo "Illegal switch '$switch' specified!\n"; 703 case 'h': 704 case '-help': 705 case '--help': 706 echo <<<HELP 707Synopsis: 708 php run-tests.php [options] [files] [directories] 709 710Options: 711 -l <file> Read the testfiles to be executed from <file>. After the test 712 has finished all failed tests are written to the same <file>. 713 If the list is empty and no further test is specified then 714 all tests are executed (same as: -r <file> -w <file>). 715 716 -r <file> Read the testfiles to be executed from <file>. 717 718 -w <file> Write a list of all failed tests to <file>. 719 720 -a <file> Same as -w but append rather then truncating <file>. 721 722 -W <file> Write a list of all tests and their result status to <file>. 723 724 -c <file> Look for php.ini in directory <file> or use <file> as ini. 725 726 -n Pass -n option to the php binary (Do not use a php.ini). 727 728 -d foo=bar Pass -d option to the php binary (Define INI entry foo 729 with value 'bar'). 730 731 -g Comma separated list of groups to show during test run 732 (possible values: PASS, FAIL, XFAIL, SKIP, BORK, WARN, LEAK, REDIRECT). 733 734 -m Test for memory leaks with Valgrind. 735 736 -p <php> Specify PHP executable to run. 737 738 -P Use PHP_BINARY as PHP executable to run. 739 740 -q Quiet, no user interaction (same as environment NO_INTERACTION). 741 742 -s <file> Write output to <file>. 743 744 -x Sets 'SKIP_SLOW_TESTS' environmental variable. 745 746 --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. 747 748 --verbose 749 -v Verbose mode. 750 751 --help 752 -h This Help. 753 754 --html <file> Generate HTML output. 755 756 --temp-source <sdir> --temp-target <tdir> [--temp-urlbase <url>] 757 Write temporary files to <tdir> by replacing <sdir> from the 758 filenames to generate with <tdir>. If --html is being used and 759 <url> given then the generated links are relative and prefixed 760 with the given url. In general you want to make <sdir> the path 761 to your source files and <tdir> some pach in your web page 762 hierarchy with <url> pointing to <tdir>. 763 764 --keep-[all|php|skip|clean] 765 Do not delete 'all' files, 'php' test file, 'skip' or 'clean' 766 file. 767 768 --set-timeout [n] 769 Set timeout for individual tests, where [n] is the number of 770 seconds. The default value is 60 seconds, or 300 seconds when 771 testing for memory leaks. 772 773 --show-[all|php|skip|clean|exp|diff|out] 774 Show 'all' files, 'php' test file, 'skip' or 'clean' file. You 775 can also use this to show the output 'out', the expected result 776 'exp' or the difference between them 'diff'. The result types 777 get written independent of the log format, however 'diff' only 778 exists when a test fails. 779 780 --show-slow [n] 781 Show all tests that took longer than [n] milliseconds to run. 782 783 --no-clean Do not execute clean section if any. 784 785HELP; 786 exit(1); 787 } 788 } 789 790 if (!$is_switch) { 791 $testfile = realpath($argv[$i]); 792 793 if (!$testfile && strpos($argv[$i], '*') !== false && function_exists('glob')) { 794 795 if (substr($argv[$i], -5) == '.phpt') { 796 $pattern_match = glob($argv[$i]); 797 } else if (preg_match("/\*$/", $argv[$i])) { 798 $pattern_match = glob($argv[$i] . '.phpt'); 799 } else { 800 die('Cannot find test file "' . $argv[$i] . '".' . PHP_EOL); 801 } 802 803 if (is_array($pattern_match)) { 804 $test_files = array_merge($test_files, $pattern_match); 805 } 806 807 } else if (is_dir($testfile)) { 808 find_files($testfile); 809 } else if (substr($testfile, -5) == '.phpt') { 810 $test_files[] = $testfile; 811 } else { 812 die('Cannot find test file "' . $argv[$i] . '".' . PHP_EOL); 813 } 814 } 815 } 816 817 if (strlen($conf_passed)) { 818 if (substr(PHP_OS, 0, 3) == "WIN") { 819 $pass_options .= " -c " . escapeshellarg($conf_passed); 820 } else { 821 $pass_options .= " -c '" . realpath($conf_passed) . "'"; 822 } 823 } 824 825 $test_files = array_unique($test_files); 826 $test_files = array_merge($test_files, $redir_tests); 827 828 // Run selected tests. 829 $test_cnt = count($test_files); 830 831 if ($test_cnt) { 832 putenv('NO_INTERACTION=1'); 833 verify_config(); 834 write_information(); 835 usort($test_files, "test_sort"); 836 $start_time = time(); 837 838 if (!$html_output) { 839 echo "Running selected tests.\n"; 840 } else { 841 show_start($start_time); 842 } 843 844 $test_idx = 0; 845 run_all_tests($test_files, $environment); 846 $end_time = time(); 847 848 if ($html_output) { 849 show_end($end_time); 850 } 851 852 if ($failed_tests_file) { 853 fclose($failed_tests_file); 854 } 855 856 if ($result_tests_file) { 857 fclose($result_tests_file); 858 } 859 860 compute_summary(); 861 if ($html_output) { 862 fwrite($html_file, "<hr/>\n" . get_summary(false, true)); 863 } 864 echo "====================================================================="; 865 echo get_summary(false, false); 866 867 if ($html_output) { 868 fclose($html_file); 869 } 870 871 if ($output_file != '' && $just_save_results) { 872 save_or_mail_results(); 873 } 874 875 junit_save_xml(); 876 877 if (getenv('REPORT_EXIT_STATUS') !== '0' && 878 getenv('REPORT_EXIT_STATUS') !== 'no' && 879 ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) { 880 exit(1); 881 } 882 883 exit(0); 884 } 885} 886 887verify_config(); 888write_information(); 889 890// Compile a list of all test files (*.phpt). 891$test_files = array(); 892$exts_tested = count($exts_to_test); 893$exts_skipped = 0; 894$ignored_by_ext = 0; 895sort($exts_to_test); 896$test_dirs = array(); 897$optionals = array('tests', 'ext', 'Zend', 'sapi'); 898 899foreach($optionals as $dir) { 900 if (is_dir($dir)) { 901 $test_dirs[] = $dir; 902 } 903} 904 905// Convert extension names to lowercase 906foreach ($exts_to_test as $key => $val) { 907 $exts_to_test[$key] = strtolower($val); 908} 909 910foreach ($test_dirs as $dir) { 911 find_files(TEST_PHP_SRCDIR."/{$dir}", $dir == 'ext'); 912} 913 914foreach ($user_tests as $dir) { 915 find_files($dir, $dir == 'ext'); 916} 917 918function find_files($dir, $is_ext_dir = false, $ignore = false) 919{ 920 global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped; 921 922 $o = opendir($dir) or error("cannot open directory: $dir"); 923 924 while (($name = readdir($o)) !== false) { 925 926 if (is_dir("{$dir}/{$name}") && !in_array($name, array('.', '..', '.svn'))) { 927 $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test)); 928 if ($skip_ext) { 929 $exts_skipped++; 930 } 931 find_files("{$dir}/{$name}", false, $ignore || $skip_ext); 932 } 933 934 // Cleanup any left-over tmp files from last run. 935 if (substr($name, -4) == '.tmp') { 936 @unlink("$dir/$name"); 937 continue; 938 } 939 940 // Otherwise we're only interested in *.phpt files. 941 if (substr($name, -5) == '.phpt') { 942 if ($ignore) { 943 $ignored_by_ext++; 944 } else { 945 $testfile = realpath("{$dir}/{$name}"); 946 $test_files[] = $testfile; 947 } 948 } 949 } 950 951 closedir($o); 952} 953 954function test_name($name) 955{ 956 if (is_array($name)) { 957 return $name[0] . ':' . $name[1]; 958 } else { 959 return $name; 960 } 961} 962 963function test_sort($a, $b) 964{ 965 $a = test_name($a); 966 $b = test_name($b); 967 968 $ta = strpos($a, TEST_PHP_SRCDIR."/tests") === 0 ? 1 + (strpos($a, TEST_PHP_SRCDIR."/tests/run-test") === 0 ? 1 : 0) : 0; 969 $tb = strpos($b, TEST_PHP_SRCDIR."/tests") === 0 ? 1 + (strpos($b, TEST_PHP_SRCDIR."/tests/run-test") === 0 ? 1 : 0) : 0; 970 971 if ($ta == $tb) { 972 return strcmp($a, $b); 973 } else { 974 return $tb - $ta; 975 } 976} 977 978$test_files = array_unique($test_files); 979usort($test_files, "test_sort"); 980 981$start_time = time(); 982show_start($start_time); 983 984$test_cnt = count($test_files); 985$test_idx = 0; 986run_all_tests($test_files, $environment); 987$end_time = time(); 988 989if ($failed_tests_file) { 990 fclose($failed_tests_file); 991} 992 993if ($result_tests_file) { 994 fclose($result_tests_file); 995} 996 997// Summarize results 998 999if (0 == count($test_results)) { 1000 echo "No tests were run.\n"; 1001 return; 1002} 1003 1004compute_summary(); 1005 1006show_end($end_time); 1007show_summary(); 1008 1009if ($html_output) { 1010 fclose($html_file); 1011} 1012 1013save_or_mail_results(); 1014 1015junit_save_xml(); 1016if (getenv('REPORT_EXIT_STATUS') !== '0' && 1017 getenv('REPORT_EXIT_STATUS') !== 'no' && 1018 ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) { 1019 exit(1); 1020} 1021exit(0); 1022 1023// 1024// Send Email to QA Team 1025// 1026 1027function mail_qa_team($data, $status = false) 1028{ 1029 $url_bits = parse_url(QA_SUBMISSION_PAGE); 1030 1031 if ($proxy = getenv('http_proxy')) { 1032 $proxy = parse_url($proxy); 1033 $path = $url_bits['host'].$url_bits['path']; 1034 $host = $proxy['host']; 1035 if (empty($proxy['port'])) { 1036 $proxy['port'] = 80; 1037 } 1038 $port = $proxy['port']; 1039 } else { 1040 $path = $url_bits['path']; 1041 $host = $url_bits['host']; 1042 $port = empty($url_bits['port']) ? 80 : $port = $url_bits['port']; 1043 } 1044 1045 $data = "php_test_data=" . urlencode(base64_encode(str_replace("\00", '[0x0]', $data))); 1046 $data_length = strlen($data); 1047 1048 $fs = fsockopen($host, $port, $errno, $errstr, 10); 1049 1050 if (!$fs) { 1051 return false; 1052 } 1053 1054 $php_version = urlencode(TESTED_PHP_VERSION); 1055 1056 echo "\nPosting to ". QA_SUBMISSION_PAGE . "\n"; 1057 fwrite($fs, "POST " . $path . "?status=$status&version=$php_version HTTP/1.1\r\n"); 1058 fwrite($fs, "Host: " . $host . "\r\n"); 1059 fwrite($fs, "User-Agent: QA Browser 0.1\r\n"); 1060 fwrite($fs, "Content-Type: application/x-www-form-urlencoded\r\n"); 1061 fwrite($fs, "Content-Length: " . $data_length . "\r\n\r\n"); 1062 fwrite($fs, $data); 1063 fwrite($fs, "\r\n\r\n"); 1064 fclose($fs); 1065 1066 return 1; 1067} 1068 1069 1070// 1071// Write the given text to a temporary file, and return the filename. 1072// 1073 1074function save_text($filename, $text, $filename_copy = null) 1075{ 1076 global $DETAILED; 1077 1078 if ($filename_copy && $filename_copy != $filename) { 1079 if (file_put_contents($filename_copy, $text, FILE_BINARY) === false) { 1080 error("Cannot open file '" . $filename_copy . "' (save_text)"); 1081 } 1082 } 1083 1084 if (file_put_contents($filename, $text, FILE_BINARY) === false) { 1085 error("Cannot open file '" . $filename . "' (save_text)"); 1086 } 1087 1088 if (1 < $DETAILED) echo " 1089FILE $filename {{{ 1090$text 1091}}} 1092"; 1093} 1094 1095// 1096// Write an error in a format recognizable to Emacs or MSVC. 1097// 1098 1099function error_report($testname, $logname, $tested) 1100{ 1101 $testname = realpath($testname); 1102 $logname = realpath($logname); 1103 1104 switch (strtoupper(getenv('TEST_PHP_ERROR_STYLE'))) { 1105 case 'MSVC': 1106 echo $testname . "(1) : $tested\n"; 1107 echo $logname . "(1) : $tested\n"; 1108 break; 1109 case 'EMACS': 1110 echo $testname . ":1: $tested\n"; 1111 echo $logname . ":1: $tested\n"; 1112 break; 1113 } 1114} 1115 1116function system_with_timeout($commandline, $env = null, $stdin = null, $captureStdIn = true, $captureStdOut = true, $captureStdErr = true) 1117{ 1118 global $valgrind; 1119 1120 $data = ''; 1121 1122 $bin_env = array(); 1123 foreach((array)$env as $key => $value) { 1124 $bin_env[$key] = $value; 1125 } 1126 1127 $descriptorspec = array(); 1128 if ($captureStdIn) { 1129 $descriptorspec[0] = array('pipe', 'r'); 1130 } 1131 if ($captureStdOut) { 1132 $descriptorspec[1] = array('pipe', 'w'); 1133 } 1134 if ($captureStdErr) { 1135 $descriptorspec[2] = array('pipe', 'w'); 1136 } 1137 $proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, array('suppress_errors' => true)); 1138 1139 if (!$proc) { 1140 return false; 1141 } 1142 1143 if ($captureStdIn) { 1144 if (!is_null($stdin)) { 1145 fwrite($pipes[0], $stdin); 1146 } 1147 fclose($pipes[0]); 1148 unset($pipes[0]); 1149 } 1150 1151 $timeout = $valgrind ? 300 : ($env['TEST_TIMEOUT'] ?? 60); 1152 1153 while (true) { 1154 /* hide errors from interrupted syscalls */ 1155 $r = $pipes; 1156 $w = null; 1157 $e = null; 1158 1159 $n = @stream_select($r, $w, $e, $timeout); 1160 1161 if ($n === false) { 1162 break; 1163 } else if ($n === 0) { 1164 /* timed out */ 1165 $data .= "\n ** ERROR: process timed out **\n"; 1166 proc_terminate($proc, 9); 1167 return $data; 1168 } else if ($n > 0) { 1169 if ($captureStdOut) { 1170 $line = fread($pipes[1], 8192); 1171 } elseif ($captureStdErr) { 1172 $line = fread($pipes[2], 8192); 1173 } else { 1174 $line = ''; 1175 } 1176 if (strlen($line) == 0) { 1177 /* EOF */ 1178 break; 1179 } 1180 $data .= $line; 1181 } 1182 } 1183 1184 $stat = proc_get_status($proc); 1185 1186 if ($stat['signaled']) { 1187 $data .= "\nTermsig=" . $stat['stopsig'] . "\n"; 1188 } 1189 if ($stat["exitcode"] > 128 && $stat["exitcode"] < 160) { 1190 $data .= "\nTermsig=" . ($stat["exitcode"] - 128) . "\n"; 1191 } 1192 1193 proc_close($proc); 1194 return $data; 1195} 1196 1197function run_all_tests($test_files, $env, $redir_tested = null) 1198{ 1199 global $test_results, $failed_tests_file, $result_tests_file, $php, $test_idx; 1200 1201 foreach($test_files as $name) { 1202 1203 if (is_array($name)) { 1204 $index = "# $name[1]: $name[0]"; 1205 1206 if ($redir_tested) { 1207 $name = $name[0]; 1208 } 1209 } else if ($redir_tested) { 1210 $index = "# $redir_tested: $name"; 1211 } else { 1212 $index = $name; 1213 } 1214 $test_idx++; 1215 $result = run_test($php, $name, $env); 1216 1217 if (!is_array($name) && $result != 'REDIR') { 1218 $test_results[$index] = $result; 1219 if ($failed_tests_file && ($result == 'XFAILED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) { 1220 fwrite($failed_tests_file, "$index\n"); 1221 } 1222 if ($result_tests_file) { 1223 fwrite($result_tests_file, "$result\t$index\n"); 1224 } 1225 } 1226 } 1227} 1228 1229// 1230// Show file or result block 1231// 1232function show_file_block($file, $block, $section = null) 1233{ 1234 global $cfg; 1235 1236 if ($cfg['show'][$file]) { 1237 1238 if (is_null($section)) { 1239 $section = strtoupper($file); 1240 } 1241 1242 echo "\n========" . $section . "========\n"; 1243 echo rtrim($block); 1244 echo "\n========DONE========\n"; 1245 } 1246} 1247 1248// 1249// Run an individual test case. 1250// 1251function run_test($php, $file, $env) 1252{ 1253 global $log_format, $ini_overwrites, $PHP_FAILED_TESTS; 1254 global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx; 1255 global $valgrind, $temp_source, $temp_target, $cfg, $environment; 1256 global $no_clean; 1257 global $SHOW_ONLY_GROUPS; 1258 global $no_file_cache; 1259 global $slow_min_ms; 1260 $temp_filenames = null; 1261 $org_file = $file; 1262 1263 if (isset($env['TEST_PHP_CGI_EXECUTABLE'])) { 1264 $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE']; 1265 } 1266 1267 if (isset($env['TEST_PHPDBG_EXECUTABLE'])) { 1268 $phpdbg = $env['TEST_PHPDBG_EXECUTABLE']; 1269 } 1270 1271 if (is_array($file)) { 1272 $file = $file[0]; 1273 } 1274 1275 if ($DETAILED) echo " 1276================= 1277TEST $file 1278"; 1279 1280 // Load the sections of the test file. 1281 $section_text = array('TEST' => ''); 1282 1283 $fp = fopen($file, "rb") or error("Cannot open test file: $file"); 1284 1285 $bork_info = null; 1286 1287 if (!feof($fp)) { 1288 $line = fgets($fp); 1289 1290 if ($line === false) { 1291 $bork_info = "cannot read test"; 1292 } 1293 } else { 1294 $bork_info = "empty test [$file]"; 1295 } 1296 if ($bork_info === null && strncmp('--TEST--', $line, 8)) { 1297 $bork_info = "tests must start with --TEST-- [$file]"; 1298 } 1299 1300 $section = 'TEST'; 1301 $secfile = false; 1302 $secdone = false; 1303 1304 while (!feof($fp)) { 1305 $line = fgets($fp); 1306 1307 if ($line === false) { 1308 break; 1309 } 1310 1311 // Match the beginning of a section. 1312 if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { 1313 $section = (string) $r[1]; 1314 1315 if (isset($section_text[$section]) && $section_text[$section]) { 1316 $bork_info = "duplicated $section section"; 1317 } 1318 1319 // check for unknown sections 1320 if (!in_array($section, array( 1321 'EXPECT', 'EXPECTF', 'EXPECTREGEX', 'EXPECTREGEX_EXTERNAL', 'EXPECT_EXTERNAL', 'EXPECTF_EXTERNAL', 'EXPECTHEADERS', 1322 'POST', 'POST_RAW', 'GZIP_POST', 'DEFLATE_POST', 'PUT', 'GET', 'COOKIE', 'ARGS', 'REQUEST', 'HEADERS', 1323 'FILE', 'FILEEOF', 'FILE_EXTERNAL', 'REDIRECTTEST', 1324 'CAPTURE_STDIO', 'STDIN', 'CGI', 'PHPDBG', 1325 'INI', 'ENV', 'EXTENSIONS', 1326 'SKIPIF', 'XFAIL', 'CLEAN', 1327 'CREDITS', 'DESCRIPTION', 'CONFLICTS', 1328 ))) { 1329 $bork_info = 'Unknown section "' . $section . '"'; 1330 } 1331 1332 $section_text[$section] = ''; 1333 $secfile = $section == 'FILE' || $section == 'FILEEOF' || $section == 'FILE_EXTERNAL'; 1334 $secdone = false; 1335 continue; 1336 } 1337 1338 // Add to the section text. 1339 if (!$secdone) { 1340 $section_text[$section] .= $line; 1341 } 1342 1343 // End of actual test? 1344 if ($secfile && preg_match('/^===DONE===\s*$/', $line)) { 1345 $secdone = true; 1346 } 1347 } 1348 1349 // the redirect section allows a set of tests to be reused outside of 1350 // a given test dir 1351 if ($bork_info === null) { 1352 if (isset($section_text['REDIRECTTEST'])) { 1353 1354 if ($IN_REDIRECT) { 1355 $bork_info = "Can't redirect a test from within a redirected test"; 1356 } 1357 1358 } else { 1359 1360 if (!isset($section_text['PHPDBG']) && @count($section_text['FILE']) + @count($section_text['FILEEOF']) + @count($section_text['FILE_EXTERNAL']) != 1) { 1361 $bork_info = "missing section --FILE--"; 1362 } 1363 1364 if (isset($section_text['FILEEOF'])) { 1365 $section_text['FILE'] = preg_replace("/[\r\n]+$/", '', $section_text['FILEEOF']); 1366 unset($section_text['FILEEOF']); 1367 } 1368 1369 foreach (array( 'FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX' ) as $prefix) { 1370 $key = $prefix . '_EXTERNAL'; 1371 1372 if (isset($section_text[$key])) { 1373 // don't allow tests to retrieve files from anywhere but this subdirectory 1374 $section_text[$key] = dirname($file) . '/' . trim(str_replace('..', '', $section_text[$key])); 1375 1376 if (file_exists($section_text[$key])) { 1377 $section_text[$prefix] = file_get_contents($section_text[$key], FILE_BINARY); 1378 unset($section_text[$key]); 1379 } else { 1380 $bork_info = "could not load --" . $key . "-- " . dirname($file) . '/' . trim($section_text[$key]); 1381 } 1382 } 1383 } 1384 1385 if ((@count($section_text['EXPECT']) + @count($section_text['EXPECTF']) + @count($section_text['EXPECTREGEX'])) != 1) { 1386 $bork_info = "missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"; 1387 } 1388 } 1389 } 1390 fclose($fp); 1391 1392 $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file); 1393 $tested_file = $shortname; 1394 1395 if ($bork_info !== null) { 1396 show_result("BORK", $bork_info, $tested_file); 1397 $PHP_FAILED_TESTS['BORKED'][] = array ( 1398 'name' => $file, 1399 'test_name' => '', 1400 'output' => '', 1401 'diff' => '', 1402 'info' => "$bork_info [$file]", 1403 ); 1404 1405 junit_mark_test_as('BORK', $shortname, $tested_file, 0, $bork_info); 1406 return 'BORKED'; 1407 } 1408 1409 if (isset($section_text['CAPTURE_STDIO'])) { 1410 $captureStdIn = stripos($section_text['CAPTURE_STDIO'], 'STDIN') !== false; 1411 $captureStdOut = stripos($section_text['CAPTURE_STDIO'], 'STDOUT') !== false; 1412 $captureStdErr = stripos($section_text['CAPTURE_STDIO'], 'STDERR') !== false; 1413 } else { 1414 $captureStdIn = true; 1415 $captureStdOut = true; 1416 $captureStdErr = true; 1417 } 1418 if ($captureStdOut && $captureStdErr) { 1419 $cmdRedirect = ' 2>&1'; 1420 } else { 1421 $cmdRedirect = ''; 1422 } 1423 1424 $tested = trim($section_text['TEST']); 1425 1426 /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */ 1427 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'])) { 1428 if (isset($php_cgi)) { 1429 $php = $php_cgi . ' -C '; 1430 } else if (!strncasecmp(PHP_OS, "win", 3) && file_exists(dirname($php) . "/php-cgi.exe")) { 1431 $php = realpath(dirname($php) . "/php-cgi.exe") . ' -C '; 1432 } else { 1433 if (file_exists(dirname($php) . "/../../sapi/cgi/php-cgi")) { 1434 $php = realpath(dirname($php) . "/../../sapi/cgi/php-cgi") . ' -C '; 1435 } else if (file_exists("./sapi/cgi/php-cgi")) { 1436 $php = realpath("./sapi/cgi/php-cgi") . ' -C '; 1437 } else if (file_exists(dirname($php) . "/php-cgi")) { 1438 $php = realpath(dirname($php) . "/php-cgi") . ' -C '; 1439 } else { 1440 show_result('SKIP', $tested, $tested_file, "reason: CGI not available"); 1441 1442 junit_init_suite(junit_get_suitename_for($shortname)); 1443 junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available'); 1444 return 'SKIPPED'; 1445 } 1446 } 1447 $uses_cgi = true; 1448 } 1449 1450 /* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */ 1451 if (array_key_exists('PHPDBG', $section_text)) { 1452 if (!isset($section_text['STDIN'])) { 1453 $section_text['STDIN'] = $section_text['PHPDBG']."\n"; 1454 } 1455 1456 if (isset($phpdbg)) { 1457 $php = $phpdbg . ' -qIb'; 1458 } else { 1459 show_result('SKIP', $tested, $tested_file, "reason: phpdbg not available"); 1460 1461 junit_init_suite(junit_get_suitename_for($shortname)); 1462 junit_mark_test_as('SKIP', $shortname, $tested, 0, 'phpdbg not available'); 1463 return 'SKIPPED'; 1464 } 1465 } 1466 1467 if (!$SHOW_ONLY_GROUPS) { 1468 show_test($test_idx, $shortname); 1469 } 1470 1471 if (is_array($IN_REDIRECT)) { 1472 $temp_dir = $test_dir = $IN_REDIRECT['dir']; 1473 } else { 1474 $temp_dir = $test_dir = realpath(dirname($file)); 1475 } 1476 1477 if ($temp_source && $temp_target) { 1478 $temp_dir = str_replace($temp_source, $temp_target, $temp_dir); 1479 } 1480 1481 $main_file_name = basename($file,'phpt'); 1482 1483 $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff'; 1484 $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log'; 1485 $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp'; 1486 $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out'; 1487 $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem'; 1488 $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh'; 1489 $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; 1490 $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; 1491 $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; 1492 $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; 1493 $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; 1494 $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; 1495 $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('/phpt.'); 1496 $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't'; 1497 1498 if ($temp_source && $temp_target) { 1499 $temp_skipif .= 's'; 1500 $temp_file .= 's'; 1501 $temp_clean .= 's'; 1502 $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps'; 1503 1504 if (!is_dir(dirname($copy_file))) { 1505 mkdir(dirname($copy_file), 0777, true) or error("Cannot create output directory - " . dirname($copy_file)); 1506 } 1507 1508 if (isset($section_text['FILE'])) { 1509 save_text($copy_file, $section_text['FILE']); 1510 } 1511 1512 $temp_filenames = array( 1513 'file' => $copy_file, 1514 'diff' => $diff_filename, 1515 'log' => $log_filename, 1516 'exp' => $exp_filename, 1517 'out' => $output_filename, 1518 'mem' => $memcheck_filename, 1519 'sh' => $sh_filename, 1520 'php' => $temp_file, 1521 'skip' => $temp_skipif, 1522 'clean'=> $temp_clean); 1523 } 1524 1525 if (is_array($IN_REDIRECT)) { 1526 $tested = $IN_REDIRECT['prefix'] . ' ' . trim($section_text['TEST']); 1527 $tested_file = $tmp_relative_file; 1528 } 1529 1530 // unlink old test results 1531 @unlink($diff_filename); 1532 @unlink($log_filename); 1533 @unlink($exp_filename); 1534 @unlink($output_filename); 1535 @unlink($memcheck_filename); 1536 @unlink($sh_filename); 1537 @unlink($temp_file); 1538 @unlink($test_file); 1539 @unlink($temp_skipif); 1540 @unlink($test_skipif); 1541 @unlink($tmp_post); 1542 @unlink($temp_clean); 1543 @unlink($test_clean); 1544 1545 // Reset environment from any previous test. 1546 $env['REDIRECT_STATUS'] = ''; 1547 $env['QUERY_STRING'] = ''; 1548 $env['PATH_TRANSLATED'] = ''; 1549 $env['SCRIPT_FILENAME'] = ''; 1550 $env['REQUEST_METHOD'] = ''; 1551 $env['CONTENT_TYPE'] = ''; 1552 $env['CONTENT_LENGTH'] = ''; 1553 $env['TZ'] = ''; 1554 1555 if (!empty($section_text['ENV'])) { 1556 1557 foreach(explode("\n", trim($section_text['ENV'])) as $e) { 1558 $e = explode('=', trim($e), 2); 1559 1560 if (!empty($e[0]) && isset($e[1])) { 1561 $env[$e[0]] = $e[1]; 1562 } 1563 } 1564 } 1565 1566 // Default ini settings 1567 $ini_settings = array(); 1568 1569 // Additional required extensions 1570 if (array_key_exists('EXTENSIONS', $section_text)) { 1571 $ext_params = array(); 1572 settings2array($ini_overwrites, $ext_params); 1573 settings2params($ext_params); 1574 $ext_dir=`$php $pass_options $ext_params -d display_errors=0 -r "echo ini_get('extension_dir');"`; 1575 $extensions = preg_split("/[\n\r]+/", trim($section_text['EXTENSIONS'])); 1576 $loaded = explode(",", `$php $pass_options $ext_params -d display_errors=0 -r "echo implode(',', get_loaded_extensions());"`); 1577 $ext_prefix = substr(PHP_OS, 0, 3) === "WIN" ? "php_" : ""; 1578 foreach ($extensions as $req_ext) { 1579 if (!in_array($req_ext, $loaded)) { 1580 if ($req_ext == 'opcache') { 1581 $ini_settings['zend_extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $ext_prefix . $req_ext . '.' . PHP_SHLIB_SUFFIX; 1582 } else { 1583 $ini_settings['extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $ext_prefix . $req_ext . '.' . PHP_SHLIB_SUFFIX; 1584 } 1585 } 1586 } 1587 } 1588 1589 // additional ini overwrites 1590 //$ini_overwrites[] = 'setting=value'; 1591 settings2array($ini_overwrites, $ini_settings); 1592 1593 // Any special ini settings 1594 // these may overwrite the test defaults... 1595 if (array_key_exists('INI', $section_text)) { 1596 $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); 1597 $section_text['INI'] = str_replace('{TMP}', sys_get_temp_dir(), $section_text['INI']); 1598 settings2array(preg_split( "/[\n\r]+/", $section_text['INI']), $ini_settings); 1599 } 1600 1601 settings2params($ini_settings); 1602 1603 $env['TEST_PHP_EXTRA_ARGS'] = $pass_options . ' ' . $ini_settings; 1604 1605 // Check if test should be skipped. 1606 $info = ''; 1607 $warn = false; 1608 1609 if (array_key_exists('SKIPIF', $section_text)) { 1610 1611 if (trim($section_text['SKIPIF'])) { 1612 show_file_block('skip', $section_text['SKIPIF']); 1613 save_text($test_skipif, $section_text['SKIPIF'], $temp_skipif); 1614 $extra = substr(PHP_OS, 0, 3) !== "WIN" ? 1615 "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; 1616 1617 if ($valgrind) { 1618 $env['USE_ZEND_ALLOC'] = '0'; 1619 $env['ZEND_DONT_UNLOAD_MODULES'] = 1; 1620 } else { 1621 $env['USE_ZEND_ALLOC'] = '1'; 1622 $env['ZEND_DONT_UNLOAD_MODULES'] = 0; 1623 } 1624 1625 junit_start_timer($shortname); 1626 1627 $output = system_with_timeout("$extra $php $pass_options -q $ini_settings $no_file_cache -d display_errors=0 \"$test_skipif\"", $env); 1628 1629 junit_finish_timer($shortname); 1630 1631 if (!$cfg['keep']['skip']) { 1632 @unlink($test_skipif); 1633 } 1634 1635 if (!strncasecmp('skip', ltrim($output), 4)) { 1636 1637 if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { 1638 show_result('SKIP', $tested, $tested_file, "reason: $m[1]", $temp_filenames); 1639 } else { 1640 show_result('SKIP', $tested, $tested_file, '', $temp_filenames); 1641 } 1642 1643 if (!$cfg['keep']['skip']) { 1644 @unlink($test_skipif); 1645 } 1646 1647 $message = !empty($m[1]) ? $m[1] : ''; 1648 junit_mark_test_as('SKIP', $shortname, $tested, null, $message); 1649 return 'SKIPPED'; 1650 } 1651 1652 if (!strncasecmp('info', ltrim($output), 4)) { 1653 if (preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { 1654 $info = " (info: $m[1])"; 1655 } 1656 } 1657 1658 if (!strncasecmp('warn', ltrim($output), 4)) { 1659 if (preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { 1660 $warn = true; /* only if there is a reason */ 1661 $info = " (warn: $m[1])"; 1662 } 1663 } 1664 1665 if (!strncasecmp('xfail', ltrim($output), 5)) { 1666 // Pretend we have an XFAIL section 1667 $section_text['XFAIL'] = trim(substr(ltrim($output), 5)); 1668 } 1669 } 1670 } 1671 1672 if (!extension_loaded("zlib") 1673 && ( array_key_exists("GZIP_POST", $section_text) 1674 || array_key_exists("DEFLATE_POST", $section_text)) 1675 ) { 1676 $message = "ext/zlib required"; 1677 show_result('SKIP', $tested, $tested_file, "reason: $message", $temp_filenames); 1678 junit_mark_test_as('SKIP', $shortname, $tested, null, $message); 1679 return 'SKIPPED'; 1680 } 1681 1682 if (isset($section_text['REDIRECTTEST'])) { 1683 $test_files = array(); 1684 1685 $IN_REDIRECT = eval($section_text['REDIRECTTEST']); 1686 $IN_REDIRECT['via'] = "via [$shortname]\n\t"; 1687 $IN_REDIRECT['dir'] = realpath(dirname($file)); 1688 $IN_REDIRECT['prefix'] = trim($section_text['TEST']); 1689 1690 if (!empty($IN_REDIRECT['TESTS'])) { 1691 1692 if (is_array($org_file)) { 1693 $test_files[] = $org_file[1]; 1694 } else { 1695 $GLOBALS['test_files'] = $test_files; 1696 find_files($IN_REDIRECT['TESTS']); 1697 1698 foreach($GLOBALS['test_files'] as $f) { 1699 $test_files[] = array($f, $file); 1700 } 1701 } 1702 $test_cnt += @count($test_files) - 1; 1703 $test_idx--; 1704 1705 show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file); 1706 1707 // set up environment 1708 $redirenv = array_merge($environment, $IN_REDIRECT['ENV']); 1709 $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR; 1710 1711 usort($test_files, "test_sort"); 1712 run_all_tests($test_files, $redirenv, $tested); 1713 1714 show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file); 1715 1716 // a redirected test never fails 1717 $IN_REDIRECT = false; 1718 1719 junit_mark_test_as('PASS', $shortname, $tested); 1720 return 'REDIR'; 1721 1722 } else { 1723 1724 $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; 1725 show_result("BORK", $bork_info, '', $temp_filenames); 1726 $PHP_FAILED_TESTS['BORKED'][] = array ( 1727 'name' => $file, 1728 'test_name' => '', 1729 'output' => '', 1730 'diff' => '', 1731 'info' => "$bork_info [$file]", 1732 ); 1733 } 1734 } 1735 1736 if (is_array($org_file) || @count($section_text['REDIRECTTEST']) == 1) { 1737 1738 if (is_array($org_file)) { 1739 $file = $org_file[0]; 1740 } 1741 1742 $bork_info = "Redirected test did not contain redirection info"; 1743 show_result("BORK", $bork_info, '', $temp_filenames); 1744 $PHP_FAILED_TESTS['BORKED'][] = array ( 1745 'name' => $file, 1746 'test_name' => '', 1747 'output' => '', 1748 'diff' => '', 1749 'info' => "$bork_info [$file]", 1750 ); 1751 1752 junit_mark_test_as('BORK', $shortname, $tested, null, $bork_info); 1753 1754 return 'BORKED'; 1755 } 1756 1757 // We've satisfied the preconditions - run the test! 1758 if (isset($section_text['FILE'])) { 1759 show_file_block('php', $section_text['FILE'], 'TEST'); 1760 save_text($test_file, $section_text['FILE'], $temp_file); 1761 } else { 1762 $test_file = $temp_file = ""; 1763 } 1764 1765 if (array_key_exists('GET', $section_text)) { 1766 $query_string = trim($section_text['GET']); 1767 } else { 1768 $query_string = ''; 1769 } 1770 1771 $env['REDIRECT_STATUS'] = '1'; 1772 if (empty($env['QUERY_STRING'])) { 1773 $env['QUERY_STRING'] = $query_string; 1774 } 1775 if (empty($env['PATH_TRANSLATED'])) { 1776 $env['PATH_TRANSLATED'] = $test_file; 1777 } 1778 if (empty($env['SCRIPT_FILENAME'])) { 1779 $env['SCRIPT_FILENAME'] = $test_file; 1780 } 1781 1782 if (array_key_exists('COOKIE', $section_text)) { 1783 $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); 1784 } else { 1785 $env['HTTP_COOKIE'] = ''; 1786 } 1787 1788 $args = isset($section_text['ARGS']) ? ' -- ' . $section_text['ARGS'] : ''; 1789 1790 if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { 1791 1792 $post = trim($section_text['POST_RAW']); 1793 $raw_lines = explode("\n", $post); 1794 1795 $request = ''; 1796 $started = false; 1797 1798 foreach ($raw_lines as $line) { 1799 1800 if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { 1801 $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); 1802 continue; 1803 } 1804 1805 if ($started) { 1806 $request .= "\n"; 1807 } 1808 1809 $started = true; 1810 $request .= $line; 1811 } 1812 1813 $env['CONTENT_LENGTH'] = strlen($request); 1814 $env['REQUEST_METHOD'] = 'POST'; 1815 1816 if (empty($request)) { 1817 junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); 1818 return 'BORKED'; 1819 } 1820 1821 save_text($tmp_post, $request); 1822 $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; 1823 1824 } elseif (array_key_exists('PUT', $section_text) && !empty($section_text['PUT'])) { 1825 1826 $post = trim($section_text['PUT']); 1827 $raw_lines = explode("\n", $post); 1828 1829 $request = ''; 1830 $started = false; 1831 1832 foreach ($raw_lines as $line) { 1833 1834 if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { 1835 $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); 1836 continue; 1837 } 1838 1839 if ($started) { 1840 $request .= "\n"; 1841 } 1842 1843 $started = true; 1844 $request .= $line; 1845 } 1846 1847 $env['CONTENT_LENGTH'] = strlen($request); 1848 $env['REQUEST_METHOD'] = 'PUT'; 1849 1850 if (empty($request)) { 1851 junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); 1852 return 'BORKED'; 1853 } 1854 1855 save_text($tmp_post, $request); 1856 $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; 1857 1858 } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { 1859 1860 $post = trim($section_text['POST']); 1861 $content_length = strlen($post); 1862 save_text($tmp_post, $post); 1863 1864 $env['REQUEST_METHOD'] = 'POST'; 1865 if (empty($env['CONTENT_TYPE'])) { 1866 $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1867 } 1868 1869 if (empty($env['CONTENT_LENGTH'])) { 1870 $env['CONTENT_LENGTH'] = $content_length; 1871 } 1872 1873 $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; 1874 1875 } else if (array_key_exists('GZIP_POST', $section_text) && !empty($section_text['GZIP_POST'])) { 1876 1877 $post = trim($section_text['GZIP_POST']); 1878 $post = gzencode($post, 9, FORCE_GZIP); 1879 $env['HTTP_CONTENT_ENCODING'] = 'gzip'; 1880 1881 save_text($tmp_post, $post); 1882 $content_length = strlen($post); 1883 1884 $env['REQUEST_METHOD'] = 'POST'; 1885 $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1886 $env['CONTENT_LENGTH'] = $content_length; 1887 1888 $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; 1889 1890 } else if (array_key_exists('DEFLATE_POST', $section_text) && !empty($section_text['DEFLATE_POST'])) { 1891 $post = trim($section_text['DEFLATE_POST']); 1892 $post = gzcompress($post, 9); 1893 $env['HTTP_CONTENT_ENCODING'] = 'deflate'; 1894 save_text($tmp_post, $post); 1895 $content_length = strlen($post); 1896 1897 $env['REQUEST_METHOD'] = 'POST'; 1898 $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1899 $env['CONTENT_LENGTH'] = $content_length; 1900 1901 $cmd = "$php $pass_options $ini_settings -f \"$test_file\"$cmdRedirect < \"$tmp_post\""; 1902 1903 } else { 1904 1905 $env['REQUEST_METHOD'] = 'GET'; 1906 $env['CONTENT_TYPE'] = ''; 1907 $env['CONTENT_LENGTH'] = ''; 1908 1909 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args$cmdRedirect"; 1910 } 1911 1912 if ($valgrind) { 1913 $env['USE_ZEND_ALLOC'] = '0'; 1914 $env['ZEND_DONT_UNLOAD_MODULES'] = 1; 1915 1916 $cmd = $valgrind->wrapCommand($cmd, $memcheck_filename, strpos($test_file, "pcre") !== false); 1917 } else { 1918 $env['USE_ZEND_ALLOC'] = '1'; 1919 $env['ZEND_DONT_UNLOAD_MODULES'] = 0; 1920 } 1921 1922 if ($DETAILED) echo " 1923CONTENT_LENGTH = " . $env['CONTENT_LENGTH'] . " 1924CONTENT_TYPE = " . $env['CONTENT_TYPE'] . " 1925PATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . " 1926QUERY_STRING = " . $env['QUERY_STRING'] . " 1927REDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . " 1928REQUEST_METHOD = " . $env['REQUEST_METHOD'] . " 1929SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . " 1930HTTP_COOKIE = " . $env['HTTP_COOKIE'] . " 1931COMMAND $cmd 1932"; 1933 1934 junit_start_timer($shortname); 1935 $hrtime = hrtime(); 1936 $startTime = $hrtime[0]*1000000000 + $hrtime[1]; 1937 1938 $out = system_with_timeout($cmd, $env, $section_text['STDIN'] ?? null, $captureStdIn, $captureStdOut, $captureStdErr); 1939 1940 junit_finish_timer($shortname); 1941 $hrtime = hrtime(); 1942 $time = $hrtime[0]*1000000000 + $hrtime[1] - $startTime; 1943 if ($time >= $slow_min_ms * 1000000) { 1944 $PHP_FAILED_TESTS['SLOW'][] = array( 1945 'name' => $file, 1946 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 1947 'output' => '', 1948 'diff' => '', 1949 'info' => $time / 1000000000, 1950 ); 1951 } 1952 1953 if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) { 1954 1955 if (trim($section_text['CLEAN'])) { 1956 show_file_block('clean', $section_text['CLEAN']); 1957 save_text($test_clean, trim($section_text['CLEAN']), $temp_clean); 1958 1959 if (!$no_clean) { 1960 $clean_params = array(); 1961 settings2array($ini_overwrites, $clean_params); 1962 settings2params($clean_params); 1963 $extra = substr(PHP_OS, 0, 3) !== "WIN" ? 1964 "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; 1965 system_with_timeout("$extra $php $pass_options -q $clean_params $no_file_cache \"$test_clean\"", $env); 1966 } 1967 1968 if (!$cfg['keep']['clean']) { 1969 @unlink($test_clean); 1970 } 1971 } 1972 } 1973 1974 $leaked = false; 1975 $passed = false; 1976 1977 if ($valgrind) { // leak check 1978 $leaked = filesize($memcheck_filename) > 0; 1979 1980 if (!$leaked) { 1981 @unlink($memcheck_filename); 1982 } 1983 } 1984 1985 // Does the output match what is expected? 1986 $output = preg_replace("/\r\n/", "\n", trim($out)); 1987 1988 /* when using CGI, strip the headers from the output */ 1989 $headers = array(); 1990 1991 if (!empty($uses_cgi) && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { 1992 $output = trim($match[2]); 1993 $rh = preg_split("/[\n\r]+/", $match[1]); 1994 1995 foreach ($rh as $line) { 1996 if (strpos($line, ':') !== false) { 1997 $line = explode(':', $line, 2); 1998 $headers[trim($line[0])] = trim($line[1]); 1999 } 2000 } 2001 } 2002 2003 $failed_headers = false; 2004 2005 if (isset($section_text['EXPECTHEADERS'])) { 2006 $want = array(); 2007 $wanted_headers = array(); 2008 $lines = preg_split("/[\n\r]+/", $section_text['EXPECTHEADERS']); 2009 2010 foreach($lines as $line) { 2011 if (strpos($line, ':') !== false) { 2012 $line = explode(':', $line, 2); 2013 $want[trim($line[0])] = trim($line[1]); 2014 $wanted_headers[] = trim($line[0]) . ': ' . trim($line[1]); 2015 } 2016 } 2017 2018 $output_headers = array(); 2019 2020 foreach($want as $k => $v) { 2021 2022 if (isset($headers[$k])) { 2023 $output_headers[] = $k . ': ' . $headers[$k]; 2024 } 2025 2026 if (!isset($headers[$k]) || $headers[$k] != $v) { 2027 $failed_headers = true; 2028 } 2029 } 2030 2031 ksort($wanted_headers); 2032 $wanted_headers = implode("\n", $wanted_headers); 2033 ksort($output_headers); 2034 $output_headers = implode("\n", $output_headers); 2035 } 2036 2037 show_file_block('out', $output); 2038 2039 if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { 2040 2041 if (isset($section_text['EXPECTF'])) { 2042 $wanted = trim($section_text['EXPECTF']); 2043 } else { 2044 $wanted = trim($section_text['EXPECTREGEX']); 2045 } 2046 2047 show_file_block('exp', $wanted); 2048 $wanted_re = preg_replace('/\r\n/', "\n", $wanted); 2049 2050 if (isset($section_text['EXPECTF'])) { 2051 2052 // do preg_quote, but miss out any %r delimited sections 2053 $temp = ""; 2054 $r = "%r"; 2055 $startOffset = 0; 2056 $length = strlen($wanted_re); 2057 while($startOffset < $length) { 2058 $start = strpos($wanted_re, $r, $startOffset); 2059 if ($start !== false) { 2060 // we have found a start tag 2061 $end = strpos($wanted_re, $r, $start+2); 2062 if ($end === false) { 2063 // unbalanced tag, ignore it. 2064 $end = $start = $length; 2065 } 2066 } else { 2067 // no more %r sections 2068 $start = $end = $length; 2069 } 2070 // quote a non re portion of the string 2071 $temp .= preg_quote(substr($wanted_re, $startOffset, $start - $startOffset), '/'); 2072 // add the re unquoted. 2073 if ($end > $start) { 2074 $temp .= '(' . substr($wanted_re, $start+2, $end - $start-2). ')'; 2075 } 2076 $startOffset = $end + 2; 2077 } 2078 $wanted_re = $temp; 2079 2080 // Stick to basics 2081 $wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re); 2082 $wanted_re = str_replace('%s', '[^\r\n]+', $wanted_re); 2083 $wanted_re = str_replace('%S', '[^\r\n]*', $wanted_re); 2084 $wanted_re = str_replace('%a', '.+', $wanted_re); 2085 $wanted_re = str_replace('%A', '.*', $wanted_re); 2086 $wanted_re = str_replace('%w', '\s*', $wanted_re); 2087 $wanted_re = str_replace('%i', '[+-]?\d+', $wanted_re); 2088 $wanted_re = str_replace('%d', '\d+', $wanted_re); 2089 $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re); 2090 $wanted_re = str_replace('%f', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re); 2091 $wanted_re = str_replace('%c', '.', $wanted_re); 2092 // %f allows two points "-.0.0" but that is the best *simple* expression 2093 } 2094/* DEBUG YOUR REGEX HERE 2095 var_dump($wanted_re); 2096 print(str_repeat('=', 80) . "\n"); 2097 var_dump($output); 2098*/ 2099 if (preg_match("/^$wanted_re\$/s", $output)) { 2100 $passed = true; 2101 if (!$cfg['keep']['php']) { 2102 @unlink($test_file); 2103 } 2104 2105 if (!$leaked && !$failed_headers) { 2106 if (isset($section_text['XFAIL'] )) { 2107 $warn = true; 2108 $info = " (warn: XFAIL section but test passes)"; 2109 }else { 2110 show_result("PASS", $tested, $tested_file, '', $temp_filenames); 2111 junit_mark_test_as('PASS', $shortname, $tested); 2112 return 'PASSED'; 2113 } 2114 } 2115 } 2116 2117 } else { 2118 2119 $wanted = trim($section_text['EXPECT']); 2120 $wanted = preg_replace('/\r\n/',"\n", $wanted); 2121 show_file_block('exp', $wanted); 2122 2123 // compare and leave on success 2124 if (!strcmp($output, $wanted)) { 2125 $passed = true; 2126 2127 if (!$cfg['keep']['php']) { 2128 @unlink($test_file); 2129 } 2130 2131 if (!$leaked && !$failed_headers) { 2132 if (isset($section_text['XFAIL'] )) { 2133 $warn = true; 2134 $info = " (warn: XFAIL section but test passes)"; 2135 }else { 2136 show_result("PASS", $tested, $tested_file, '', $temp_filenames); 2137 junit_mark_test_as('PASS', $shortname, $tested); 2138 return 'PASSED'; 2139 } 2140 } 2141 } 2142 2143 $wanted_re = null; 2144 } 2145 2146 // Test failed so we need to report details. 2147 if ($failed_headers) { 2148 $passed = false; 2149 $wanted = $wanted_headers . "\n--HEADERS--\n" . $wanted; 2150 $output = $output_headers . "\n--HEADERS--\n" . $output; 2151 2152 if (isset($wanted_re)) { 2153 $wanted_re = preg_quote($wanted_headers . "\n--HEADERS--\n", '/') . $wanted_re; 2154 } 2155 } 2156 2157 if ($leaked) { 2158 $restype[] = 'LEAK'; 2159 } 2160 2161 if ($warn) { 2162 $restype[] = 'WARN'; 2163 } 2164 2165 if ($passed) { 2166 @unlink($tmp_post); 2167 } 2168 2169 if (!$passed) { 2170 if (isset($section_text['XFAIL'])) { 2171 $restype[] = 'XFAIL'; 2172 $info = ' XFAIL REASON: ' . rtrim($section_text['XFAIL']); 2173 } else { 2174 $restype[] = 'FAIL'; 2175 } 2176 } 2177 2178 if (!$passed) { 2179 2180 // write .exp 2181 if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted, FILE_BINARY) === false) { 2182 error("Cannot create expected test output - $exp_filename"); 2183 } 2184 2185 // write .out 2186 if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output, FILE_BINARY) === false) { 2187 error("Cannot create test output - $output_filename"); 2188 } 2189 2190 // write .diff 2191 $diff = generate_diff($wanted, $wanted_re, $output); 2192 if (is_array($IN_REDIRECT)) { 2193 $diff = "# original source file: $shortname\n" . $diff; 2194 } 2195 show_file_block('diff', $diff); 2196 if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff, FILE_BINARY) === false) { 2197 error("Cannot create test diff - $diff_filename"); 2198 } 2199 2200 // write .sh 2201 if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, "#!/bin/sh 2202 2203{$cmd} 2204", FILE_BINARY) === false) { 2205 error("Cannot create test shell script - $sh_filename"); 2206 } 2207 chmod($sh_filename, 0755); 2208 2209 // write .log 2210 if (strpos($log_format, 'L') !== false && file_put_contents($log_filename, " 2211---- EXPECTED OUTPUT 2212$wanted 2213---- ACTUAL OUTPUT 2214$output 2215---- FAILED 2216", FILE_BINARY) === false) { 2217 error("Cannot create test log - $log_filename"); 2218 error_report($file, $log_filename, $tested); 2219 } 2220 } 2221 2222 if ($valgrind && $leaked && $cfg["show"]["mem"]) { 2223 show_file_block('mem', file_get_contents($memcheck_filename)); 2224 } 2225 2226 show_result(implode('&', $restype), $tested, $tested_file, $info, $temp_filenames); 2227 2228 foreach ($restype as $type) { 2229 $PHP_FAILED_TESTS[$type.'ED'][] = array ( 2230 'name' => $file, 2231 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 2232 'output' => $output_filename, 2233 'diff' => $diff_filename, 2234 'info' => $info, 2235 ); 2236 } 2237 2238 $diff = empty($diff) ? '' : preg_replace('/\e/', '<esc>', $diff); 2239 2240 junit_mark_test_as($restype, str_replace(TEST_PHP_SRCDIR . '/', '', $tested_file), $tested, null, $info, $diff); 2241 2242 return $restype[0] . 'ED'; 2243} 2244 2245function comp_line($l1, $l2, $is_reg) 2246{ 2247 if ($is_reg) { 2248 return preg_match('/^'. $l1 . '$/s', $l2); 2249 } else { 2250 return !strcmp($l1, $l2); 2251 } 2252} 2253 2254function count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2, $cnt1, $cnt2, $steps) 2255{ 2256 $equal = 0; 2257 2258 while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { 2259 $idx1++; 2260 $idx2++; 2261 $equal++; 2262 $steps--; 2263 } 2264 if (--$steps > 0) { 2265 $eq1 = 0; 2266 $st = $steps / 2; 2267 2268 for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) { 2269 $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st); 2270 2271 if ($eq > $eq1) { 2272 $eq1 = $eq; 2273 } 2274 } 2275 2276 $eq2 = 0; 2277 $st = $steps; 2278 2279 for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) { 2280 $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st); 2281 if ($eq > $eq2) { 2282 $eq2 = $eq; 2283 } 2284 } 2285 2286 if ($eq1 > $eq2) { 2287 $equal += $eq1; 2288 } else if ($eq2 > 0) { 2289 $equal += $eq2; 2290 } 2291 } 2292 2293 return $equal; 2294} 2295 2296function generate_array_diff($ar1, $ar2, $is_reg, $w) 2297{ 2298 $idx1 = 0; $cnt1 = @count($ar1); 2299 $idx2 = 0; $cnt2 = @count($ar2); 2300 $diff = array(); 2301 $old1 = array(); 2302 $old2 = array(); 2303 2304 while ($idx1 < $cnt1 && $idx2 < $cnt2) { 2305 2306 if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { 2307 $idx1++; 2308 $idx2++; 2309 continue; 2310 } else { 2311 2312 $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1+1, $idx2, $cnt1, $cnt2, 10); 2313 $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2+1, $cnt1, $cnt2, 10); 2314 2315 if ($c1 > $c2) { 2316 $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; 2317 } else if ($c2 > 0) { 2318 $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; 2319 } else { 2320 $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; 2321 $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; 2322 } 2323 } 2324 } 2325 2326 reset($old1); $k1 = key($old1); $l1 = -2; 2327 reset($old2); $k2 = key($old2); $l2 = -2; 2328 2329 while ($k1 !== null || $k2 !== null) { 2330 2331 if ($k1 == $l1 + 1 || $k2 === null) { 2332 $l1 = $k1; 2333 $diff[] = current($old1); 2334 $k1 = next($old1) ? key($old1) : null; 2335 } else if ($k2 == $l2 + 1 || $k1 === null) { 2336 $l2 = $k2; 2337 $diff[] = current($old2); 2338 $k2 = next($old2) ? key($old2) : null; 2339 } else if ($k1 < $k2) { 2340 $l1 = $k1; 2341 $diff[] = current($old1); 2342 $k1 = next($old1) ? key($old1) : null; 2343 } else { 2344 $l2 = $k2; 2345 $diff[] = current($old2); 2346 $k2 = next($old2) ? key($old2) : null; 2347 } 2348 } 2349 2350 while ($idx1 < $cnt1) { 2351 $diff[] = sprintf("%03d- ", $idx1 + 1) . $w[$idx1++]; 2352 } 2353 2354 while ($idx2 < $cnt2) { 2355 $diff[] = sprintf("%03d+ ", $idx2 + 1) . $ar2[$idx2++]; 2356 } 2357 2358 return $diff; 2359} 2360 2361function generate_diff($wanted, $wanted_re, $output) 2362{ 2363 $w = explode("\n", $wanted); 2364 $o = explode("\n", $output); 2365 $r = is_null($wanted_re) ? $w : explode("\n", $wanted_re); 2366 $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w); 2367 2368 return implode("\r\n", $diff); 2369} 2370 2371function error($message) 2372{ 2373 echo "ERROR: {$message}\n"; 2374 exit(1); 2375} 2376 2377function settings2array($settings, &$ini_settings) 2378{ 2379 foreach($settings as $setting) { 2380 2381 if (strpos($setting, '=') !== false) { 2382 $setting = explode("=", $setting, 2); 2383 $name = trim($setting[0]); 2384 $value = trim($setting[1]); 2385 2386 if ($name == 'extension' || $name == 'zend_extension') { 2387 2388 if (!isset($ini_settings[$name])) { 2389 $ini_settings[$name] = array(); 2390 } 2391 2392 $ini_settings[$name][] = $value; 2393 2394 } else { 2395 $ini_settings[$name] = $value; 2396 } 2397 } 2398 } 2399} 2400 2401function settings2params(&$ini_settings) 2402{ 2403 $settings = ''; 2404 2405 foreach($ini_settings as $name => $value) { 2406 2407 if (is_array($value)) { 2408 foreach($value as $val) { 2409 $val = addslashes($val); 2410 $settings .= " -d \"$name=$val\""; 2411 } 2412 } else { 2413 if (substr(PHP_OS, 0, 3) == "WIN" && !empty($value) && $value{0} == '"') { 2414 $len = strlen($value); 2415 2416 if ($value{$len - 1} == '"') { 2417 $value{0} = "'"; 2418 $value{$len - 1} = "'"; 2419 } 2420 } else { 2421 $value = addslashes($value); 2422 } 2423 2424 $settings .= " -d \"$name=$value\""; 2425 } 2426 } 2427 2428 $ini_settings = $settings; 2429} 2430 2431function compute_summary() 2432{ 2433 global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results; 2434 2435 $n_total = count($test_results); 2436 $n_total += $ignored_by_ext; 2437 $sum_results = array( 2438 'PASSED' => 0, 2439 'WARNED' => 0, 2440 'SKIPPED' => 0, 2441 'FAILED' => 0, 2442 'BORKED' => 0, 2443 'LEAKED' => 0, 2444 'XFAILED' => 0 2445 ); 2446 2447 foreach ($test_results as $v) { 2448 $sum_results[$v]++; 2449 } 2450 2451 $sum_results['SKIPPED'] += $ignored_by_ext; 2452 $percent_results = array(); 2453 2454 foreach ($sum_results as $v => $n) { 2455 $percent_results[$v] = (100.0 * $n) / $n_total; 2456 } 2457} 2458 2459function get_summary($show_ext_summary, $show_html) 2460{ 2461 global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $valgrind; 2462 2463 $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED']; 2464 2465 if ($x_total) { 2466 $x_warned = (100.0 * $sum_results['WARNED']) / $x_total; 2467 $x_failed = (100.0 * $sum_results['FAILED']) / $x_total; 2468 $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total; 2469 $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total; 2470 $x_passed = (100.0 * $sum_results['PASSED']) / $x_total; 2471 } else { 2472 $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = 0; 2473 } 2474 2475 $summary = ''; 2476 2477 if ($show_html) { 2478 $summary .= "<pre>\n"; 2479 } 2480 2481 if ($show_ext_summary) { 2482 $summary .= ' 2483===================================================================== 2484TEST RESULT SUMMARY 2485--------------------------------------------------------------------- 2486Exts skipped : ' . sprintf('%4d', $exts_skipped) . ' 2487Exts tested : ' . sprintf('%4d', $exts_tested) . ' 2488--------------------------------------------------------------------- 2489'; 2490 } 2491 2492 $summary .= ' 2493Number of tests : ' . sprintf('%4d', $n_total) . ' ' . sprintf('%8d', $x_total); 2494 2495 if ($sum_results['BORKED']) { 2496 $summary .= ' 2497Tests borked : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------'; 2498 } 2499 2500 $summary .= ' 2501Tests skipped : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' -------- 2502Tests warned : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . ' 2503Tests failed : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed) . ' 2504Expected fail : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed); 2505 2506 if ($valgrind) { 2507 $summary .= ' 2508Tests leaked : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked); 2509 } 2510 2511 $summary .= ' 2512Tests passed : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . ' 2513--------------------------------------------------------------------- 2514Time taken : ' . sprintf('%4d seconds', $end_time - $start_time) . ' 2515===================================================================== 2516'; 2517 $failed_test_summary = ''; 2518 2519 if (count($PHP_FAILED_TESTS['SLOW'])) { 2520 usort($PHP_FAILED_TESTS['SLOW'], function($a, $b) { 2521 return $a['info'] < $b['info'] ? 1 : -1; 2522 }); 2523 2524 $failed_test_summary .= ' 2525===================================================================== 2526SLOW TEST SUMMARY 2527--------------------------------------------------------------------- 2528'; 2529 foreach ($PHP_FAILED_TESTS['SLOW'] as $failed_test_data) { 2530 $failed_test_summary .= sprintf('(%.3f s) ', $failed_test_data['info']) . $failed_test_data['test_name'] . "\n"; 2531 } 2532 $failed_test_summary .= "=====================================================================\n"; 2533 } 2534 2535 if (count($PHP_FAILED_TESTS['XFAILED'])) { 2536 $failed_test_summary .= ' 2537===================================================================== 2538EXPECTED FAILED TEST SUMMARY 2539--------------------------------------------------------------------- 2540'; 2541 foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) { 2542 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2543 } 2544 $failed_test_summary .= "=====================================================================\n"; 2545 } 2546 2547 if (count($PHP_FAILED_TESTS['BORKED'])) { 2548 $failed_test_summary .= ' 2549===================================================================== 2550BORKED TEST SUMMARY 2551--------------------------------------------------------------------- 2552'; 2553 foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) { 2554 $failed_test_summary .= $failed_test_data['info'] . "\n"; 2555 } 2556 2557 $failed_test_summary .= "=====================================================================\n"; 2558 } 2559 2560 if (count($PHP_FAILED_TESTS['FAILED'])) { 2561 $failed_test_summary .= ' 2562===================================================================== 2563FAILED TEST SUMMARY 2564--------------------------------------------------------------------- 2565'; 2566 foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) { 2567 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2568 } 2569 $failed_test_summary .= "=====================================================================\n"; 2570 } 2571 if (count($PHP_FAILED_TESTS['WARNED'])) { 2572 $failed_test_summary .= ' 2573===================================================================== 2574WARNED TEST SUMMARY 2575--------------------------------------------------------------------- 2576'; 2577 foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) { 2578 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2579 } 2580 2581 $failed_test_summary .= "=====================================================================\n"; 2582 } 2583 2584 if (count($PHP_FAILED_TESTS['LEAKED'])) { 2585 $failed_test_summary .= ' 2586===================================================================== 2587LEAKED TEST SUMMARY 2588--------------------------------------------------------------------- 2589'; 2590 foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) { 2591 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2592 } 2593 2594 $failed_test_summary .= "=====================================================================\n"; 2595 } 2596 2597 if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) { 2598 $summary .= $failed_test_summary; 2599 } 2600 2601 if ($show_html) { 2602 $summary .= "</pre>"; 2603 } 2604 2605 return $summary; 2606} 2607 2608function show_start($start_time) 2609{ 2610 global $html_output, $html_file; 2611 2612 if ($html_output) { 2613 fwrite($html_file, "<h2>Time Start: " . date('Y-m-d H:i:s', $start_time) . "</h2>\n"); 2614 fwrite($html_file, "<table>\n"); 2615 } 2616 2617 echo "TIME START " . date('Y-m-d H:i:s', $start_time) . "\n=====================================================================\n"; 2618} 2619 2620function show_end($end_time) 2621{ 2622 global $html_output, $html_file; 2623 2624 if ($html_output) { 2625 fwrite($html_file, "</table>\n"); 2626 fwrite($html_file, "<h2>Time End: " . date('Y-m-d H:i:s', $end_time) . "</h2>\n"); 2627 } 2628 2629 echo "=====================================================================\nTIME END " . date('Y-m-d H:i:s', $end_time) . "\n"; 2630} 2631 2632function show_summary() 2633{ 2634 global $html_output, $html_file; 2635 2636 if ($html_output) { 2637 fwrite($html_file, "<hr/>\n" . get_summary(true, true)); 2638 } 2639 2640 echo get_summary(true, false); 2641} 2642 2643function show_redirect_start($tests, $tested, $tested_file) 2644{ 2645 global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; 2646 2647 if ($html_output) { 2648 fwrite($html_file, "<tr><td colspan='3'>---> $tests ($tested [$tested_file]) begin</td></tr>\n"); 2649 } 2650 2651 if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { 2652 echo "REDIRECT $tests ($tested [$tested_file]) begin\n"; 2653 } else { 2654 // Write over the last line to avoid random trailing chars on next echo 2655 echo str_repeat(" ", $line_length), "\r"; 2656 } 2657} 2658 2659function show_redirect_ends($tests, $tested, $tested_file) 2660{ 2661 global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; 2662 2663 if ($html_output) { 2664 fwrite($html_file, "<tr><td colspan='3'>---> $tests ($tested [$tested_file]) done</td></tr>\n"); 2665 } 2666 2667 if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { 2668 echo "REDIRECT $tests ($tested [$tested_file]) done\n"; 2669 } else { 2670 // Write over the last line to avoid random trailing chars on next echo 2671 echo str_repeat(" ", $line_length), "\r"; 2672 } 2673} 2674 2675function show_test($test_idx, $shortname) 2676{ 2677 global $test_cnt; 2678 global $line_length; 2679 2680 $str = "TEST $test_idx/$test_cnt [$shortname]\r"; 2681 $line_length = strlen($str); 2682 echo $str; 2683 flush(); 2684} 2685 2686function show_result($result, $tested, $tested_file, $extra = '', $temp_filenames = null) 2687{ 2688 global $html_output, $html_file, $temp_target, $temp_urlbase, $line_length, $SHOW_ONLY_GROUPS; 2689 2690 if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { 2691 echo "$result $tested [$tested_file] $extra\n"; 2692 } else if (!$SHOW_ONLY_GROUPS) { 2693 // Write over the last line to avoid random trailing chars on next echo 2694 echo str_repeat(" ", $line_length), "\r"; 2695 } 2696 2697 if ($html_output) { 2698 2699 if (isset($temp_filenames['file']) && file_exists($temp_filenames['file'])) { 2700 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['file']); 2701 $tested = "<a href='$url'>$tested</a>"; 2702 } 2703 2704 if (isset($temp_filenames['skip']) && file_exists($temp_filenames['skip'])) { 2705 2706 if (empty($extra)) { 2707 $extra = "skipif"; 2708 } 2709 2710 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['skip']); 2711 $extra = "<a href='$url'>$extra</a>"; 2712 2713 } else if (empty($extra)) { 2714 $extra = " "; 2715 } 2716 2717 if (isset($temp_filenames['diff']) && file_exists($temp_filenames['diff'])) { 2718 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['diff']); 2719 $diff = "<a href='$url'>diff</a>"; 2720 } else { 2721 $diff = " "; 2722 } 2723 2724 if (isset($temp_filenames['mem']) && file_exists($temp_filenames['mem'])) { 2725 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['mem']); 2726 $mem = "<a href='$url'>leaks</a>"; 2727 } else { 2728 $mem = " "; 2729 } 2730 2731 fwrite($html_file, 2732 "<tr>" . 2733 "<td>$result</td>" . 2734 "<td>$tested</td>" . 2735 "<td>$extra</td>" . 2736 "<td>$diff</td>" . 2737 "<td>$mem</td>" . 2738 "</tr>\n"); 2739 } 2740} 2741 2742function junit_init() { 2743 // Check whether a junit log is wanted. 2744 $JUNIT = getenv('TEST_PHP_JUNIT'); 2745 if (empty($JUNIT)) { 2746 $JUNIT = FALSE; 2747 } elseif (!$fp = fopen($JUNIT, 'w')) { 2748 error("Failed to open $JUNIT for writing."); 2749 } else { 2750 $JUNIT = array( 2751 'fp' => $fp, 2752 'name' => 'PHP', 2753 'test_total' => 0, 2754 'test_pass' => 0, 2755 'test_fail' => 0, 2756 'test_error' => 0, 2757 'test_skip' => 0, 2758 'test_warn' => 0, 2759 'execution_time'=> 0, 2760 'suites' => array(), 2761 'files' => array() 2762 ); 2763 } 2764 2765 $GLOBALS['JUNIT'] = $JUNIT; 2766} 2767 2768function junit_save_xml() { 2769 global $JUNIT; 2770 if (!junit_enabled()) return; 2771 2772 $xml = '<' . '?' . 'xml version="1.0" encoding="UTF-8"' . '?' . '>'. PHP_EOL; 2773 $xml .= sprintf( 2774 '<testsuites name="%s" tests="%s" failures="%d" errors="%d" skip="%d" time="%s">' . PHP_EOL, 2775 $JUNIT['name'], $JUNIT['test_total'], $JUNIT['test_fail'], $JUNIT['test_error'], $JUNIT['test_skip'], 2776 $JUNIT['execution_time'] 2777 ); 2778 $xml .= junit_get_suite_xml(); 2779 $xml .= '</testsuites>'; 2780 fwrite($JUNIT['fp'], $xml); 2781} 2782 2783function junit_get_suite_xml($suite_name = '') { 2784 global $JUNIT; 2785 2786 $result = ""; 2787 2788 foreach ($JUNIT['suites'] as $suite_name => $suite) { 2789 $result .= sprintf( 2790 '<testsuite name="%s" tests="%s" failures="%d" errors="%d" skip="%d" time="%s">' . PHP_EOL, 2791 $suite['name'], $suite['test_total'], $suite['test_fail'], $suite['test_error'], $suite['test_skip'], 2792 $suite['execution_time'] 2793 ); 2794 2795 if (!empty($suite_name)) { 2796 foreach($suite['files'] as $file) { 2797 $result .= $JUNIT['files'][$file]['xml']; 2798 } 2799 } 2800 2801 $result .= '</testsuite>' . PHP_EOL; 2802 } 2803 2804 return $result; 2805} 2806 2807function junit_enabled() { 2808 global $JUNIT; 2809 return !empty($JUNIT); 2810} 2811 2812/** 2813 * @param array|string $type 2814 * @param string $file_name 2815 * @param string $test_name 2816 * @param int|string $time 2817 * @param string $message 2818 * @param string $details 2819 * @return void 2820 */ 2821function junit_mark_test_as($type, $file_name, $test_name, $time = null, $message = '', $details = '') { 2822 global $JUNIT; 2823 if (!junit_enabled()) return; 2824 2825 $suite = junit_get_suitename_for($file_name); 2826 2827 junit_suite_record($suite, 'test_total'); 2828 2829 $time = $time ?? junit_get_timer($file_name); 2830 junit_suite_record($suite, 'execution_time', $time); 2831 2832 $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8'); 2833 $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function ($c) { 2834 return sprintf('[[0x%02x]]', ord($c[0])); 2835 }, $escaped_details); 2836 $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); 2837 2838 $escaped_test_name = htmlspecialchars($test_name, ENT_QUOTES); 2839 $JUNIT['files'][$file_name]['xml'] = "<testcase classname='" . $suite . "." . basename($file_name) . "' name='$escaped_test_name' time='$time'>\n"; 2840 2841 if (is_array($type)) { 2842 $output_type = $type[0] . 'ED'; 2843 $temp = array_intersect(array('XFAIL', 'FAIL', 'WARN'), $type); 2844 $type = reset($temp); 2845 } else { 2846 $output_type = $type . 'ED'; 2847 } 2848 2849 if ('PASS' == $type || 'XFAIL' == $type) { 2850 junit_suite_record($suite, 'test_pass'); 2851 } elseif ('BORK' == $type) { 2852 junit_suite_record($suite, 'test_error'); 2853 $JUNIT['files'][$file_name]['xml'] .= "<error type='$output_type' message='$escaped_message'/>\n"; 2854 } elseif ('SKIP' == $type) { 2855 junit_suite_record($suite, 'test_skip'); 2856 $JUNIT['files'][$file_name]['xml'] .= "<skipped>$escaped_message</skipped>\n"; 2857 } elseif ('WARN' == $type) { 2858 junit_suite_record($suite, 'test_warn'); 2859 $JUNIT['files'][$file_name]['xml'] .= "<warning>$escaped_message</warning>\n"; 2860 } elseif ('FAIL' == $type) { 2861 junit_suite_record($suite, 'test_fail'); 2862 $JUNIT['files'][$file_name]['xml'] .= "<failure type='$output_type' message='$escaped_message'>$escaped_details</failure>\n"; 2863 } else { 2864 junit_suite_record($suite, 'test_error'); 2865 $JUNIT['files'][$file_name]['xml'] .= "<error type='$output_type' message='$escaped_message'>$escaped_details</error>\n"; 2866 } 2867 2868 $JUNIT['files'][$file_name]['xml'] .= "</testcase>\n"; 2869 2870} 2871 2872function junit_suite_record($suite, $param, $value = 1) { 2873 global $JUNIT; 2874 2875 $JUNIT[$param] += $value; 2876 $JUNIT['suites'][$suite][$param] += $value; 2877} 2878 2879function junit_get_timer($file_name) { 2880 global $JUNIT; 2881 if (!junit_enabled()) return 0; 2882 2883 if (isset($JUNIT['files'][$file_name]['total'])) { 2884 return number_format($JUNIT['files'][$file_name]['total'], 4); 2885 } 2886 2887 return 0; 2888} 2889 2890function junit_start_timer($file_name) { 2891 global $JUNIT; 2892 if (!junit_enabled()) return; 2893 2894 if (!isset($JUNIT['files'][$file_name]['start'])) { 2895 $JUNIT['files'][$file_name]['start'] = microtime(true); 2896 2897 $suite = junit_get_suitename_for($file_name); 2898 junit_init_suite($suite); 2899 $JUNIT['suites'][$suite]['files'][$file_name] = $file_name; 2900 } 2901} 2902 2903function junit_get_suitename_for($file_name) { 2904 return junit_path_to_classname(dirname($file_name)); 2905} 2906 2907function junit_path_to_classname($file_name) { 2908 global $JUNIT; 2909 2910 $ret = $JUNIT['name']; 2911 $_tmp = array(); 2912 2913 // lookup whether we're in the PHP source checkout 2914 $max = 5; 2915 if (is_file($file_name)) { 2916 $dir = dirname(realpath($file_name)); 2917 } else { 2918 $dir = realpath($file_name); 2919 } 2920 do { 2921 array_unshift($_tmp, basename($dir)); 2922 $chk = $dir . DIRECTORY_SEPARATOR . "main" . DIRECTORY_SEPARATOR . "php_version.h"; 2923 $dir = dirname($dir); 2924 } while (!file_exists($chk) && --$max > 0); 2925 if (file_exists($chk)) { 2926 if ($max) { 2927 array_shift($_tmp); 2928 } 2929 foreach ($_tmp as $p) { 2930 $ret = $ret . "." . preg_replace(",[^a-z0-9]+,i", ".", $p); 2931 } 2932 return $ret; 2933 } 2934 2935 return $JUNIT['name'] . '.' . str_replace(array(DIRECTORY_SEPARATOR, '-'), '.', $file_name); 2936} 2937 2938function junit_init_suite($suite_name) { 2939 global $JUNIT; 2940 if (!junit_enabled()) return; 2941 2942 if (!empty($JUNIT['suites'][$suite_name])) { 2943 return; 2944 } 2945 2946 $JUNIT['suites'][$suite_name] = array( 2947 'name' => $suite_name, 2948 'test_total' => 0, 2949 'test_pass' => 0, 2950 'test_fail' => 0, 2951 'test_error' => 0, 2952 'test_skip' => 0, 2953 'suites' => array(), 2954 'files' => array(), 2955 'execution_time'=> 0, 2956 ); 2957} 2958 2959function junit_finish_timer($file_name) { 2960 global $JUNIT; 2961 if (!junit_enabled()) return; 2962 2963 if (!isset($JUNIT['files'][$file_name]['start'])) { 2964 error("Timer for $file_name was not started!"); 2965 } 2966 2967 if (!isset($JUNIT['files'][$file_name]['total'])) { 2968 $JUNIT['files'][$file_name]['total'] = 0; 2969 } 2970 2971 $start = $JUNIT['files'][$file_name]['start']; 2972 $JUNIT['files'][$file_name]['total'] += microtime(true) - $start; 2973 unset($JUNIT['files'][$file_name]['start']); 2974} 2975 2976class RuntestsValgrind { 2977 protected $version = ''; 2978 protected $header = ''; 2979 protected $version_3_3_0 = false; 2980 protected $version_3_8_0 = false; 2981 2982 public function getVersion() { 2983 return $this->version; 2984 } 2985 2986 public function getHeader() { 2987 return $this->header; 2988 } 2989 2990 public function __construct(array $environment) { 2991 $header = system_with_timeout('valgrind --version', $environment); 2992 if (!$header) { 2993 error("Valgrind returned no version info, cannot proceed.\nPlease check if Valgrind is installed."); 2994 } 2995 $count = 0; 2996 $version = preg_replace("/valgrind-(\d+)\.(\d+)\.(\d+)([.\w_-]+)?(\s+)/", '$1.$2.$3', $header, 1, $count); 2997 if ($count != 1) { 2998 error("Valgrind returned invalid version info (\"{$header}\"), cannot proceed."); 2999 } 3000 $this->version = $version; 3001 $this->header = trim($header); 3002 $this->version_3_3_0 = version_compare($version, '3.3.0', '>='); 3003 $this->version_3_8_0 = version_compare($version, '3.8.0', '>='); 3004 } 3005 3006 public function wrapCommand($cmd, $memcheck_filename, $check_all) { 3007 $vcmd = 'valgrind -q --tool=memcheck --trace-children=yes'; 3008 if ($check_all) { 3009 $vcmd .= ' --smc-check=all'; 3010 } 3011 3012 /* --vex-iropt-register-updates=allregs-at-mem-access is necessary for phpdbg watchpoint tests */ 3013 if ($this->version_3_8_0) { 3014 /* valgrind 3.3.0+ doesn't have --log-file-exactly option */ 3015 return "$vcmd --vex-iropt-register-updates=allregs-at-mem-access --log-file=$memcheck_filename $cmd"; 3016 } elseif ($this->version_3_3_0) { 3017 return "$vcmd --vex-iropt-precise-memory-exns=yes --log-file=$memcheck_filename $cmd"; 3018 } else { 3019 return "$vcmd --vex-iropt-precise-memory-exns=yes --log-file-exactly=$memcheck_filename $cmd"; 3020 } 3021 } 3022} 3023 3024/* 3025 * Local variables: 3026 * tab-width: 4 3027 * c-basic-offset: 4 3028 * End: 3029 * vim: noet sw=4 ts=4 3030 */ 3031?> 3032