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