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