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