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