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