1#!/usr/bin/env php 2<?php 3/* 4 +----------------------------------------------------------------------+ 5 | PHP Version 5 | 6 +----------------------------------------------------------------------+ 7 | Copyright (c) 1997-2010 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: 60288e2d791bcf8486e334d4ea43c876431c9b3a $ */ 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: 60288e2d791bcf8486e334d4ea43c876431c9b3a $' . "\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'); 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 if (@count($section_text['FILE_EXTERNAL']) == 1) { 1296 // don't allow tests to retrieve files from anywhere but this subdirectory 1297 $section_text['FILE_EXTERNAL'] = dirname($file) . '/' . trim(str_replace('..', '', $section_text['FILE_EXTERNAL'])); 1298 1299 if (file_exists($section_text['FILE_EXTERNAL'])) { 1300 $section_text['FILE'] = file_get_contents($section_text['FILE_EXTERNAL'], FILE_BINARY); 1301 unset($section_text['FILE_EXTERNAL']); 1302 } else { 1303 $bork_info = "could not load --FILE_EXTERNAL-- " . dirname($file) . '/' . trim($section_text['FILE_EXTERNAL']); 1304 $borked = true; 1305 } 1306 } 1307 1308 if ((@count($section_text['EXPECT']) + @count($section_text['EXPECTF']) + @count($section_text['EXPECTREGEX'])) != 1) { 1309 $bork_info = "missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"; 1310 $borked = true; 1311 } 1312 } 1313 } 1314 fclose($fp); 1315 1316 $shortname = str_replace($cwd . '/', '', $file); 1317 $tested_file = $shortname; 1318 1319 if ($borked) { 1320 show_result("BORK", $bork_info, $tested_file); 1321 $PHP_FAILED_TESTS['BORKED'][] = array ( 1322 'name' => $file, 1323 'test_name' => '', 1324 'output' => '', 1325 'diff' => '', 1326 'info' => "$bork_info [$file]", 1327 ); 1328 1329 junit_mark_test_as('BORK', $shortname, $tested_file, 0, $bork_info); 1330 return 'BORKED'; 1331 } 1332 1333 $tested = trim($section_text['TEST']); 1334 1335 /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */ 1336 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'])) { 1337 if (isset($php_cgi)) { 1338 $old_php = $php; 1339 $php = $php_cgi . ' -C '; 1340 } else if (!strncasecmp(PHP_OS, "win", 3) && file_exists(dirname($php) . "/php-cgi.exe")) { 1341 $old_php = $php; 1342 $php = realpath(dirname($php) . "/php-cgi.exe") . ' -C '; 1343 } else { 1344 if (file_exists(dirname($php) . "/../../sapi/cgi/php-cgi")) { 1345 $old_php = $php; 1346 $php = realpath(dirname($php) . "/../../sapi/cgi/php-cgi") . ' -C '; 1347 } else if (file_exists("./sapi/cgi/php-cgi")) { 1348 $old_php = $php; 1349 $php = realpath("./sapi/cgi/php-cgi") . ' -C '; 1350 } else if (file_exists(dirname($php) . "/php-cgi")) { 1351 $old_php = $php; 1352 $php = realpath(dirname($php) . "/php-cgi") . ' -C '; 1353 } else { 1354 show_result('SKIP', $tested, $tested_file, "reason: CGI not available"); 1355 1356 junit_init_suite(junit_get_suitename_for($shortname)); 1357 junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available'); 1358 return 'SKIPPED'; 1359 } 1360 } 1361 } 1362 1363 show_test($test_idx, $shortname); 1364 1365 if (is_array($IN_REDIRECT)) { 1366 $temp_dir = $test_dir = $IN_REDIRECT['dir']; 1367 } else { 1368 $temp_dir = $test_dir = realpath(dirname($file)); 1369 } 1370 1371 if ($temp_source && $temp_target) { 1372 $temp_dir = str_replace($temp_source, $temp_target, $temp_dir); 1373 } 1374 1375 $main_file_name = basename($file,'phpt'); 1376 1377 $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff'; 1378 $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log'; 1379 $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp'; 1380 $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out'; 1381 $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem'; 1382 $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh'; 1383 $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; 1384 $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; 1385 $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; 1386 $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; 1387 $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; 1388 $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; 1389 $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('/phpt.'); 1390 $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't'; 1391 1392 if ($temp_source && $temp_target) { 1393 $temp_skipif .= 's'; 1394 $temp_file .= 's'; 1395 $temp_clean .= 's'; 1396 $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps'; 1397 1398 if (!is_dir(dirname($copy_file))) { 1399 mkdir(dirname($copy_file), 0777, true) or error("Cannot create output directory - " . dirname($copy_file)); 1400 } 1401 1402 if (isset($section_text['FILE'])) { 1403 save_text($copy_file, $section_text['FILE']); 1404 } 1405 1406 $temp_filenames = array( 1407 'file' => $copy_file, 1408 'diff' => $diff_filename, 1409 'log' => $log_filename, 1410 'exp' => $exp_filename, 1411 'out' => $output_filename, 1412 'mem' => $memcheck_filename, 1413 'sh' => $sh_filename, 1414 'php' => $temp_file, 1415 'skip' => $temp_skipif, 1416 'clean'=> $temp_clean); 1417 } 1418 1419 if (is_array($IN_REDIRECT)) { 1420 $tested = $IN_REDIRECT['prefix'] . ' ' . trim($section_text['TEST']); 1421 $tested_file = $tmp_relative_file; 1422 } 1423 1424 // unlink old test results 1425 @unlink($diff_filename); 1426 @unlink($log_filename); 1427 @unlink($exp_filename); 1428 @unlink($output_filename); 1429 @unlink($memcheck_filename); 1430 @unlink($sh_filename); 1431 @unlink($temp_file); 1432 @unlink($test_file); 1433 @unlink($temp_skipif); 1434 @unlink($test_skipif); 1435 @unlink($tmp_post); 1436 @unlink($temp_clean); 1437 @unlink($test_clean); 1438 1439 // Reset environment from any previous test. 1440 $env['REDIRECT_STATUS'] = ''; 1441 $env['QUERY_STRING'] = ''; 1442 $env['PATH_TRANSLATED'] = ''; 1443 $env['SCRIPT_FILENAME'] = ''; 1444 $env['REQUEST_METHOD'] = ''; 1445 $env['CONTENT_TYPE'] = ''; 1446 $env['CONTENT_LENGTH'] = ''; 1447 $env['TZ'] = ''; 1448 1449 if (!empty($section_text['ENV'])) { 1450 1451 foreach(explode("\n", trim($section_text['ENV'])) as $e) { 1452 $e = explode('=', trim($e), 2); 1453 1454 if (!empty($e[0]) && isset($e[1])) { 1455 $env[$e[0]] = $e[1]; 1456 } 1457 } 1458 } 1459 1460 // Default ini settings 1461 $ini_settings = array(); 1462 // additional ini overwrites 1463 //$ini_overwrites[] = 'setting=value'; 1464 settings2array($ini_overwrites, $ini_settings); 1465 1466 // Any special ini settings 1467 // these may overwrite the test defaults... 1468 if (array_key_exists('INI', $section_text)) { 1469 if (strpos($section_text['INI'], '{PWD}') !== false) { 1470 $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); 1471 } 1472 settings2array(preg_split( "/[\n\r]+/", $section_text['INI']), $ini_settings); 1473 } 1474 1475 // Additional required extensions 1476 if (array_key_exists('EXTENSIONS', $section_text)) { 1477 $ext_dir=`$php -r 'echo ini_get("extension_dir");'`; 1478 $extensions = preg_split("/[\n\r]+/", trim($section_text['EXTENSIONS'])); 1479 $loaded = explode(",", `$php -n -r 'echo join(",", get_loaded_extensions());'`); 1480 foreach ($extensions as $req_ext) { 1481 if (!in_array($req_ext, $loaded)) { 1482 $ini_settings['extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $req_ext . '.' . PHP_SHLIB_SUFFIX; 1483 } 1484 } 1485 } 1486 1487 settings2params($ini_settings); 1488 1489 // Check if test should be skipped. 1490 $info = ''; 1491 $warn = false; 1492 1493 if (array_key_exists('SKIPIF', $section_text)) { 1494 1495 if (trim($section_text['SKIPIF'])) { 1496 show_file_block('skip', $section_text['SKIPIF']); 1497 save_text($test_skipif, $section_text['SKIPIF'], $temp_skipif); 1498 $extra = substr(PHP_OS, 0, 3) !== "WIN" ? 1499 "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; 1500 1501 if ($leak_check) { 1502 $env['USE_ZEND_ALLOC'] = '0'; 1503 $env['ZEND_DONT_UNLOAD_MODULES'] = 1; 1504 } else { 1505 $env['USE_ZEND_ALLOC'] = '1'; 1506 $env['ZEND_DONT_UNLOAD_MODULES'] = 0; 1507 } 1508 1509 junit_start_timer($shortname); 1510 1511 $output = system_with_timeout("$extra $php $pass_options -q $ini_settings -d display_errors=0 \"$test_skipif\"", $env); 1512 1513 junit_finish_timer($shortname); 1514 1515 if (!$cfg['keep']['skip']) { 1516 @unlink($test_skipif); 1517 } 1518 1519 if (!strncasecmp('skip', ltrim($output), 4)) { 1520 1521 if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { 1522 show_result('SKIP', $tested, $tested_file, "reason: $m[1]", $temp_filenames); 1523 } else { 1524 show_result('SKIP', $tested, $tested_file, '', $temp_filenames); 1525 } 1526 1527 if (isset($old_php)) { 1528 $php = $old_php; 1529 } 1530 1531 if (!$cfg['keep']['skip']) { 1532 @unlink($test_skipif); 1533 } 1534 1535 $message = !empty($m[1]) ? $m[1] : ''; 1536 junit_mark_test_as('SKIP', $shortname, $tested, null, $message); 1537 return 'SKIPPED'; 1538 } 1539 1540 if (!strncasecmp('info', ltrim($output), 4)) { 1541 if (preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { 1542 $info = " (info: $m[1])"; 1543 } 1544 } 1545 1546 if (!strncasecmp('warn', ltrim($output), 4)) { 1547 if (preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { 1548 $warn = true; /* only if there is a reason */ 1549 $info = " (warn: $m[1])"; 1550 } 1551 } 1552 } 1553 } 1554 1555 if (!extension_loaded("zlib") 1556 && ( array_key_exists("GZIP_POST", $section_text) 1557 || array_key_exists("DEFLATE_POST", $section_text)) 1558 ) { 1559 $message = "ext/zlib required"; 1560 show_result('SKIP', $tested, $tested_file, "reason: $message", $temp_filenames); 1561 junit_mark_test_as('SKIP', $shortname, $tested, null, $message); 1562 return 'SKIPPED'; 1563 } 1564 1565 if (@count($section_text['REDIRECTTEST']) == 1) { 1566 $test_files = array(); 1567 1568 $IN_REDIRECT = eval($section_text['REDIRECTTEST']); 1569 $IN_REDIRECT['via'] = "via [$shortname]\n\t"; 1570 $IN_REDIRECT['dir'] = realpath(dirname($file)); 1571 $IN_REDIRECT['prefix'] = trim($section_text['TEST']); 1572 1573 if (count($IN_REDIRECT['TESTS']) == 1) { 1574 1575 if (is_array($org_file)) { 1576 $test_files[] = $org_file[1]; 1577 } else { 1578 $GLOBALS['test_files'] = $test_files; 1579 find_files($IN_REDIRECT['TESTS']); 1580 1581 foreach($GLOBALS['test_files'] as $f) { 1582 $test_files[] = array($f, $file); 1583 } 1584 } 1585 $test_cnt += @count($test_files) - 1; 1586 $test_idx--; 1587 1588 show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file); 1589 1590 // set up environment 1591 $redirenv = array_merge($environment, $IN_REDIRECT['ENV']); 1592 $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR; 1593 1594 usort($test_files, "test_sort"); 1595 run_all_tests($test_files, $redirenv, $tested); 1596 1597 show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file); 1598 1599 // a redirected test never fails 1600 $IN_REDIRECT = false; 1601 1602 junit_mark_test_as('PASS', $shortname, $tested); 1603 return 'REDIR'; 1604 1605 } else { 1606 1607 $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; 1608 show_result("BORK", $bork_info, '', $temp_filenames); 1609 $PHP_FAILED_TESTS['BORKED'][] = array ( 1610 'name' => $file, 1611 'test_name' => '', 1612 'output' => '', 1613 'diff' => '', 1614 'info' => "$bork_info [$file]", 1615 ); 1616 } 1617 } 1618 1619 if (is_array($org_file) || @count($section_text['REDIRECTTEST']) == 1) { 1620 1621 if (is_array($org_file)) { 1622 $file = $org_file[0]; 1623 } 1624 1625 $bork_info = "Redirected test did not contain redirection info"; 1626 show_result("BORK", $bork_info, '', $temp_filenames); 1627 $PHP_FAILED_TESTS['BORKED'][] = array ( 1628 'name' => $file, 1629 'test_name' => '', 1630 'output' => '', 1631 'diff' => '', 1632 'info' => "$bork_info [$file]", 1633 ); 1634 1635 junit_mark_test_as('BORK', $shortname, $tested, null, $bork_info); 1636 1637 return 'BORKED'; 1638 } 1639 1640 // We've satisfied the preconditions - run the test! 1641 show_file_block('php', $section_text['FILE'], 'TEST'); 1642 save_text($test_file, $section_text['FILE'], $temp_file); 1643 1644 if (array_key_exists('GET', $section_text)) { 1645 $query_string = trim($section_text['GET']); 1646 } else { 1647 $query_string = ''; 1648 } 1649 1650 $env['REDIRECT_STATUS'] = '1'; 1651 $env['QUERY_STRING'] = $query_string; 1652 $env['PATH_TRANSLATED'] = $test_file; 1653 $env['SCRIPT_FILENAME'] = $test_file; 1654 1655 if (array_key_exists('COOKIE', $section_text)) { 1656 $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); 1657 } else { 1658 $env['HTTP_COOKIE'] = ''; 1659 } 1660 1661 $args = isset($section_text['ARGS']) ? ' -- ' . $section_text['ARGS'] : ''; 1662 1663 if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { 1664 1665 $post = trim($section_text['POST_RAW']); 1666 $raw_lines = explode("\n", $post); 1667 1668 $request = ''; 1669 $started = false; 1670 1671 foreach ($raw_lines as $line) { 1672 1673 if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { 1674 $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); 1675 continue; 1676 } 1677 1678 if ($started) { 1679 $request .= "\n"; 1680 } 1681 1682 $started = true; 1683 $request .= $line; 1684 } 1685 1686 $env['CONTENT_LENGTH'] = strlen($request); 1687 $env['REQUEST_METHOD'] = 'POST'; 1688 1689 if (empty($request)) { 1690 junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); 1691 return 'BORKED'; 1692 } 1693 1694 save_text($tmp_post, $request); 1695 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; 1696 1697 } elseif (array_key_exists('PUT', $section_text) && !empty($section_text['PUT'])) { 1698 1699 $post = trim($section_text['PUT']); 1700 $raw_lines = explode("\n", $post); 1701 1702 $request = ''; 1703 $started = false; 1704 1705 foreach ($raw_lines as $line) { 1706 1707 if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { 1708 $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); 1709 continue; 1710 } 1711 1712 if ($started) { 1713 $request .= "\n"; 1714 } 1715 1716 $started = true; 1717 $request .= $line; 1718 } 1719 1720 $env['CONTENT_LENGTH'] = strlen($request); 1721 $env['REQUEST_METHOD'] = 'PUT'; 1722 1723 if (empty($request)) { 1724 junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); 1725 return 'BORKED'; 1726 } 1727 1728 save_text($tmp_post, $request); 1729 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; 1730 1731 } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { 1732 1733 $post = trim($section_text['POST']); 1734 save_text($tmp_post, $post); 1735 $content_length = strlen($post); 1736 1737 $env['REQUEST_METHOD'] = 'POST'; 1738 $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1739 $env['CONTENT_LENGTH'] = $content_length; 1740 1741 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; 1742 1743 } else if (array_key_exists('GZIP_POST', $section_text) && !empty($section_text['GZIP_POST'])) { 1744 1745 $post = trim($section_text['GZIP_POST']); 1746 $post = gzencode($post, 9, FORCE_GZIP); 1747 $env['HTTP_CONTENT_ENCODING'] = 'gzip'; 1748 1749 save_text($tmp_post, $post); 1750 $content_length = strlen($post); 1751 1752 $env['REQUEST_METHOD'] = 'POST'; 1753 $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1754 $env['CONTENT_LENGTH'] = $content_length; 1755 1756 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; 1757 1758 } else if (array_key_exists('DEFLATE_POST', $section_text) && !empty($section_text['DEFLATE_POST'])) { 1759 $post = trim($section_text['DEFLATE_POST']); 1760 $post = gzcompress($post, 9); 1761 $env['HTTP_CONTENT_ENCODING'] = 'deflate'; 1762 save_text($tmp_post, $post); 1763 $content_length = strlen($post); 1764 1765 $env['REQUEST_METHOD'] = 'POST'; 1766 $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1767 $env['CONTENT_LENGTH'] = $content_length; 1768 1769 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; 1770 1771 1772 } else { 1773 1774 $env['REQUEST_METHOD'] = 'GET'; 1775 $env['CONTENT_TYPE'] = ''; 1776 $env['CONTENT_LENGTH'] = ''; 1777 1778 $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args 2>&1"; 1779 } 1780 1781 if ($leak_check) { 1782 $env['USE_ZEND_ALLOC'] = '0'; 1783 $env['ZEND_DONT_UNLOAD_MODULES'] = 1; 1784 1785 if (version_compare($valgrind_version, '3.3.0', '>=')) { 1786 /* valgrind 3.3.0+ doesn't have --log-file-exactly option */ 1787 $cmd = "valgrind -q --tool=memcheck --trace-children=yes --log-file=$memcheck_filename $cmd"; 1788 } else { 1789 $cmd = "valgrind -q --tool=memcheck --trace-children=yes --log-file-exactly=$memcheck_filename $cmd"; 1790 } 1791 1792 } else { 1793 $env['USE_ZEND_ALLOC'] = '1'; 1794 $env['ZEND_DONT_UNLOAD_MODULES'] = 0; 1795 } 1796 1797 if ($DETAILED) echo " 1798CONTENT_LENGTH = " . $env['CONTENT_LENGTH'] . " 1799CONTENT_TYPE = " . $env['CONTENT_TYPE'] . " 1800PATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . " 1801QUERY_STRING = " . $env['QUERY_STRING'] . " 1802REDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . " 1803REQUEST_METHOD = " . $env['REQUEST_METHOD'] . " 1804SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . " 1805HTTP_COOKIE = " . $env['HTTP_COOKIE'] . " 1806COMMAND $cmd 1807"; 1808 1809 junit_start_timer($shortname); 1810 1811 $out = system_with_timeout($cmd, $env, isset($section_text['STDIN']) ? $section_text['STDIN'] : null); 1812 1813 junit_finish_timer($shortname); 1814 1815 if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) { 1816 1817 if (trim($section_text['CLEAN'])) { 1818 show_file_block('clean', $section_text['CLEAN']); 1819 save_text($test_clean, trim($section_text['CLEAN']), $temp_clean); 1820 1821 if (!$no_clean) { 1822 $clean_params = array(); 1823 settings2array($ini_overwrites, $clean_params); 1824 settings2params($clean_params); 1825 $extra = substr(PHP_OS, 0, 3) !== "WIN" ? 1826 "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; 1827 system_with_timeout("$extra $php $pass_options -q $clean_params \"$test_clean\"", $env); 1828 } 1829 1830 if (!$cfg['keep']['clean']) { 1831 @unlink($test_clean); 1832 } 1833 } 1834 } 1835 1836 @unlink($tmp_post); 1837 1838 $leaked = false; 1839 $passed = false; 1840 1841 if ($leak_check) { // leak check 1842 $leaked = filesize($memcheck_filename) > 0; 1843 1844 if (!$leaked) { 1845 @unlink($memcheck_filename); 1846 } 1847 } 1848 1849 // Does the output match what is expected? 1850 $output = preg_replace("/\r\n/", "\n", trim($out)); 1851 1852 /* when using CGI, strip the headers from the output */ 1853 $headers = ""; 1854 1855 if (isset($old_php) && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { 1856 $output = trim($match[2]); 1857 $rh = preg_split("/[\n\r]+/", $match[1]); 1858 $headers = array(); 1859 1860 foreach ($rh as $line) { 1861 if (strpos($line, ':') !== false) { 1862 $line = explode(':', $line, 2); 1863 $headers[trim($line[0])] = trim($line[1]); 1864 } 1865 } 1866 } 1867 1868 $failed_headers = false; 1869 1870 if (isset($section_text['EXPECTHEADERS'])) { 1871 $want = array(); 1872 $wanted_headers = array(); 1873 $lines = preg_split("/[\n\r]+/", $section_text['EXPECTHEADERS']); 1874 1875 foreach($lines as $line) { 1876 if (strpos($line, ':') !== false) { 1877 $line = explode(':', $line, 2); 1878 $want[trim($line[0])] = trim($line[1]); 1879 $wanted_headers[] = trim($line[0]) . ': ' . trim($line[1]); 1880 } 1881 } 1882 1883 $org_headers = $headers; 1884 $headers = array(); 1885 $output_headers = array(); 1886 1887 foreach($want as $k => $v) { 1888 1889 if (isset($org_headers[$k])) { 1890 $headers = $org_headers[$k]; 1891 $output_headers[] = $k . ': ' . $org_headers[$k]; 1892 } 1893 1894 if (!isset($org_headers[$k]) || $org_headers[$k] != $v) { 1895 $failed_headers = true; 1896 } 1897 } 1898 1899 ksort($wanted_headers); 1900 $wanted_headers = join("\n", $wanted_headers); 1901 ksort($output_headers); 1902 $output_headers = join("\n", $output_headers); 1903 } 1904 1905 show_file_block('out', $output); 1906 1907 if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { 1908 1909 if (isset($section_text['EXPECTF'])) { 1910 $wanted = trim($section_text['EXPECTF']); 1911 } else { 1912 $wanted = trim($section_text['EXPECTREGEX']); 1913 } 1914 1915 show_file_block('exp', $wanted); 1916 $wanted_re = preg_replace('/\r\n/', "\n", $wanted); 1917 1918 if (isset($section_text['EXPECTF'])) { 1919 1920 // do preg_quote, but miss out any %r delimited sections 1921 $temp = ""; 1922 $r = "%r"; 1923 $startOffset = 0; 1924 $length = strlen($wanted_re); 1925 while($startOffset < $length) { 1926 $start = strpos($wanted_re, $r, $startOffset); 1927 if ($start !== false) { 1928 // we have found a start tag 1929 $end = strpos($wanted_re, $r, $start+2); 1930 if ($end === false) { 1931 // unbalanced tag, ignore it. 1932 $end = $start = $length; 1933 } 1934 } else { 1935 // no more %r sections 1936 $start = $end = $length; 1937 } 1938 // quote a non re portion of the string 1939 $temp = $temp . preg_quote(substr($wanted_re, $startOffset, ($start - $startOffset)), '/'); 1940 // add the re unquoted. 1941 if ($end > $start) { 1942 $temp = $temp . '(' . substr($wanted_re, $start+2, ($end - $start-2)). ')'; 1943 } 1944 $startOffset = $end + 2; 1945 } 1946 $wanted_re = $temp; 1947 1948 $wanted_re = str_replace( 1949 array('%binary_string_optional%'), 1950 'string', 1951 $wanted_re 1952 ); 1953 $wanted_re = str_replace( 1954 array('%unicode_string_optional%'), 1955 'string', 1956 $wanted_re 1957 ); 1958 $wanted_re = str_replace( 1959 array('%unicode\|string%', '%string\|unicode%'), 1960 'string', 1961 $wanted_re 1962 ); 1963 $wanted_re = str_replace( 1964 array('%u\|b%', '%b\|u%'), 1965 '', 1966 $wanted_re 1967 ); 1968 // Stick to basics 1969 $wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re); 1970 $wanted_re = str_replace('%s', '[^\r\n]+', $wanted_re); 1971 $wanted_re = str_replace('%S', '[^\r\n]*', $wanted_re); 1972 $wanted_re = str_replace('%a', '.+', $wanted_re); 1973 $wanted_re = str_replace('%A', '.*', $wanted_re); 1974 $wanted_re = str_replace('%w', '\s*', $wanted_re); 1975 $wanted_re = str_replace('%i', '[+-]?\d+', $wanted_re); 1976 $wanted_re = str_replace('%d', '\d+', $wanted_re); 1977 $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re); 1978 $wanted_re = str_replace('%f', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re); 1979 $wanted_re = str_replace('%c', '.', $wanted_re); 1980 // %f allows two points "-.0.0" but that is the best *simple* expression 1981 } 1982/* DEBUG YOUR REGEX HERE 1983 var_dump($wanted_re); 1984 print(str_repeat('=', 80) . "\n"); 1985 var_dump($output); 1986*/ 1987 if (preg_match("/^$wanted_re\$/s", $output)) { 1988 $passed = true; 1989 if (!$cfg['keep']['php']) { 1990 @unlink($test_file); 1991 } 1992 if (isset($old_php)) { 1993 $php = $old_php; 1994 } 1995 1996 if (!$leaked && !$failed_headers) { 1997 if (isset($section_text['XFAIL'] )) { 1998 $warn = true; 1999 $info = " (warn: XFAIL section but test passes)"; 2000 }else { 2001 show_result("PASS", $tested, $tested_file, '', $temp_filenames); 2002 junit_mark_test_as('PASS', $shortname, $tested); 2003 return 'PASSED'; 2004 } 2005 } 2006 } 2007 2008 } else { 2009 2010 $wanted = trim($section_text['EXPECT']); 2011 $wanted = preg_replace('/\r\n/',"\n", $wanted); 2012 show_file_block('exp', $wanted); 2013 2014 // compare and leave on success 2015 if (!strcmp($output, $wanted)) { 2016 $passed = true; 2017 2018 if (!$cfg['keep']['php']) { 2019 @unlink($test_file); 2020 } 2021 2022 if (isset($old_php)) { 2023 $php = $old_php; 2024 } 2025 2026 if (!$leaked && !$failed_headers) { 2027 if (isset($section_text['XFAIL'] )) { 2028 $warn = true; 2029 $info = " (warn: XFAIL section but test passes)"; 2030 }else { 2031 show_result("PASS", $tested, $tested_file, '', $temp_filenames); 2032 junit_mark_test_as('PASS', $shortname, $tested); 2033 return 'PASSED'; 2034 } 2035 } 2036 } 2037 2038 $wanted_re = null; 2039 } 2040 2041 // Test failed so we need to report details. 2042 if ($failed_headers) { 2043 $passed = false; 2044 $wanted = $wanted_headers . "\n--HEADERS--\n" . $wanted; 2045 $output = $output_headers . "\n--HEADERS--\n" . $output; 2046 2047 if (isset($wanted_re)) { 2048 $wanted_re = preg_quote($wanted_headers . "\n--HEADERS--\n", '/') . $wanted_re; 2049 } 2050 } 2051 2052 if ($leaked) { 2053 $restype[] = 'LEAK'; 2054 } 2055 2056 if ($warn) { 2057 $restype[] = 'WARN'; 2058 } 2059 2060 if (!$passed) { 2061 if (isset($section_text['XFAIL'])) { 2062 $restype[] = 'XFAIL'; 2063 $info = ' XFAIL REASON: ' . rtrim($section_text['XFAIL']); 2064 } else { 2065 $restype[] = 'FAIL'; 2066 } 2067 } 2068 2069 if (!$passed) { 2070 2071 // write .exp 2072 if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted, FILE_BINARY) === false) { 2073 error("Cannot create expected test output - $exp_filename"); 2074 } 2075 2076 // write .out 2077 if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output, FILE_BINARY) === false) { 2078 error("Cannot create test output - $output_filename"); 2079 } 2080 2081 // write .diff 2082 $diff = generate_diff($wanted, $wanted_re, $output); 2083 if (is_array($IN_REDIRECT)) { 2084 $diff = "# original source file: $shortname\n" . $diff; 2085 } 2086 show_file_block('diff', $diff); 2087 if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff, FILE_BINARY) === false) { 2088 error("Cannot create test diff - $diff_filename"); 2089 } 2090 2091 // write .sh 2092 if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, "#!/bin/sh 2093 2094{$cmd} 2095", FILE_BINARY) === false) { 2096 error("Cannot create test shell script - $sh_filename"); 2097 } 2098 chmod($sh_filename, 0755); 2099 2100 // write .log 2101 if (strpos($log_format, 'L') !== false && file_put_contents($log_filename, " 2102---- EXPECTED OUTPUT 2103$wanted 2104---- ACTUAL OUTPUT 2105$output 2106---- FAILED 2107", FILE_BINARY) === false) { 2108 error("Cannot create test log - $log_filename"); 2109 error_report($file, $log_filename, $tested); 2110 } 2111 } 2112 2113 show_result(implode('&', $restype), $tested, $tested_file, $info, $temp_filenames); 2114 2115 foreach ($restype as $type) { 2116 $PHP_FAILED_TESTS[$type.'ED'][] = array ( 2117 'name' => $file, 2118 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 2119 'output' => $output_filename, 2120 'diff' => $diff_filename, 2121 'info' => $info, 2122 ); 2123 } 2124 2125 if (isset($old_php)) { 2126 $php = $old_php; 2127 } 2128 2129 $diff = empty($diff) ? '' : preg_replace('/\e/', '<esc>', $diff); 2130 2131 junit_mark_test_as($restype, str_replace($cwd . '/', '', $tested_file), $tested, null, $info, $diff); 2132 2133 return $restype[0] . 'ED'; 2134} 2135 2136function comp_line($l1, $l2, $is_reg) 2137{ 2138 if ($is_reg) { 2139 return preg_match('/^'. $l1 . '$/s', $l2); 2140 } else { 2141 return !strcmp($l1, $l2); 2142 } 2143} 2144 2145function count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2, $cnt1, $cnt2, $steps) 2146{ 2147 $equal = 0; 2148 2149 while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { 2150 $idx1++; 2151 $idx2++; 2152 $equal++; 2153 $steps--; 2154 } 2155 if (--$steps > 0) { 2156 $eq1 = 0; 2157 $st = $steps / 2; 2158 2159 for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) { 2160 $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st); 2161 2162 if ($eq > $eq1) { 2163 $eq1 = $eq; 2164 } 2165 } 2166 2167 $eq2 = 0; 2168 $st = $steps; 2169 2170 for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) { 2171 $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st); 2172 if ($eq > $eq2) { 2173 $eq2 = $eq; 2174 } 2175 } 2176 2177 if ($eq1 > $eq2) { 2178 $equal += $eq1; 2179 } else if ($eq2 > 0) { 2180 $equal += $eq2; 2181 } 2182 } 2183 2184 return $equal; 2185} 2186 2187function generate_array_diff($ar1, $ar2, $is_reg, $w) 2188{ 2189 $idx1 = 0; $ofs1 = 0; $cnt1 = @count($ar1); 2190 $idx2 = 0; $ofs2 = 0; $cnt2 = @count($ar2); 2191 $diff = array(); 2192 $old1 = array(); 2193 $old2 = array(); 2194 2195 while ($idx1 < $cnt1 && $idx2 < $cnt2) { 2196 2197 if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { 2198 $idx1++; 2199 $idx2++; 2200 continue; 2201 } else { 2202 2203 $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1+1, $idx2, $cnt1, $cnt2, 10); 2204 $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2+1, $cnt1, $cnt2, 10); 2205 2206 if ($c1 > $c2) { 2207 $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; 2208 $last = 1; 2209 } else if ($c2 > 0) { 2210 $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; 2211 $last = 2; 2212 } else { 2213 $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; 2214 $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; 2215 } 2216 } 2217 } 2218 2219 reset($old1); $k1 = key($old1); $l1 = -2; 2220 reset($old2); $k2 = key($old2); $l2 = -2; 2221 2222 while ($k1 !== null || $k2 !== null) { 2223 2224 if ($k1 == $l1 + 1 || $k2 === null) { 2225 $l1 = $k1; 2226 $diff[] = current($old1); 2227 $k1 = next($old1) ? key($old1) : null; 2228 } else if ($k2 == $l2 + 1 || $k1 === null) { 2229 $l2 = $k2; 2230 $diff[] = current($old2); 2231 $k2 = next($old2) ? key($old2) : null; 2232 } else if ($k1 < $k2) { 2233 $l1 = $k1; 2234 $diff[] = current($old1); 2235 $k1 = next($old1) ? key($old1) : null; 2236 } else { 2237 $l2 = $k2; 2238 $diff[] = current($old2); 2239 $k2 = next($old2) ? key($old2) : null; 2240 } 2241 } 2242 2243 while ($idx1 < $cnt1) { 2244 $diff[] = sprintf("%03d- ", $idx1 + 1) . $w[$idx1++]; 2245 } 2246 2247 while ($idx2 < $cnt2) { 2248 $diff[] = sprintf("%03d+ ", $idx2 + 1) . $ar2[$idx2++]; 2249 } 2250 2251 return $diff; 2252} 2253 2254function generate_diff($wanted, $wanted_re, $output) 2255{ 2256 $w = explode("\n", $wanted); 2257 $o = explode("\n", $output); 2258 $r = is_null($wanted_re) ? $w : explode("\n", $wanted_re); 2259 $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w); 2260 2261 return implode("\r\n", $diff); 2262} 2263 2264function error($message) 2265{ 2266 echo "ERROR: {$message}\n"; 2267 exit(1); 2268} 2269 2270function settings2array($settings, &$ini_settings) 2271{ 2272 foreach($settings as $setting) { 2273 2274 if (strpos($setting, '=') !== false) { 2275 $setting = explode("=", $setting, 2); 2276 $name = trim($setting[0]); 2277 $value = trim($setting[1]); 2278 2279 if ($name == 'extension') { 2280 2281 if (!isset($ini_settings[$name])) { 2282 $ini_settings[$name] = array(); 2283 } 2284 2285 $ini_settings[$name][] = $value; 2286 2287 } else { 2288 $ini_settings[$name] = $value; 2289 } 2290 } 2291 } 2292} 2293 2294function settings2params(&$ini_settings) 2295{ 2296 $settings = ''; 2297 2298 foreach($ini_settings as $name => $value) { 2299 2300 if (is_array($value)) { 2301 foreach($value as $val) { 2302 $val = addslashes($val); 2303 $settings .= " -d \"$name=$val\""; 2304 } 2305 } else { 2306 if (substr(PHP_OS, 0, 3) == "WIN" && !empty($value) && $value{0} == '"') { 2307 $len = strlen($value); 2308 2309 if ($value{$len - 1} == '"') { 2310 $value{0} = "'"; 2311 $value{$len - 1} = "'"; 2312 } 2313 } else { 2314 $value = addslashes($value); 2315 } 2316 2317 $settings .= " -d \"$name=$value\""; 2318 } 2319 } 2320 2321 $ini_settings = $settings; 2322} 2323 2324function compute_summary() 2325{ 2326 global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results; 2327 2328 $n_total = count($test_results); 2329 $n_total += $ignored_by_ext; 2330 $sum_results = array( 2331 'PASSED' => 0, 2332 'WARNED' => 0, 2333 'SKIPPED' => 0, 2334 'FAILED' => 0, 2335 'BORKED' => 0, 2336 'LEAKED' => 0, 2337 'XFAILED' => 0 2338 ); 2339 2340 foreach ($test_results as $v) { 2341 $sum_results[$v]++; 2342 } 2343 2344 $sum_results['SKIPPED'] += $ignored_by_ext; 2345 $percent_results = array(); 2346 2347 while (list($v, $n) = each($sum_results)) { 2348 $percent_results[$v] = (100.0 * $n) / $n_total; 2349 } 2350} 2351 2352function get_summary($show_ext_summary, $show_html) 2353{ 2354 global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $leak_check; 2355 2356 $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED']; 2357 2358 if ($x_total) { 2359 $x_warned = (100.0 * $sum_results['WARNED']) / $x_total; 2360 $x_failed = (100.0 * $sum_results['FAILED']) / $x_total; 2361 $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total; 2362 $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total; 2363 $x_passed = (100.0 * $sum_results['PASSED']) / $x_total; 2364 } else { 2365 $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = 0; 2366 } 2367 2368 $summary = ''; 2369 2370 if ($show_html) { 2371 $summary .= "<pre>\n"; 2372 } 2373 2374 if ($show_ext_summary) { 2375 $summary .= ' 2376===================================================================== 2377TEST RESULT SUMMARY 2378--------------------------------------------------------------------- 2379Exts skipped : ' . sprintf('%4d', $exts_skipped) . ' 2380Exts tested : ' . sprintf('%4d', $exts_tested) . ' 2381--------------------------------------------------------------------- 2382'; 2383 } 2384 2385 $summary .= ' 2386Number of tests : ' . sprintf('%4d', $n_total) . ' ' . sprintf('%8d', $x_total); 2387 2388 if ($sum_results['BORKED']) { 2389 $summary .= ' 2390Tests borked : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------'; 2391 } 2392 2393 $summary .= ' 2394Tests skipped : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' -------- 2395Tests warned : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . ' 2396Tests failed : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed) . ' 2397Expected fail : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed); 2398 2399 if ($leak_check) { 2400 $summary .= ' 2401Tests leaked : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked); 2402 } 2403 2404 $summary .= ' 2405Tests passed : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . ' 2406--------------------------------------------------------------------- 2407Time taken : ' . sprintf('%4d seconds', $end_time - $start_time) . ' 2408===================================================================== 2409'; 2410 $failed_test_summary = ''; 2411 2412 if (count($PHP_FAILED_TESTS['XFAILED'])) { 2413 $failed_test_summary .= ' 2414===================================================================== 2415EXPECTED FAILED TEST SUMMARY 2416--------------------------------------------------------------------- 2417'; 2418 foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) { 2419 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2420 } 2421 $failed_test_summary .= "=====================================================================\n"; 2422 } 2423 2424 if (count($PHP_FAILED_TESTS['BORKED'])) { 2425 $failed_test_summary .= ' 2426===================================================================== 2427BORKED TEST SUMMARY 2428--------------------------------------------------------------------- 2429'; 2430 foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) { 2431 $failed_test_summary .= $failed_test_data['info'] . "\n"; 2432 } 2433 2434 $failed_test_summary .= "=====================================================================\n"; 2435 } 2436 2437 if (count($PHP_FAILED_TESTS['FAILED'])) { 2438 $failed_test_summary .= ' 2439===================================================================== 2440FAILED TEST SUMMARY 2441--------------------------------------------------------------------- 2442'; 2443 foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) { 2444 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2445 } 2446 $failed_test_summary .= "=====================================================================\n"; 2447 } 2448 if (count($PHP_FAILED_TESTS['WARNED'])) { 2449 $failed_test_summary .= ' 2450===================================================================== 2451WARNED TEST SUMMARY 2452--------------------------------------------------------------------- 2453'; 2454 foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) { 2455 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2456 } 2457 2458 $failed_test_summary .= "=====================================================================\n"; 2459 } 2460 2461 if (count($PHP_FAILED_TESTS['LEAKED'])) { 2462 $failed_test_summary .= ' 2463===================================================================== 2464LEAKED TEST SUMMARY 2465--------------------------------------------------------------------- 2466'; 2467 foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) { 2468 $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n"; 2469 } 2470 2471 $failed_test_summary .= "=====================================================================\n"; 2472 } 2473 2474 if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) { 2475 $summary .= $failed_test_summary; 2476 } 2477 2478 if ($show_html) { 2479 $summary .= "</pre>"; 2480 } 2481 2482 return $summary; 2483} 2484 2485function show_start($start_time) 2486{ 2487 global $html_output, $html_file; 2488 2489 if ($html_output) { 2490 fwrite($html_file, "<h2>Time Start: " . date('Y-m-d H:i:s', $start_time) . "</h2>\n"); 2491 fwrite($html_file, "<table>\n"); 2492 } 2493 2494 echo "TIME START " . date('Y-m-d H:i:s', $start_time) . "\n=====================================================================\n"; 2495} 2496 2497function show_end($end_time) 2498{ 2499 global $html_output, $html_file; 2500 2501 if ($html_output) { 2502 fwrite($html_file, "</table>\n"); 2503 fwrite($html_file, "<h2>Time End: " . date('Y-m-d H:i:s', $end_time) . "</h2>\n"); 2504 } 2505 2506 echo "=====================================================================\nTIME END " . date('Y-m-d H:i:s', $end_time) . "\n"; 2507} 2508 2509function show_summary() 2510{ 2511 global $html_output, $html_file; 2512 2513 if ($html_output) { 2514 fwrite($html_file, "<hr/>\n" . get_summary(true, true)); 2515 } 2516 2517 echo get_summary(true, false); 2518} 2519 2520function show_redirect_start($tests, $tested, $tested_file) 2521{ 2522 global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; 2523 2524 if ($html_output) { 2525 fwrite($html_file, "<tr><td colspan='3'>---> $tests ($tested [$tested_file]) begin</td></tr>\n"); 2526 } 2527 2528 if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { 2529 echo "REDIRECT $tests ($tested [$tested_file]) begin\n"; 2530 } else { 2531 // Write over the last line to avoid random trailing chars on next echo 2532 echo str_repeat(" ", $line_length), "\r"; 2533 } 2534} 2535 2536function show_redirect_ends($tests, $tested, $tested_file) 2537{ 2538 global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; 2539 2540 if ($html_output) { 2541 fwrite($html_file, "<tr><td colspan='3'>---> $tests ($tested [$tested_file]) done</td></tr>\n"); 2542 } 2543 2544 if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { 2545 echo "REDIRECT $tests ($tested [$tested_file]) done\n"; 2546 } else { 2547 // Write over the last line to avoid random trailing chars on next echo 2548 echo str_repeat(" ", $line_length), "\r"; 2549 } 2550} 2551 2552function show_test($test_idx, $shortname) 2553{ 2554 global $test_cnt; 2555 global $line_length; 2556 2557 $str = "TEST $test_idx/$test_cnt [$shortname]\r"; 2558 $line_length = strlen($str); 2559 echo $str; 2560 flush(); 2561} 2562 2563function show_result($result, $tested, $tested_file, $extra = '', $temp_filenames = null) 2564{ 2565 global $html_output, $html_file, $temp_target, $temp_urlbase, $line_length, $SHOW_ONLY_GROUPS; 2566 2567 if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { 2568 echo "$result $tested [$tested_file] $extra\n"; 2569 } else { 2570 // Write over the last line to avoid random trailing chars on next echo 2571 echo str_repeat(" ", $line_length), "\r"; 2572 } 2573 2574 if ($html_output) { 2575 2576 if (isset($temp_filenames['file']) && file_exists($temp_filenames['file'])) { 2577 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['file']); 2578 $tested = "<a href='$url'>$tested</a>"; 2579 } 2580 2581 if (isset($temp_filenames['skip']) && file_exists($temp_filenames['skip'])) { 2582 2583 if (empty($extra)) { 2584 $extra = "skipif"; 2585 } 2586 2587 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['skip']); 2588 $extra = "<a href='$url'>$extra</a>"; 2589 2590 } else if (empty($extra)) { 2591 $extra = " "; 2592 } 2593 2594 if (isset($temp_filenames['diff']) && file_exists($temp_filenames['diff'])) { 2595 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['diff']); 2596 $diff = "<a href='$url'>diff</a>"; 2597 } else { 2598 $diff = " "; 2599 } 2600 2601 if (isset($temp_filenames['mem']) && file_exists($temp_filenames['mem'])) { 2602 $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['mem']); 2603 $mem = "<a href='$url'>leaks</a>"; 2604 } else { 2605 $mem = " "; 2606 } 2607 2608 fwrite($html_file, 2609 "<tr>" . 2610 "<td>$result</td>" . 2611 "<td>$tested</td>" . 2612 "<td>$extra</td>" . 2613 "<td>$diff</td>" . 2614 "<td>$mem</td>" . 2615 "</tr>\n"); 2616 } 2617} 2618 2619function junit_init() { 2620 // Check whether a junit log is wanted. 2621 $JUNIT = getenv('TEST_PHP_JUNIT'); 2622 if (empty($JUNIT)) { 2623 $JUNIT = FALSE; 2624 } elseif (!$fp = fopen($JUNIT, 'w')) { 2625 error("Failed to open $JUNIT for writing."); 2626 } else { 2627 $JUNIT = array( 2628 'fp' => $fp, 2629 'name' => 'php-src', 2630 'test_total' => 0, 2631 'test_pass' => 0, 2632 'test_fail' => 0, 2633 'test_error' => 0, 2634 'test_skip' => 0, 2635 'execution_time'=> 0, 2636 'suites' => array(), 2637 'files' => array() 2638 ); 2639 } 2640 2641 $GLOBALS['JUNIT'] = $JUNIT; 2642} 2643 2644function junit_save_xml() { 2645 global $JUNIT; 2646 if (!junit_enabled()) return; 2647 2648 $xml = '<?xml version="1.0" encoding="UTF-8"?>'. PHP_EOL . 2649 '<testsuites>' . PHP_EOL; 2650 $xml .= junit_get_suite_xml(); 2651 $xml .= '</testsuites>'; 2652 fwrite($JUNIT['fp'], $xml); 2653} 2654 2655function junit_get_suite_xml($suite_name = '') { 2656 global $JUNIT; 2657 2658 $suite = $suite_name ? $JUNIT['suites'][$suite_name] : $JUNIT; 2659 2660 $result = sprintf( 2661 '<testsuite name="%s" tests="%s" failures="%d" errors="%d" skip="%d" time="%s">' . PHP_EOL, 2662 $suite['name'], $suite['test_total'], $suite['test_fail'], $suite['test_error'], $suite['test_skip'], 2663 $suite['execution_time'] 2664 ); 2665 2666 foreach($suite['suites'] as $sub_suite) { 2667 $result .= junit_get_suite_xml($sub_suite['name']); 2668 } 2669 2670 // Output files only in subsuites 2671 if (!empty($suite_name)) { 2672 foreach($suite['files'] as $file) { 2673 $result .= $JUNIT['files'][$file]['xml']; 2674 } 2675 } 2676 2677 $result .= '</testsuite>' . PHP_EOL; 2678 2679 return $result; 2680} 2681 2682function junit_enabled() { 2683 global $JUNIT; 2684 return !empty($JUNIT); 2685} 2686 2687/** 2688 * @param array|string $type 2689 * @param string $file_name 2690 * @param string $test_name 2691 * @param int|string $time 2692 * @param string $message 2693 * @param string $details 2694 * @return void 2695 */ 2696function junit_mark_test_as($type, $file_name, $test_name, $time = null, $message = '', $details = '') { 2697 global $JUNIT; 2698 if (!junit_enabled()) return; 2699 2700 $suite = junit_get_suitename_for($file_name); 2701 2702 junit_suite_record($suite, 'test_total'); 2703 2704 $time = null !== $time ? $time : junit_get_timer($file_name); 2705 junit_suite_record($suite, 'execution_time', $time); 2706 2707 $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8'); 2708 $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function ($c) { 2709 return sprintf('[[0x%02x]]', ord($c[0])); 2710 }, $escaped_details); 2711 $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); 2712 2713 $escaped_test_name = basename($file_name) . ' - ' . htmlspecialchars($test_name, ENT_QUOTES); 2714 $JUNIT['files'][$file_name]['xml'] = "<testcase classname='$suite' name='$escaped_test_name' time='$time'>\n"; 2715 2716 if (is_array($type)) { 2717 $output_type = $type[0] . 'ED'; 2718 $temp = array_intersect(array('XFAIL', 'FAIL', 'WARN'), $type); 2719 $type = reset($temp); 2720 } else { 2721 $output_type = $type . 'ED'; 2722 } 2723 2724 if ('PASS' == $type || 'XFAIL' == $type) { 2725 junit_suite_record($suite, 'test_pass'); 2726 } elseif ('BORK' == $type) { 2727 junit_suite_record($suite, 'test_error'); 2728 $JUNIT['files'][$file_name]['xml'] .= "<error type='$output_type' message='$escaped_message'/>\n"; 2729 } elseif ('SKIP' == $type) { 2730 junit_suite_record($suite, 'test_skip'); 2731 $JUNIT['files'][$file_name]['xml'] .= "<skipped>$escaped_message</skipped>\n"; 2732 } elseif ('WARN' == $type) { 2733 junit_suite_record($suite, 'test_warn'); 2734 $JUNIT['files'][$file_name]['xml'] .= "<warning>$escaped_message</warning>\n"; 2735 } elseif('FAIL' == $type) { 2736 junit_suite_record($suite, 'test_fail'); 2737 $JUNIT['files'][$file_name]['xml'] .= "<failure type='$output_type' message='$escaped_message'>$escaped_details</failure>\n"; 2738 } else { 2739 junit_suite_record($suite, 'test_error'); 2740 $JUNIT['files'][$file_name]['xml'] .= "<error type='$output_type' message='$escaped_message'>$escaped_details</error>\n"; 2741 } 2742 2743 $JUNIT['files'][$file_name]['xml'] .= "</testcase>\n"; 2744 2745} 2746 2747function junit_suite_record($suite, $param, $value = 1) { 2748 global $JUNIT; 2749 2750 $JUNIT[$param] += $value; 2751 $JUNIT['suites'][$suite][$param] += $value; 2752} 2753 2754function junit_get_timer($file_name) { 2755 global $JUNIT; 2756 if (!junit_enabled()) return 0; 2757 2758 if (isset($JUNIT['files'][$file_name]['total'])) { 2759 return number_format($JUNIT['files'][$file_name]['total'], 4); 2760 } 2761 2762 return 0; 2763} 2764 2765function junit_start_timer($file_name) { 2766 global $JUNIT; 2767 if (!junit_enabled()) return; 2768 2769 if (!isset($JUNIT['files'][$file_name]['start'])) { 2770 $JUNIT['files'][$file_name]['start'] = microtime(true); 2771 2772 $suite = junit_get_suitename_for($file_name); 2773 junit_init_suite($suite); 2774 $JUNIT['suites'][$suite]['files'][$file_name] = $file_name; 2775 } 2776} 2777 2778function junit_get_suitename_for($file_name) { 2779 return junit_path_to_classname(dirname($file_name)); 2780} 2781 2782function junit_path_to_classname($file_name) { 2783 global $JUNIT; 2784 return $JUNIT['name'] . '.' . str_replace(DIRECTORY_SEPARATOR, '.', $file_name); 2785} 2786 2787function junit_init_suite($suite_name) { 2788 global $JUNIT; 2789 if (!junit_enabled()) return; 2790 2791 if (!empty($JUNIT['suites'][$suite_name])) { 2792 return; 2793 } 2794 2795 $JUNIT['suites'][$suite_name] = array( 2796 'name' => $suite_name, 2797 'test_total' => 0, 2798 'test_pass' => 0, 2799 'test_fail' => 0, 2800 'test_error' => 0, 2801 'test_skip' => 0, 2802 'suites' => array(), 2803 'files' => array(), 2804 'execution_time'=> 0, 2805 ); 2806} 2807 2808function junit_finish_timer($file_name) { 2809 global $JUNIT; 2810 if (!junit_enabled()) return; 2811 2812 if (!isset($JUNIT['files'][$file_name]['start'])) { 2813 error("Timer for $file_name was not started!"); 2814 } 2815 2816 if (!isset($JUNIT['files'][$file_name]['total'])) { 2817 $JUNIT['files'][$file_name]['total'] = 0; 2818 } 2819 2820 $start = $JUNIT['files'][$file_name]['start']; 2821 $JUNIT['files'][$file_name]['total'] += microtime(true) - $start; 2822 unset($JUNIT['files'][$file_name]['start']); 2823} 2824 2825/* 2826 * Local variables: 2827 * tab-width: 4 2828 * c-basic-offset: 4 2829 * End: 2830 * vim: noet sw=4 ts=4 2831 */ 2832?> 2833