PEAR
[ class tree: PEAR ] [ index: PEAR ] [ all elements ]

Source for file RunTest.php

Documentation is available at RunTest.php

  1. <?php
  2. /**
  3.  * PEAR_RunTest
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * @category   pear
  8.  * @package    PEAR
  9.  * @author     Tomas V.V.Cox <cox@idecnet.com>
  10.  * @author     Greg Beaver <cellog@php.net>
  11.  * @copyright  1997-2009 The Authors
  12.  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
  13.  * @version    CVS: $Id: RunTest.php 313024 2011-07-06 19:51:24Z dufuz $
  14.  * @link       http://pear.php.net/package/PEAR
  15.  * @since      File available since Release 1.3.3
  16.  */
  17.  
  18. /**
  19.  * for error handling
  20.  */
  21. require_once 'PEAR.php';
  22. require_once 'PEAR/Config.php';
  23.  
  24. define('DETAILED'1);
  25. putenv("PHP_PEAR_RUNTESTS=1");
  26.  
  27. /**
  28.  * Simplified version of PHP's test suite
  29.  *
  30.  * Try it with:
  31.  *
  32.  * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
  33.  *
  34.  *
  35.  * @category   pear
  36.  * @package    PEAR
  37.  * @author     Tomas V.V.Cox <cox@idecnet.com>
  38.  * @author     Greg Beaver <cellog@php.net>
  39.  * @copyright  1997-2009 The Authors
  40.  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
  41.  * @version    Release: 1.9.4
  42.  * @link       http://pear.php.net/package/PEAR
  43.  * @since      Class available since Release 1.3.3
  44.  */
  45. {
  46.     var $_headers = array();
  47.     var $_logger;
  48.     var $_options;
  49.     var $_php;
  50.     var $tests_count;
  51.     var $xdebug_loaded;
  52.     /**
  53.      * Saved value of php executable, used to reset $_php when we
  54.      * have a test that uses cgi
  55.      *
  56.      * @var unknown_type 
  57.      */
  58.     var $_savephp;
  59.     var $ini_overwrites = array(
  60.         'output_handler=',
  61.         'open_basedir=',
  62.         'safe_mode=0',
  63.         'disable_functions=',
  64.         'output_buffering=Off',
  65.         'display_errors=1',
  66.         'log_errors=0',
  67.         'html_errors=0',
  68.         'track_errors=1',
  69.         'report_memleaks=0',
  70.         'report_zend_debug=0',
  71.         'docref_root=',
  72.         'docref_ext=.html',
  73.         'error_prepend_string=',
  74.         'error_append_string=',
  75.         'auto_prepend_file=',
  76.         'auto_append_file=',
  77.         'magic_quotes_runtime=0',
  78.         'xdebug.default_enable=0',
  79.         'allow_url_fopen=1',
  80.     );
  81.  
  82.     /**
  83.      * An object that supports the PEAR_Common->log() signature, or null
  84.      * @param PEAR_Common|null
  85.      */
  86.     function PEAR_RunTest($logger = null$options = array())
  87.     {
  88.         if (!defined('E_DEPRECATED')) {
  89.             define('E_DEPRECATED'0);
  90.         }
  91.         if (!defined('E_STRICT')) {
  92.             define('E_STRICT'0);
  93.         }
  94.         $this->ini_overwrites['error_reporting=' (E_ALL ~(E_DEPRECATED | E_STRICT));
  95.         if (is_null($logger)) {
  96.             require_once 'PEAR/Common.php';
  97.             $logger = new PEAR_Common;
  98.         }
  99.         $this->_logger  $logger;
  100.         $this->_options $options;
  101.  
  102.         $conf &PEAR_Config::singleton();
  103.         $this->_php $conf->get('php_bin');
  104.     }
  105.  
  106.     /**
  107.      * Taken from php-src/run-tests.php
  108.      *
  109.      * @param string $commandline command name
  110.      * @param array $env 
  111.      * @param string $stdin standard input to pass to the command
  112.      * @return unknown 
  113.      */
  114.     function system_with_timeout($commandline$env = null$stdin = null)
  115.     {
  116.         $data '';
  117.         if (version_compare(phpversion()'5.0.0''<')) {
  118.             $proc proc_open($commandlinearray(
  119.                 0 => array('pipe''r'),
  120.                 1 => array('pipe''w'),
  121.                 2 => array('pipe''w')
  122.                 )$pipes);
  123.         else {
  124.             $proc proc_open($commandlinearray(
  125.                 0 => array('pipe''r'),
  126.                 1 => array('pipe''w'),
  127.                 2 => array('pipe''w')
  128.                 )$pipesnull$envarray('suppress_errors' => true));
  129.         }
  130.  
  131.         if (!$proc{
  132.             return false;
  133.         }
  134.  
  135.         if (is_string($stdin)) {
  136.             fwrite($pipes[0]$stdin);
  137.         }
  138.         fclose($pipes[0]);
  139.  
  140.         while (true{
  141.             /* hide errors from interrupted syscalls */
  142.             $r $pipes;
  143.             $e $w = null;
  144.             $n @stream_select($r$w$e60);
  145.  
  146.             if ($n === 0{
  147.                 /* timed out */
  148.                 $data .= "\n ** ERROR: process timed out **\n";
  149.                 proc_terminate($proc);
  150.                 return array(1234567890$data);
  151.             else if ($n > 0{
  152.                 $line fread($pipes[1]8192);
  153.                 if (strlen($line== 0{
  154.                     /* EOF */
  155.                     break;
  156.                 }
  157.                 $data .= $line;
  158.             }
  159.         }
  160.         if (function_exists('proc_get_status')) {
  161.             $stat proc_get_status($proc);
  162.             if ($stat['signaled']{
  163.                 $data .= "\nTermsig=".$stat['stopsig'];
  164.             }
  165.         }
  166.         $code proc_close($proc);
  167.         if (function_exists('proc_get_status')) {
  168.             $code $stat['exitcode'];
  169.         }
  170.         return array($code$data);
  171.     }
  172.  
  173.     /**
  174.      * Turns a PHP INI string into an array
  175.      *
  176.      * Turns -d "include_path=/foo/bar" into this:
  177.      * array(
  178.      *   'include_path' => array(
  179.      *          'operator' => '-d',
  180.      *          'value'    => '/foo/bar',
  181.      *   )
  182.      * )
  183.      * Works both with quotes and without
  184.      *
  185.      * @param string an PHP INI string, -d "include_path=/foo/bar"
  186.      * @return array 
  187.      */
  188.     function iniString2array($ini_string)
  189.     {
  190.         if (!$ini_string{
  191.             return array();
  192.         }
  193.         $split preg_split('/[\s]|=/'$ini_string-1PREG_SPLIT_NO_EMPTY);
  194.         $key   $split[1][0== '"'                     substr($split[1]1)     $split[1];
  195.         $value $split[2][strlen($split[2]- 1== '"' substr($split[2]0-1$split[2];
  196.         // FIXME review if this is really the struct to go with
  197.         $array = array($key => array('operator' => $split[0]'value' => $value));
  198.         return $array;
  199.     }
  200.  
  201.     function settings2array($settings$ini_settings)
  202.     {
  203.         foreach ($settings as $setting{
  204.             if (strpos($setting'='!== false{
  205.                 $setting explode('='$setting2);
  206.                 $name  trim(strtolower($setting[0]));
  207.                 $value trim($setting[1]);
  208.                 $ini_settings[$name$value;
  209.             }
  210.         }
  211.         return $ini_settings;
  212.     }
  213.  
  214.     function settings2params($ini_settings)
  215.     {
  216.         $settings '';
  217.         foreach ($ini_settings as $name => $value{
  218.             if (is_array($value)) {
  219.                 $operator $value['operator'];
  220.                 $value    $value['value'];
  221.             else {
  222.                 $operator '-d';
  223.             }
  224.             $value addslashes($value);
  225.             $settings .= " $operator \"$name=$value\"";
  226.         }
  227.         return $settings;
  228.     }
  229.  
  230.     function _preparePhpBin($php$file$ini_settings)
  231.     {
  232.         $file escapeshellarg($file);
  233.         // This was fixed in php 5.3 and is not needed after that
  234.         if (OS_WINDOWS && version_compare(PHP_VERSION'5.3''<')) {
  235.             $cmd '"'.escapeshellarg($php).' '.$ini_settings.' -f ' $file .'"';
  236.         else {
  237.             $cmd $php $ini_settings ' -f ' $file;
  238.         }
  239.  
  240.         return $cmd;
  241.     }
  242.  
  243.     function runPHPUnit($file$ini_settings '')
  244.     {
  245.         if (!file_exists($file&& file_exists(getcwd(. DIRECTORY_SEPARATOR . $file)) {
  246.             $file realpath(getcwd(. DIRECTORY_SEPARATOR . $file);
  247.         elseif (file_exists($file)) {
  248.             $file realpath($file);
  249.         }
  250.  
  251.         $cmd $this->_preparePhpBin($this->_php$file$ini_settings);
  252.         if (isset($this->_logger)) {
  253.             $this->_logger->log(2'Running command "' $cmd '"');
  254.         }
  255.  
  256.         $savedir getcwd()// in case the test moves us around
  257.         chdir(dirname($file));
  258.         echo `$cmd`;
  259.         chdir($savedir);
  260.         return 'PASSED'// we have no way of knowing this information so assume passing
  261.     }
  262.  
  263.     /**
  264.      * Runs an individual test case.
  265.      *
  266.      * @param string       The filename of the test
  267.      * @param array|stringINI settings to be applied to the test run
  268.      * @param integer      Number what the current running test is of the
  269.      *                      whole test suite being runned.
  270.      *
  271.      * @return string|object Returns  PASSED, WARNED, FAILED depending on how the
  272.      *                        test came out.
  273.      *                        PEAR Error when the tester it self fails
  274.      */
  275.     function run($file$ini_settings = array()$test_number = 1)
  276.     {
  277.         if (isset($this->_savephp)) {
  278.             $this->_php $this->_savephp;
  279.             unset($this->_savephp);
  280.         }
  281.         if (empty($this->_options['cgi'])) {
  282.             // try to see if php-cgi is in the path
  283.             $res $this->system_with_timeout('php-cgi -v');
  284.             if (false !== $res && !(is_array($res&& in_array($res[0]array(-1127)))) {
  285.                 $this->_options['cgi''php-cgi';
  286.             }
  287.         }
  288.         if (1 < $len strlen($this->tests_count)) {
  289.             $test_number str_pad($test_number$len' 'STR_PAD_LEFT);
  290.             $test_nr = "[$test_number/$this->tests_count";
  291.         else {
  292.             $test_nr '';
  293.         }
  294.  
  295.         $file realpath($file);
  296.         $section_text $this->_readFile($file);
  297.         if (PEAR::isError($section_text)) {
  298.             return $section_text;
  299.         }
  300.  
  301.         if (isset($section_text['POST_RAW']&& isset($section_text['UPLOAD'])) {
  302.             return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file");
  303.         }
  304.  
  305.         $cwd getcwd();
  306.  
  307.         $pass_options '';
  308.         if (!empty($this->_options['ini'])) {
  309.             $pass_options $this->_options['ini'];
  310.         }
  311.  
  312.         if (is_string($ini_settings)) {
  313.             $ini_settings $this->iniString2array($ini_settings);
  314.         }
  315.  
  316.         $ini_settings $this->settings2array($this->ini_overwrites$ini_settings);
  317.         if ($section_text['INI']{
  318.             if (strpos($section_text['INI']'{PWD}'!== false{
  319.                 $section_text['INI'str_replace('{PWD}'dirname($file)$section_text['INI']);
  320.             }
  321.             $ini preg_split"/[\n\r]+/"$section_text['INI']);
  322.             $ini_settings $this->settings2array($ini$ini_settings);
  323.         }
  324.         $ini_settings $this->settings2params($ini_settings);
  325.         $shortname str_replace($cwd . DIRECTORY_SEPARATOR''$file);
  326.  
  327.         $tested trim($section_text['TEST']);
  328.         $tested.= !isset($this->_options['simple']? "[$shortname]" : ' ';
  329.  
  330.         if (!empty($section_text['POST']|| !empty($section_text['POST_RAW']||
  331.               !empty($section_text['UPLOAD']|| !empty($section_text['GET']||
  332.               !empty($section_text['COOKIE']|| !empty($section_text['EXPECTHEADERS'])) {
  333.             if (empty($this->_options['cgi'])) {
  334.                 if (!isset($this->_options['quiet'])) {
  335.                     $this->_logger->log(0"SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')");
  336.                 }
  337.                 if (isset($this->_options['tapoutput'])) {
  338.                     return array('ok'' # skip --cgi option needed for this test, "pear help run-tests" for info');
  339.                 }
  340.                 return 'SKIPPED';
  341.             }
  342.             $this->_savephp $this->_php;
  343.             $this->_php $this->_options['cgi'];
  344.         }
  345.  
  346.         $temp_dir realpath(dirname($file));
  347.         $main_file_name basename($file'phpt');
  348.         $diff_filename     $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff';
  349.         $log_filename      $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log';
  350.         $exp_filename      $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp';
  351.         $output_filename   $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out';
  352.         $memcheck_filename $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem';
  353.         $temp_file         $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php';
  354.         $temp_skipif       $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php';
  355.         $temp_clean        $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php';
  356.         $tmp_post          $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
  357.  
  358.         // unlink old test results
  359.         $this->_cleanupOldFiles($file);
  360.  
  361.         // Check if test should be skipped.
  362.         $res  $this->_runSkipIf($section_text$temp_skipif$tested$ini_settings);
  363.         if (count($res!= 2{
  364.             return $res;
  365.         }
  366.         $info $res['info'];
  367.         $warn $res['warn'];
  368.  
  369.         // We've satisfied the preconditions - run the test!
  370.         if (isset($this->_options['coverage']&& $this->xdebug_loaded{
  371.             $xdebug_file $temp_dir . DIRECTORY_SEPARATOR . $main_file_name 'xdebug';
  372.             $text "\n" 'function coverage_shutdown() {' .
  373.                     "\n" '    $xdebug = var_export(xdebug_get_code_coverage(), true);';
  374.             if (!function_exists('file_put_contents')) {
  375.                 $text .= "\n" '    $fh = fopen(\'' $xdebug_file '\', "wb");' .
  376.                         "\n" '    if ($fh !== false) {' .
  377.                         "\n" '        fwrite($fh, $xdebug);' .
  378.                         "\n" '        fclose($fh);' .
  379.                         "\n" '    }';
  380.             else {
  381.                 $text .= "\n" '    file_put_contents(\'' $xdebug_file '\', $xdebug);';
  382.             }
  383.  
  384.             // Workaround for http://pear.php.net/bugs/bug.php?id=17292
  385.             $lines             explode("\n"$section_text['FILE']);
  386.             $numLines          count($lines);
  387.             $namespace         '';
  388.             $coverage_shutdown 'coverage_shutdown';
  389.  
  390.             if (
  391.                 substr($lines[0]02== '<?' ||
  392.                 substr($lines[0]05== '<?php'
  393.             {
  394.                 unset($lines[0]);
  395.             }
  396.  
  397.  
  398.             for ($i = 0; $i $numLines$i++{
  399.                 if (isset($lines[$i]&& substr($lines[$i]09== 'namespace'{
  400.                     $namespace         substr($lines[$i]10-1);
  401.                     $coverage_shutdown $namespace '\\coverage_shutdown';
  402.                     $namespace         "namespace " $namespace ";\n";
  403.  
  404.                     unset($lines[$i]);
  405.                     break;
  406.                 }
  407.             }
  408.  
  409.             $text .= "\n    xdebug_stop_code_coverage();" .
  410.                 "\n" '} // end coverage_shutdown()' .
  411.                 "\n\n" 'register_shutdown_function("' $coverage_shutdown '");';
  412.             $text .= "\n" 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' "\n";
  413.  
  414.             $this->save_text($temp_file"<?php\n" $namespace $text  "\n" implode("\n"$lines));
  415.         else {
  416.             $this->save_text($temp_file$section_text['FILE']);
  417.         }
  418.  
  419.         $args $section_text['ARGS'' -- '.$section_text['ARGS''';
  420.         $cmd $this->_preparePhpBin($this->_php$temp_file$ini_settings);
  421.         $cmd.= "$args 2>&1";
  422.         if (isset($this->_logger)) {
  423.             $this->_logger->log(2'Running command "' $cmd '"');
  424.         }
  425.  
  426.         // Reset environment from any previous test.
  427.         $env $this->_resetEnv($section_text$temp_file);
  428.  
  429.         $section_text $this->_processUpload($section_text$file);
  430.         if (PEAR::isError($section_text)) {
  431.             return $section_text;
  432.         }
  433.  
  434.         if (array_key_exists('POST_RAW'$section_text&& !empty($section_text['POST_RAW'])) {
  435.             $post trim($section_text['POST_RAW']);
  436.             $raw_lines explode("\n"$post);
  437.  
  438.             $request '';
  439.             $started = false;
  440.             foreach ($raw_lines as $i => $line{
  441.                 if (empty($env['CONTENT_TYPE']&&
  442.                     preg_match('/^Content-Type:(.*)/i'$line$res)) {
  443.                     $env['CONTENT_TYPE'trim(str_replace("\r"''$res[1]));
  444.                     continue;
  445.                 }
  446.                 if ($started{
  447.                     $request .= "\n";
  448.                 }
  449.                 $started = true;
  450.                 $request .= $line;
  451.             }
  452.  
  453.             $env['CONTENT_LENGTH'strlen($request);
  454.             $env['REQUEST_METHOD''POST';
  455.  
  456.             $this->save_text($tmp_post$request);
  457.             $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
  458.         elseif (array_key_exists('POST'$section_text&& !empty($section_text['POST'])) {
  459.             $post trim($section_text['POST']);
  460.             $this->save_text($tmp_post$post);
  461.             $content_length strlen($post);
  462.  
  463.             $env['REQUEST_METHOD''POST';
  464.             $env['CONTENT_TYPE']   'application/x-www-form-urlencoded';
  465.             $env['CONTENT_LENGTH'$content_length;
  466.  
  467.             $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
  468.         else {
  469.             $env['REQUEST_METHOD''GET';
  470.             $env['CONTENT_TYPE']   '';
  471.             $env['CONTENT_LENGTH''';
  472.         }
  473.  
  474.         if (OS_WINDOWS && isset($section_text['RETURNS'])) {
  475.             ob_start();
  476.             system($cmd$return_value);
  477.             $out ob_get_contents();
  478.             ob_end_clean();
  479.             $section_text['RETURNS'= (int) trim($section_text['RETURNS']);
  480.             $returnfail ($return_value != $section_text['RETURNS']);
  481.         else {
  482.             $returnfail = false;
  483.             $stdin = isset($section_text['STDIN']$section_text['STDIN': null;
  484.             $out $this->system_with_timeout($cmd$env$stdin);
  485.             $return_value $out[0];
  486.             $out $out[1];
  487.         }
  488.  
  489.         $output preg_replace('/\r\n/'"\n"trim($out));
  490.  
  491.         if (isset($tmp_post&& realpath($tmp_post&& file_exists($tmp_post)) {
  492.             @unlink(realpath($tmp_post));
  493.         }
  494.         chdir($cwd)// in case the test moves us around
  495.  
  496.         $this->_testCleanup($section_text$temp_clean);
  497.  
  498.         /* when using CGI, strip the headers from the output */
  499.         $output $this->_stripHeadersCGI($output);
  500.  
  501.         if (isset($section_text['EXPECTHEADERS'])) {
  502.             $testheaders $this->_processHeaders($section_text['EXPECTHEADERS']);
  503.             $missing array_diff_assoc($testheaders$this->_headers);
  504.             $changed '';
  505.             foreach ($missing as $header => $value{
  506.                 if (isset($this->_headers[$header])) {
  507.                     $changed .= "-$header$value\n+$header";
  508.                     $changed .= $this->_headers[$header];
  509.                 else {
  510.                     $changed .= "-$header$value\n";
  511.                 }
  512.             }
  513.             if ($missing{
  514.                 // tack on failed headers to output:
  515.                 $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed";
  516.             }
  517.         }
  518.         // Does the output match what is expected?
  519.         do {
  520.             if (isset($section_text['EXPECTF']|| isset($section_text['EXPECTREGEX'])) {
  521.                 if (isset($section_text['EXPECTF'])) {
  522.                     $wanted trim($section_text['EXPECTF']);
  523.                 else {
  524.                     $wanted trim($section_text['EXPECTREGEX']);
  525.                 }
  526.                 $wanted_re preg_replace('/\r\n/'"\n"$wanted);
  527.                 if (isset($section_text['EXPECTF'])) {
  528.                     $wanted_re preg_quote($wanted_re'/');
  529.                     // Stick to basics
  530.                     $wanted_re str_replace("%s"".+?"$wanted_re)//not greedy
  531.                     $wanted_re str_replace("%i""[+\-]?[0-9]+"$wanted_re);
  532.                     $wanted_re str_replace("%d""[0-9]+"$wanted_re);
  533.                     $wanted_re str_replace("%x""[0-9a-fA-F]+"$wanted_re);
  534.                     $wanted_re str_replace("%f""[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?"$wanted_re);
  535.                     $wanted_re str_replace("%c""."$wanted_re);
  536.                     // %f allows two points "-.0.0" but that is the best *simple* expression
  537.                 }
  538.  
  539.     /* DEBUG YOUR REGEX HERE
  540.             var_dump($wanted_re);
  541.             print(str_repeat('=', 80) . "\n");
  542.             var_dump($output);
  543.     */
  544.                 if (!$returnfail && preg_match("/^$wanted_re\$/s"$output)) {
  545.                     if (file_exists($temp_file)) {
  546.                         unlink($temp_file);
  547.                     }
  548.                     if (array_key_exists('FAIL'$section_text)) {
  549.                         break;
  550.                     }
  551.                     if (!isset($this->_options['quiet'])) {
  552.                         $this->_logger->log(0"PASS $test_nr$tested$info");
  553.                     }
  554.                     if (isset($this->_options['tapoutput'])) {
  555.                         return array('ok'' - ' $tested);
  556.                     }
  557.                     return 'PASSED';
  558.                 }
  559.             else {
  560.                 if (isset($section_text['EXPECTFILE'])) {
  561.                     $f $temp_dir '/' trim($section_text['EXPECTFILE']);
  562.                     if (!($fp @fopen($f'rb'))) {
  563.                         return PEAR::raiseError('--EXPECTFILE-- section file ' .
  564.                             $f ' not found');
  565.                     }
  566.                     fclose($fp);
  567.                     $section_text['EXPECT'file_get_contents($f);
  568.                 }
  569.  
  570.                 if (isset($section_text['EXPECT'])) {
  571.                     $wanted preg_replace('/\r\n/'"\n"trim($section_text['EXPECT']));
  572.                 else {
  573.                     $wanted '';
  574.                 }
  575.  
  576.                 // compare and leave on success
  577.                 if (!$returnfail && 0 == strcmp($output$wanted)) {
  578.                     if (file_exists($temp_file)) {
  579.                         unlink($temp_file);
  580.                     }
  581.                     if (array_key_exists('FAIL'$section_text)) {
  582.                         break;
  583.                     }
  584.                     if (!isset($this->_options['quiet'])) {
  585.                         $this->_logger->log(0"PASS $test_nr$tested$info");
  586.                     }
  587.                     if (isset($this->_options['tapoutput'])) {
  588.                         return array('ok'' - ' $tested);
  589.                     }
  590.                     return 'PASSED';
  591.                 }
  592.             }
  593.         while (false);
  594.  
  595.         if (array_key_exists('FAIL'$section_text)) {
  596.             // we expect a particular failure
  597.             // this is only used for testing PEAR_RunTest
  598.             $expectf  = isset($section_text['EXPECTF']$wanted_re : null;
  599.             $faildiff $this->generate_diff($wanted$outputnull$expectf);
  600.             $faildiff preg_replace('/\r/'''$faildiff);
  601.             $wanted   preg_replace('/\r/'''trim($section_text['FAIL']));
  602.             if ($faildiff == $wanted{
  603.                 if (!isset($this->_options['quiet'])) {
  604.                     $this->_logger->log(0"PASS $test_nr$tested$info");
  605.                 }
  606.                 if (isset($this->_options['tapoutput'])) {
  607.                     return array('ok'' - ' $tested);
  608.                 }
  609.                 return 'PASSED';
  610.             }
  611.             unset($section_text['EXPECTF']);
  612.             $output $faildiff;
  613.             if (isset($section_text['RETURNS'])) {
  614.                 return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' .
  615.                     $file);
  616.             }
  617.         }
  618.  
  619.         // Test failed so we need to report details.
  620.         $txt $warn 'WARN ' 'FAIL ';
  621.         $this->_logger->log(0$txt $test_nr $tested $info);
  622.  
  623.         // write .exp
  624.         $res $this->_writeLog($exp_filename$wanted);
  625.         if (PEAR::isError($res)) {
  626.             return $res;
  627.         }
  628.  
  629.         // write .out
  630.         $res $this->_writeLog($output_filename$output);
  631.         if (PEAR::isError($res)) {
  632.             return $res;
  633.         }
  634.  
  635.         // write .diff
  636.         $returns = isset($section_text['RETURNS']?
  637.                         array(trim($section_text['RETURNS'])$return_value: null;
  638.         $expectf = isset($section_text['EXPECTF']$wanted_re : null;
  639.         $data $this->generate_diff($wanted$output$returns$expectf);
  640.         $res  $this->_writeLog($diff_filename$data);
  641.         if (PEAR::isError($res)) {
  642.             return $res;
  643.         }
  644.  
  645.         // write .log
  646.         $data = "
  647. ---- EXPECTED OUTPUT
  648. $wanted
  649. ---- ACTUAL OUTPUT
  650. $output
  651. ---- FAILED
  652. ";
  653.  
  654.         if ($returnfail{
  655.             $data .= "
  656. ---- EXPECTED RETURN
  657. $section_text[RETURNS]
  658. ---- ACTUAL RETURN
  659. $return_value
  660. ";
  661.         }
  662.  
  663.         $res $this->_writeLog($log_filename$data);
  664.         if (PEAR::isError($res)) {
  665.             return $res;
  666.         }
  667.  
  668.         if (isset($this->_options['tapoutput'])) {
  669.             $wanted explode("\n"$wanted);
  670.             $wanted "# Expected output:\n#\n#" implode("\n#"$wanted);
  671.             $output explode("\n"$output);
  672.             $output "#\n#\n# Actual output:\n#\n#" implode("\n#"$output);
  673.             return array($wanted $output 'not ok'' - ' $tested);
  674.         }
  675.         return $warn 'WARNED' 'FAILED';
  676.     }
  677.  
  678.     function generate_diff($wanted$output$rvalue$wanted_re)
  679.     {
  680.         $w  explode("\n"$wanted);
  681.         $o  explode("\n"$output);
  682.         $wr explode("\n"$wanted_re);
  683.         $w1 array_diff_assoc($w$o);
  684.         $o1 array_diff_assoc($o$w);
  685.         $o2 $w2 = array();
  686.         foreach ($w1 as $idx => $val{
  687.             if (!$wanted_re || !isset($wr[$idx]|| !isset($o1[$idx]||
  688.                   !preg_match('/^' $wr[$idx'\\z/'$o1[$idx])) {
  689.                 $w2[sprintf("%03d<"$idx)sprintf("%03d- "$idx + 1$val;
  690.             }
  691.         }
  692.         foreach ($o1 as $idx => $val{
  693.             if (!$wanted_re || !isset($wr[$idx]||
  694.                   !preg_match('/^' $wr[$idx'\\z/'$val)) {
  695.                 $o2[sprintf("%03d>"$idx)sprintf("%03d+ "$idx + 1$val;
  696.             }
  697.         }
  698.         $diff array_merge($w2$o2);
  699.         ksort($diff);
  700.         $extra $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : '';
  701.         return implode("\r\n"$diff$extra;
  702.     }
  703.  
  704.     //  Write the given text to a temporary file, and return the filename.
  705.     function save_text($filename$text)
  706.     {
  707.         if (!$fp fopen($filename'w')) {
  708.             return PEAR::raiseError("Cannot open file '" $filename "' (save_text)");
  709.         }
  710.         fwrite($fp$text);
  711.         fclose($fp);
  712.     if (1 < DETAILEDecho "
  713. FILE $filename {{{
  714. $text
  715. }}}
  716. ";
  717.     }
  718.  
  719.     function _cleanupOldFiles($file)
  720.     {
  721.         $temp_dir realpath(dirname($file));
  722.         $mainFileName basename($file'phpt');
  723.         $diff_filename     $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff';
  724.         $log_filename      $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log';
  725.         $exp_filename      $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp';
  726.         $output_filename   $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out';
  727.         $memcheck_filename $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem';
  728.         $temp_file         $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php';
  729.         $temp_skipif       $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php';
  730.         $temp_clean        $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php';
  731.         $tmp_post          $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
  732.  
  733.         // unlink old test results
  734.         @unlink($diff_filename);
  735.         @unlink($log_filename);
  736.         @unlink($exp_filename);
  737.         @unlink($output_filename);
  738.         @unlink($memcheck_filename);
  739.         @unlink($temp_file);
  740.         @unlink($temp_skipif);
  741.         @unlink($tmp_post);
  742.         @unlink($temp_clean);
  743.     }
  744.  
  745.     function _runSkipIf($section_text$temp_skipif$tested$ini_settings)
  746.     {
  747.         $info '';
  748.         $warn = false;
  749.         if (array_key_exists('SKIPIF'$section_text&& trim($section_text['SKIPIF'])) {
  750.             $this->save_text($temp_skipif$section_text['SKIPIF']);
  751.             $output $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\"");
  752.             $output $output[1];
  753.             $loutput ltrim($output);
  754.             unlink($temp_skipif);
  755.             if (!strncasecmp('skip'$loutput4)) {
  756.                 $skipreason = "SKIP $tested";
  757.                 if (preg_match('/^\s*skip\s*(.+)\s*/i'$output$m)) {
  758.                     $skipreason .= '(reason: ' $m[1')';
  759.                 }
  760.                 if (!isset($this->_options['quiet'])) {
  761.                     $this->_logger->log(0$skipreason);
  762.                 }
  763.                 if (isset($this->_options['tapoutput'])) {
  764.                     return array('ok'' # skip ' $reason);
  765.                 }
  766.                 return 'SKIPPED';
  767.             }
  768.  
  769.             if (!strncasecmp('info'$loutput4)
  770.                 && preg_match('/^\s*info\s*(.+)\s*/i'$output$m)) {
  771.                 $info = " (info: $m[1])";
  772.             }
  773.  
  774.             if (!strncasecmp('warn'$loutput4)
  775.                 && preg_match('/^\s*warn\s*(.+)\s*/i'$output$m)) {
  776.                 $warn = true; /* only if there is a reason */
  777.                 $info = " (warn: $m[1])";
  778.             }
  779.         }
  780.  
  781.         return array('warn' => $warn'info' => $info);
  782.     }
  783.  
  784.     function _stripHeadersCGI($output)
  785.     {
  786.         $this->headers = array();
  787.         if (!empty($this->_options['cgi']&&
  788.               $this->_php == $this->_options['cgi'&&
  789.               preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s"$output$match)) {
  790.             $output = isset($match[2]trim($match[2]'';
  791.             $this->_headers $this->_processHeaders($match[1]);
  792.         }
  793.  
  794.         return $output;
  795.     }
  796.  
  797.     /**
  798.      * Return an array that can be used with array_diff() to compare headers
  799.      *
  800.      * @param string $text 
  801.      */
  802.     function _processHeaders($text)
  803.     {
  804.         $headers = array();
  805.         $rh preg_split("/[\n\r]+/"$text);
  806.         foreach ($rh as $line{
  807.             if (strpos($line':')!== false{
  808.                 $line explode(':'$line2);
  809.                 $headers[trim($line[0])trim($line[1]);
  810.             }
  811.         }
  812.         return $headers;
  813.     }
  814.  
  815.     function _readFile($file)
  816.     {
  817.         // Load the sections of the test file.
  818.         $section_text = array(
  819.             'TEST'   => '(unnamed test)',
  820.             'SKIPIF' => '',
  821.             'GET'    => '',
  822.             'COOKIE' => '',
  823.             'POST'   => '',
  824.             'ARGS'   => '',
  825.             'INI'    => '',
  826.             'CLEAN'  => '',
  827.         );
  828.  
  829.         if (!is_file($file|| !$fp fopen($file"r")) {
  830.             return PEAR::raiseError("Cannot open test file: $file");
  831.         }
  832.  
  833.         $section '';
  834.         while (!feof($fp)) {
  835.             $line fgets($fp);
  836.  
  837.             // Match the beginning of a section.
  838.             if (preg_match('/^--([_A-Z]+)--/'$line$r)) {
  839.                 $section $r[1];
  840.                 $section_text[$section'';
  841.                 continue;
  842.             elseif (empty($section)) {
  843.                 fclose($fp);
  844.                 return PEAR::raiseError("Invalid sections formats in test file: $file");
  845.             }
  846.  
  847.             // Add to the section text.
  848.             $section_text[$section.= $line;
  849.         }
  850.         fclose($fp);
  851.  
  852.         return $section_text;
  853.     }
  854.  
  855.     function _writeLog($logname$data)
  856.     {
  857.         if (!$log fopen($logname'w')) {
  858.             return PEAR::raiseError("Cannot create test log - $logname");
  859.         }
  860.         fwrite($log$data);
  861.         fclose($log);
  862.     }
  863.  
  864.     function _resetEnv($section_text$temp_file)
  865.     {
  866.         $env $_ENV;
  867.         $env['REDIRECT_STATUS''';
  868.         $env['QUERY_STRING']    '';
  869.         $env['PATH_TRANSLATED''';
  870.         $env['SCRIPT_FILENAME''';
  871.         $env['REQUEST_METHOD']  '';
  872.         $env['CONTENT_TYPE']    '';
  873.         $env['CONTENT_LENGTH']  '';
  874.         if (!empty($section_text['ENV'])) {
  875.             if (strpos($section_text['ENV']'{PWD}'!== false{
  876.                 $section_text['ENV'str_replace('{PWD}'dirname($temp_file)$section_text['ENV']);
  877.             }
  878.             foreach (explode("\n"trim($section_text['ENV'])) as $e{
  879.                 $e explode('='trim($e)2);
  880.                 if (!empty($e[0]&& isset($e[1])) {
  881.                     $env[$e[0]] $e[1];
  882.                 }
  883.             }
  884.         }
  885.         if (array_key_exists('GET'$section_text)) {
  886.             $env['QUERY_STRING'trim($section_text['GET']);
  887.         else {
  888.             $env['QUERY_STRING''';
  889.         }
  890.         if (array_key_exists('COOKIE'$section_text)) {
  891.             $env['HTTP_COOKIE'trim($section_text['COOKIE']);
  892.         else {
  893.             $env['HTTP_COOKIE''';
  894.         }
  895.         $env['REDIRECT_STATUS''1';
  896.         $env['PATH_TRANSLATED'$temp_file;
  897.         $env['SCRIPT_FILENAME'$temp_file;
  898.  
  899.         return $env;
  900.     }
  901.  
  902.     function _processUpload($section_text$file)
  903.     {
  904.         if (array_key_exists('UPLOAD'$section_text&& !empty($section_text['UPLOAD'])) {
  905.             $upload_files trim($section_text['UPLOAD']);
  906.             $upload_files explode("\n"$upload_files);
  907.  
  908.             $request "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" .
  909.                        "-----------------------------20896060251896012921717172737\n";
  910.             foreach ($upload_files as $fileinfo{
  911.                 $fileinfo explode('='$fileinfo);
  912.                 if (count($fileinfo!= 2{
  913.                     return PEAR::raiseError("Invalid UPLOAD section in test file: $file");
  914.                 }
  915.                 if (!realpath(dirname($file'/' $fileinfo[1])) {
  916.                     return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " .
  917.                         "in test file: $file");
  918.                 }
  919.                 $file_contents file_get_contents(dirname($file'/' $fileinfo[1]);
  920.                 $fileinfo[1basename($fileinfo[1]);
  921.                 $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n";
  922.                 $request .= "Content-Type: text/plain\n\n";
  923.                 $request .= $file_contents "\n" .
  924.                     "-----------------------------20896060251896012921717172737\n";
  925.             }
  926.  
  927.             if (array_key_exists('POST'$section_text&& !empty($section_text['POST'])) {
  928.                 // encode POST raw
  929.                 $post trim($section_text['POST']);
  930.                 $post explode('&'$post);
  931.                 foreach ($post as $i => $post_info{
  932.                     $post_info explode('='$post_info);
  933.                     if (count($post_info!= 2{
  934.                         return PEAR::raiseError("Invalid POST data in test file: $file");
  935.                     }
  936.                     $post_info[0rawurldecode($post_info[0]);
  937.                     $post_info[1rawurldecode($post_info[1]);
  938.                     $post[$i$post_info;
  939.                 }
  940.                 foreach ($post as $post_info{
  941.                     $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n";
  942.                     $request .= $post_info[1"\n" .
  943.                         "-----------------------------20896060251896012921717172737\n";
  944.                 }
  945.                 unset($section_text['POST']);
  946.             }
  947.             $section_text['POST_RAW'$request;
  948.         }
  949.  
  950.         return $section_text;
  951.     }
  952.  
  953.     function _testCleanup($section_text$temp_clean)
  954.     {
  955.         if ($section_text['CLEAN']{
  956.             // perform test cleanup
  957.             $this->save_text($temp_clean$section_text['CLEAN']);
  958.             $output $this->system_with_timeout("$this->_php $temp_clean  2>&1");
  959.             if (strlen($output[1])) {
  960.                 echo "BORKED --CLEAN-- section! output:\n"$output[1];
  961.             }
  962.             if (file_exists($temp_clean)) {
  963.                 unlink($temp_clean);
  964.             }
  965.         }
  966.     }
  967. }

Documentation generated on Wed, 06 Jul 2011 23:31:19 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.