home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / tmp / PEAR-1.7.1 / PEAR / RunTest.php < prev    next >
Encoding:
PHP Script  |  2008-02-15  |  33.8 KB  |  921 lines

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