Back to index

php5  5.3.10
server-tests.php
Go to the documentation of this file.
00001 <?php
00002 /*
00003    +----------------------------------------------------------------------+
00004    | PHP Version 5                                                        |
00005    +----------------------------------------------------------------------+
00006    | Copyright (c) 1997-2010 The PHP Group                                |
00007    +----------------------------------------------------------------------+
00008    | This source file is subject to version 3.01 of the PHP license,      |
00009    | that is bundled with this package in the file LICENSE, and is        |
00010    | available through the world-wide-web at the following url:           |
00011    | http://www.php.net/license/3_01.txt                                  |
00012    | If you did not receive a copy of the PHP license and are unable to   |
00013    | obtain it through the world-wide-web, please send a note to          |
00014    | license@php.net so we can mail you a copy immediately.               |
00015    +----------------------------------------------------------------------+
00016    | Authors: Ilia Alshanetsky <ilia@php.net>                             |
00017    |          Preston L. Bannister <pbannister@php.net>                   |
00018    |          Marcus Boerger <helly@php.net>                              |
00019    |          Shane Caraveo <shane@php.net>                               |
00020    |          Derick Rethans <derick@php.net>                             |
00021    |          Sander Roobol <sander@php.net>                              |
00022    | (based on version by: Stig Bakken <ssb@php.net>)                     |
00023    | (based on the PHP 3 test framework by Rasmus Lerdorf)                |
00024    +----------------------------------------------------------------------+
00025  */
00026 
00027 set_time_limit(0);
00028 while(@ob_end_clean());
00029 if (ob_get_level()) echo "Not all buffers were deleted.\n";
00030 error_reporting(E_ALL);
00031 
00032 /**********************************************************************
00033  * QA configuration
00034  */
00035 
00036 define('PHP_QA_EMAIL', 'qa-reports@lists.php.net');
00037 define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php');
00038 
00039 /**********************************************************************
00040  * error messages
00041  */
00042 
00043 define('PCRE_MISSING_ERROR',
00044 '+-----------------------------------------------------------+
00045 |                       ! ERROR !                           |
00046 | The test-suite requires that you have pcre extension      |
00047 | enabled. To enable this extension either compile your PHP |
00048 | with --with-pcre-regex or if you have compiled pcre as a  |
00049 | shared module load it via php.ini.                        |
00050 +-----------------------------------------------------------+');
00051 define('SAFE_MODE_WARNING',
00052 '+-----------------------------------------------------------+
00053 |                       ! WARNING !                         |
00054 | You are running the test-suite with "safe_mode" ENABLED ! |
00055 |                                                           |
00056 | Chances are high that no test will work at all,           |
00057 | depending on how you configured "safe_mode" !             |
00058 +-----------------------------------------------------------+');
00059 define('TMP_MISSING',
00060 '+-----------------------------------------------------------+
00061 |                       ! ERROR   !                         |
00062 | You must create /tmp for session tests to work!           |
00063 |                                                           |
00064 +-----------------------------------------------------------+');
00065 define('PROC_OPEN_MISSING',
00066 '+-----------------------------------------------------------+
00067 |                       ! ERROR !                           |
00068 | The test-suite requires that proc_open() is available.    |
00069 | Please check if you disabled it in php.ini.               |
00070 +-----------------------------------------------------------+');
00071 define('REQ_PHP_VERSION',
00072 '+-----------------------------------------------------------+
00073 |                       ! ERROR !                           |
00074 | The test-suite must be run with PHP 5 or later.           |
00075 | You can still test older extecutables by setting          |
00076 | TEST_PHP_EXECUTABLE and running this script with PHP 5.   |
00077 +-----------------------------------------------------------+');
00078 /**********************************************************************
00079  * information scripts
00080  */
00081 define('PHP_INFO_SCRIPT','<?php echo "
00082 PHP_SAPI=" . PHP_SAPI . "
00083 PHP_VERSION=" . phpversion() . "
00084 ZEND_VERSION=" . zend_version() . "
00085 PHP_OS=" . PHP_OS . "
00086 INCLUDE_PATH=" . get_cfg_var("include_path") . "
00087 INI=" . realpath(get_cfg_var("cfg_file_path")) . "
00088 SCANNED_INI=" . (function_exists(\'php_ini_scanned_files\') ?
00089                                    str_replace("\n","", php_ini_scanned_files()) :
00090                                    "** not determined **") . "
00091 SERVER_SOFTWARE=" . (isset($_ENV[\'SERVER_SOFTWARE\']) ? $_ENV[\'SERVER_SOFTWARE\'] : \'UNKNOWN\');
00092 ?>');
00093 
00094 define('PHP_EXTENSIONS_SCRIPT','<?php print join(get_loaded_extensions(),":"); ?>');
00095 define('PHP_INI_SETTINGS_SCRIPT','<?php echo serialize(ini_get_all()); ?>');
00096 
00097 /**********************************************************************
00098  * various utility functions
00099  */
00100 
00101 function settings2array($settings, &$ini_settings)
00102 {
00103        foreach($settings as $setting) {
00104               if (strpos($setting, '=')!==false) {
00105                      $setting = explode("=", $setting, 2);
00106                      $name = trim($setting[0]);
00107                      $value = trim($setting[1]);
00108                      $ini_settings[$name] = $value;
00109               }
00110        }
00111 }
00112 
00113 function settings2params(&$ini_settings)
00114 {
00115        $settings = '';
00116        if (count($ini_settings)) {
00117               foreach($ini_settings as $name => $value) {
00118                      $value = addslashes($value);
00119                      $settings .= " -d \"".strtolower($name)."=$value\"";
00120               }
00121        }
00122        return $settings;
00123 }
00124 
00125 function generate_diff($wanted,$output)
00126 {
00127        $w = explode("\n", $wanted);
00128        $o = explode("\n", $output);
00129        $w1 = array_diff_assoc($w,$o);
00130        $o1 = array_diff_assoc($o,$w);
00131        $w2 = array();
00132        $o2 = array();
00133        foreach($w1 as $idx => $val) $w2[sprintf("%03d<",$idx)] = sprintf("%03d- ", $idx+1).$val;
00134        foreach($o1 as $idx => $val) $o2[sprintf("%03d>",$idx)] = sprintf("%03d+ ", $idx+1).$val;
00135        $diff = array_merge($w2, $o2);
00136        ksort($diff);
00137        return implode("\r\n", $diff);
00138 }
00139 
00140 function mkpath($path,$mode = 0777) {
00141        $dirs = split('[\\/]',$path);
00142        $path = $dirs[0];
00143        for($i = 1;$i < count($dirs);$i++) {
00144               $path .= '/'.$dirs[$i];
00145               @mkdir($path,$mode);
00146        }
00147 }
00148 
00149 function copyfiles($src,$new) {
00150        $d = dir($src);
00151        while (($entry = $d->read())) {
00152               if (is_file("$src/$entry")) {
00153                      copy("$src/$entry", "$new/$entry");
00154               }
00155        }
00156        $d->close();
00157 }
00158 
00159 function post_result_data($query,$data)
00160 {
00161        $url = QA_SUBMISSION_PAGE.'?'.$query;
00162        $post = "php_test_data=" . urlencode(base64_encode(preg_replace("/[\\x00]/", "[0x0]", $data)));
00163        $r = new HTTPRequest($url,NULL,NULL,$post);
00164        return $this->response_headers['Status']=='200';
00165 } 
00166 
00167 
00168 function execute($command, $args=NULL, $input=NULL, $cwd=NULL, $env=NULL)
00169 {
00170        $data = "";
00171        
00172        if (gettype($args)=='array') {
00173               $args = join($args,' ');
00174        }
00175        $commandline = "$command $args";
00176        $proc = proc_open($commandline, array(
00177                             0 => array('pipe', 'r'),
00178                             1 => array('pipe', 'w')),
00179                             $pipes, $cwd, $env);
00180 
00181        if (!$proc)
00182               return false;
00183 
00184        if ($input) {
00185               $out = fwrite($pipes[0],$input);
00186               if ($out != strlen($input)) {
00187                      return NULL;
00188               }
00189        }
00190        
00191        fclose($pipes[0]);
00192 
00193        while (true) {
00194               /* hide errors from interrupted syscalls */
00195               $r = $pipes;
00196               $w = null;
00197               $e = null;
00198               $n = @stream_select($r, $w, $e, 60);
00199 
00200               if ($n === 0) {
00201                      /* timed out */
00202                      $data .= "\n ** ERROR: process timed out **\n";
00203                      proc_terminate($proc);
00204                      return $data;
00205               } else if ($n) {
00206                      $line = fread($pipes[1], 8192);
00207                      if (strlen($line) == 0) {
00208                             /* EOF */
00209                             break;
00210                      }
00211                      $data .= $line;
00212               }
00213        }
00214        $code = proc_close($proc);
00215        return $data;
00216 }
00217 
00218 function executeCode($php, $ini_overwrites, $code, $remove_headers=true, $cwd=NULL, $env=NULL)
00219 {
00220        $params = NULL;
00221        if ($ini_overwrites) {
00222               $info_params = array();
00223               settings2array($ini_overwrites,$info_params);
00224               $params = settings2params($info_params);
00225        }
00226        $out = execute($php, $params, $code, $cwd, $env);
00227        // kill the headers
00228        if ($remove_headers && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) {
00229               $out = $match[2];
00230        }
00231        return $out;
00232 }
00233 
00234 
00235 /**********************************************************************
00236  * a simple request class that lets us handle http based tests
00237  */
00238 
00239 class HTTPRequest
00240 {
00241     public $headers = array();
00242     public $timeout = 4;
00243     public $urlparts = NULL;
00244     public $url = '';
00245     public $userAgent = 'PHP-Test-Harness';
00246     public $options = array();
00247     public $postdata = NULL;
00248     public $errmsg = '';
00249     public $errno = 0;
00250     public $response;
00251     public $response_headers;
00252     public $outgoing_payload;
00253     public $incoming_payload = '';
00254 
00255     /*
00256     URL is the full url
00257     headers is assoc array of outgoing http headers
00258     
00259     options may include
00260     timeout
00261     proxy_host
00262     proxy_port
00263     proxy_user
00264     proxy_pass
00265     method (GET|POST)
00266     
00267     post data is, well, post data.  It is not processed so
00268         multipart stuff must be prepared before calling this
00269         (or add it to class)
00270     */
00271     function HTTPRequest($URL, $headers=array(), $options=array(), $postdata=NULL)
00272     {
00273         $this->urlparts = @parse_url($URL);
00274         $this->url = $URL;
00275         $this->options = $options;
00276         $this->headers = $headers;
00277         $this->postdata = &$postdata;
00278         $this->doRequest();
00279     }
00280     
00281     function doRequest()
00282     {
00283         if (!$this->_validateUrl()) return;
00284         
00285         if (isset($this->options['timeout'])) 
00286             $this->timeout = (int)$this->options['timeout'];
00287     
00288         $this->_sendHTTP();
00289     }
00290 
00291     function _validateUrl()
00292     {
00293         if ( ! is_array($this->urlparts) ) {
00294             return FALSE;
00295         }
00296         if (!isset($this->urlparts['host'])) {
00297             $this->urlparts['host']='127.0.0.1';
00298         }
00299         if (!isset($this->urlparts['port'])) {
00300             $this->urlparts['port'] = 80;
00301         }
00302         if (!isset($this->urlparts['path']) || !$this->urlparts['path'])
00303             $this->urlparts['path'] = '/';
00304         return TRUE;
00305     }
00306     
00307     function _parseResponse()
00308     {
00309         if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $this->incoming_payload, $match)) {
00310             $this->response = $match[2];
00311             if (preg_match("/^HTTP\/1\.. (\d+).*/s",$match[1],$status) && !$status[1]) {
00312                     $this->errmsg = "HTTP Response $status[1] Not Found";
00313                     return FALSE;
00314             }
00315             $rh = preg_split("/[\n\r]+/",$match[1]);
00316             $this->response_headers = array();
00317             foreach ($rh as $line) {
00318                 if (strpos($line, ':')!==false) {
00319                     $line = explode(":", $line, 2);
00320                     $this->response_headers[trim($line[0])] = trim($line[1]);
00321                 }
00322             }
00323             $this->response_headers['Status']=$status[1];
00324             // if no content, return false
00325             if(strlen($this->response) > 0) return TRUE;
00326         }
00327         $this->errmsg = 'Invalid HTTP Response';
00328         return FALSE;
00329     }
00330     
00331     function &_getRequest()
00332     {
00333         $fullpath = $this->urlparts['path'].
00334                     (isset($this->urlparts['query'])?'?'.$this->urlparts['query']:'').
00335                     (isset($this->urlparts['fragment'])?'#'.$this->urlparts['fragment']:'');
00336         if (isset($this->options['proxy_host'])) {
00337             $fullpath = 'http://'.$this->urlparts['host'].':'.$this->urlparts['port'].$fullpath;
00338         }
00339         if (isset($this->options['proxy_user'])) {
00340             $headers['Proxy-Authorization'] = 'Basic ' . base64_encode($this->options['proxy_user'].":".$this->options['proxy_pass']);
00341         }
00342         $headers['User-Agent'] = $this->userAgent;
00343         $headers['Host'] = $this->urlparts['host'];
00344         $headers['Content-Length'] = strlen($this->postdata);
00345         $headers['Content-Type'] = 'application/x-www-form-urlencoded';
00346         if (isset($this->headers)) {
00347             $headers = array_merge($headers, $this->headers);
00348         }
00349         $headertext = '';
00350         foreach ($headers as $k => $v) {
00351             $headertext .= "$k: $v\r\n";
00352         }
00353         $method = trim($this->options['method'])?strtoupper(trim($this->options['method'])):'GET';
00354         $this->outgoing_payload = 
00355                 "$method $fullpath HTTP/1.0\r\n".
00356                 $headertext."\r\n".
00357                 $this->postdata;
00358         return $this->outgoing_payload;
00359     }
00360     
00361     function &_sendHTTP()
00362     {
00363         $this->_getRequest();
00364         $host = $this->urlparts['host'];
00365         $port = $this->urlparts['port'];
00366         if (isset($this->options['proxy_host'])) {
00367             $host = $this->options['proxy_host'];
00368             $port = isset($this->options['proxy_port'])?$this->options['proxy_port']:8080;
00369         }
00370         // send
00371         if ($this->timeout > 0) {
00372             $fp = fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout);
00373         } else {
00374             $fp = fsockopen($host, $port, $this->errno, $this->errmsg);
00375         }
00376         if (!$fp) {
00377             $this->errmsg = "Connect Error to $host:$port";
00378             return NULL;
00379         }
00380         if ($this->timeout > 0) {
00381             // some builds of php do not support this, silence
00382             // the warning
00383             @socket_set_timeout($fp, $this->timeout);
00384         }
00385         if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
00386             $this->errmsg = "Error Sending Request Data to $host";
00387             return NULL;
00388         }
00389         
00390         while ($data = fread($fp, 32768)) {
00391             $this->incoming_payload .= $data;
00392         }
00393 
00394         fclose($fp);
00395 
00396         $this->_parseResponse();
00397     }
00398 
00399 # a simple test case
00400 #$r = new HTTPRequest('http://localhost:81/info.php/path/info');
00401 #print_r($r->response_headers);
00402 #print $r->response;
00403 
00404 } // end HTTPRequest
00405 
00406 
00407 /**********************************************************************
00408  * main test harness
00409  */
00410 
00411 class testHarness {
00412        public $cwd;
00413        public $xargs = array(
00414               #arg         env var                value        default   description
00415               'c' => array(''                    ,'file'       ,NULL    ,'configuration file, see server-tests-config.php for example'),
00416               'd' => array('TEST_PATHS'          ,'paths'      ,NULL    ,'colon seperate path list'),
00417               'e' => array('TEST_PHP_ERROR_STYLE','EMACS|MSVC' ,'EMACS' ,'editor error style'),
00418               'h' => array(''                    ,''           ,NULL    ,'this help'),
00419               'i' => array('PHPRC'               ,'path|file'  ,NULL    ,'ini file to use for tests (sets PHPRC)'),
00420               'l' => array('TEST_PHP_LOG_FORMAT' ,'string'     ,'LEODC' ,'any combination of CDELO'),
00421               'm' => array('TEST_BASE_PATH'      ,'path'       ,NULL    ,'copy tests to this path before testing'),
00422               'n' => array('NO_PHPTEST_SUMMARY'  ,''           ,0       ,'do not print test summary'),
00423               'p' => array('TEST_PHP_EXECUTABLE' ,'path'       ,NULL    ,'php executable to be tested'),
00424               'q' => array('NO_INTERACTION'      ,''           ,0       ,'no console interaction (ie dont contact QA)'),
00425               'r' => array('REPORT_EXIT_STATUS'  ,''           ,0       ,'exit with status at end of execution'),
00426               's' => array('TEST_PHP_SRCDIR'     ,'path'       ,NULL    ,'path to php source code'),
00427               't' => array('TEST_PHP_DETAILED'   ,'number'     ,0       ,'level of detail output to dump'),
00428               'u' => array('TEST_WEB_BASE_URL'   ,'url'        ,''      ,'base url for http testing'),
00429               'v' => array('TEST_CONTEXT_INFO'   ,''           ,0       ,'view text executable context info'),
00430               'w' => array('TEST_WEB'            ,''           ,0       ,'run tests via http'),
00431               'x' => array('TEST_WEB_EXT'        ,'file ext'   ,'php'   ,'http file extension to use')
00432               );
00433        
00434        public $conf = array();
00435        public $test_to_run = array();
00436        public $test_files = array();
00437        public $test_results = array();
00438        public $failed_tests = array();
00439        public $exts_to_test;
00440        public $exts_tested = 0;
00441        public $exts_skipped = 0;
00442        public $ignored_by_ext = 0;
00443        public $test_dirs = array('tests', 'pear', 'ext', 'sapi');
00444        public $start_time;
00445        public $end_time;
00446        public $exec_info;
00447        public $test_executable_iscgi = false;
00448        public $inisettings; // the test executables settings, used for web tests
00449        public $iswin32 = false;
00450        
00451        public $ddash = "=====================================================================";
00452        public $sdash = "---------------------------------------------------------------------";
00453 
00454        // Default ini settings
00455        public $ini_overwrites = array(
00456                      'output_handler'=>'',
00457                      'zlib.output_compression'=>'Off',
00458                      'open_basedir'=>'',
00459                      'safe_mode'=>'0',
00460                      'disable_functions'=>'',
00461                      'output_buffering'=>'Off',
00462                      'error_reporting'=>'4095',
00463                      'display_errors'=>'1',
00464                      'log_errors'=>'0',
00465                      'html_errors'=>'0',
00466                      'track_errors'=>'1',
00467                      'report_memleaks'=>'1',
00468                      'report_zend_debug'=>'0',
00469                      'docref_root'=>'/phpmanual/',
00470                      'docref_ext'=>'.html',
00471                      'error_prepend_string'=>'',
00472                      'error_append_string'=>'',
00473                      'auto_prepend_file'=>'',
00474                      'auto_append_file'=>'',
00475                      'magic_quotes_runtime'=>'0',
00476               );     
00477        public $env = array();
00478        public $info_params = array();
00479 
00480        function testHarness() {
00481               $this->iswin32 = substr(PHP_OS, 0, 3) == "WIN";
00482               $this->checkRequirements();
00483               $this->env = $_ENV;
00484               $this->removeSensitiveEnvVars();
00485               
00486               $this->initializeConfiguration();
00487               $this->parseArgs();
00488               $this->setTestPaths();
00489               # change to working directory
00490               if ($this->conf['TEST_PHP_SRCDIR']) {
00491                      @chdir($this->conf['TEST_PHP_SRCDIR']);
00492               }
00493               $this->cwd = getcwd();
00494 
00495               if (!$this->conf['TEST_PHP_SRCDIR'])
00496                      $this->conf['TEST_PHP_SRCDIR'] = $this->cwd;
00497               if (!$this->conf['TEST_BASE_PATH'] && $this->conf['TEST_PHP_SRCDIR'])
00498                      $this->conf['TEST_BASE_PATH'] = $this->conf['TEST_PHP_SRCDIR'];
00499               if ($this->iswin32) {
00500                      $this->conf['TEST_PHP_SRCDIR'] = str_replace('/','\\',$this->conf['TEST_PHP_SRCDIR']);
00501                      $this->conf['TEST_BASE_PATH'] = str_replace('/','\\',$this->conf['TEST_BASE_PATH']);
00502               }
00503               
00504               if (!$this->conf['TEST_WEB'] && !is_executable($this->conf['TEST_PHP_EXECUTABLE'])) {
00505                      $this->error("invalid PHP executable specified by TEST_PHP_EXECUTABLE  = " .
00506                                    $this->conf['TEST_PHP_EXECUTABLE']);
00507                      return false;
00508               }
00509               
00510               $this->getInstalledExtensions();
00511               $this->getExecutableInfo();
00512               $this->getExecutableIniSettings();
00513               $this->test_executable_iscgi = strncmp($this->exec_info['PHP_SAPI'],'cgi',3)==0;
00514               $this->calculateDocumentRoot();
00515 
00516               // add TEST_PHP_SRCDIR to the include path, this facilitates
00517               // tests including files from src/tests
00518               //$this->ini_overwrites['include_path'] = $this->cwd.($this->iswin32?';.;':':.:').$this->exec_info['INCLUDE_PATH'];
00519               
00520               $params = array();
00521               settings2array($this->ini_overwrites,$params);
00522               $this->info_params = settings2params($params);
00523               
00524               $this->contextHeader();
00525               if ($this->conf['TEST_CONTEXT_INFO']) return;
00526               $this->loadFileList();
00527               $this->moveTestFiles();
00528               $this->run();
00529               $this->summarizeResults();
00530        }
00531 
00532        function getExecutableIniSettings()
00533        {
00534               $out = $this->runscript(PHP_INI_SETTINGS_SCRIPT,true);
00535               $this->inisettings = unserialize($out);
00536        }
00537        
00538        function getExecutableInfo()
00539        {
00540               $out = $this->runscript(PHP_INFO_SCRIPT,true);
00541               $out = preg_split("/[\n\r]+/",$out);
00542               $info = array();
00543               foreach ($out as $line) {
00544                      if (strpos($line, '=')!==false) {
00545                             $line = explode("=", $line, 2);
00546                             $name = trim($line[0]);
00547                             $value = trim($line[1]);
00548                             $info[$name] = $value;
00549                      }
00550               }
00551               $this->exec_info = $info;
00552        }
00553        
00554        function getInstalledExtensions()
00555        {
00556               // get the list of installed extensions
00557               $out = $this->runscript(PHP_EXTENSIONS_SCRIPT,true);
00558               $this->exts_to_test = split(":",$out);
00559               sort($this->exts_to_test);
00560               $this->exts_tested = count($this->exts_to_test);
00561        }
00562 
00563        // if running local, calls executeCode,
00564        // otherwise does an http request
00565        function runscript($script,$removeheaders=false,$cwd=NULL)
00566        {
00567               if ($this->conf['TEST_WEB']) {
00568                      $pi = '/testscript.' . $this->conf['TEST_WEB_EXT'];
00569                      if (!$cwd) $cwd = $this->conf['TEST_BASE_PATH'];
00570                      $tmp_file = "$cwd$pi";
00571                      $pi = substr($cwd,strlen($this->conf['TEST_BASE_PATH'])) . $pi;
00572                      $url = $this->conf['TEST_WEB_BASE_URL'] . $pi;
00573                      file_put_contents($tmp_file,$script);
00574                      $fd = fopen($url, "rb");
00575                      $out = '';
00576                      if ($fd) {
00577                             while (!feof($fd))
00578                                    $out .= fread($fd, 8192);
00579                             fclose($fd);
00580                      }
00581                      unlink($tmp_file);
00582                      if (0 && $removeheaders &&
00583                             preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) {
00584                                    return $match[2];
00585                      }
00586                      return $out;
00587               } else {
00588                      return executeCode($this->conf['TEST_PHP_EXECUTABLE'],$this->ini_overwrites, $script,$removeheaders,$cwd,$this->env);
00589               }
00590        }
00591 
00592        
00593        // Use this function to do any displaying of text, so that
00594        // things can be over-written as necessary.
00595        
00596        function writemsg($msg) {
00597            
00598            echo $msg;
00599            
00600        }
00601        
00602        // Another wrapper function, this one should be used any time
00603        // a particular test passes or fails
00604        
00605        function showstatus($item, $status, $reason = '') {
00606            
00607            switch($status) {
00608               case 'PASSED':
00609                   $this->writemsg("PASSED: $item ($reason)\n");
00610                   break;
00611               case 'FAILED':
00612                   $this->writemsg("FAILED: $item ($reason)\n");
00613                   break;
00614               case 'SKIPPED':
00615                   $this->writemsg("SKIPPED: $item ($reason)\n");
00616                   break;
00617            }
00618        }
00619        
00620        
00621        function help()
00622        {
00623               $usage = "usage: php run-tests.php [options]\n";
00624               foreach ($this->xargs as $arg=>$arg_info) {
00625                      $usage .= sprintf(" -%s  %-12s %s\n",$arg,$arg_info[1],$arg_info[3]);
00626               }
00627               return $usage;
00628        }
00629        
00630        function parseArgs() {
00631               global $argc;
00632               global $argv;
00633               global $_SERVER;
00634               
00635               if (!isset($argv)) {
00636                      $argv = $_SERVER['argv'];
00637                      $argc = $_SERVER['argc'];
00638               }
00639        
00640               $conf = NULL;
00641               for ($i=1; $i<$argc;) {
00642                      if ($argv[$i][0] != '-') continue;
00643                      $opt = $argv[$i++][1];
00644                      if (isset($value)) unset($value);
00645                      if (@$argv[$i][0] != '-') {
00646                             @$value = $argv[$i++];
00647                      }
00648                      switch($opt) {
00649                      case 'c':
00650                             /* TODO: Implement configuraiton file */
00651                             include($value);
00652                             if (!isset($conf)) {
00653                                    $this->writemsg("Invalid configuration file\n");
00654                                    exit(1);
00655                             }
00656                             $this->conf = array_merge($this->conf,$conf);
00657                             break;
00658                      case 'e':
00659                             $this->conf['TEST_PHP_ERROR_STYLE'] = strtoupper($value);
00660                             break;
00661                      case 'h':
00662                             print $this->help();
00663                             exit(0);
00664                      default:
00665                             if ($this->xargs[$opt][1] && isset($value))
00666                                    $this->conf[$this->xargs[$opt][0]] = $value;
00667                             else if (!$this->xargs[$opt][1])
00668                                    $this->conf[$this->xargs[$opt][0]] = isset($value)?$value:1;
00669                             else
00670                                    $this->error("Invalid argument setting for argument $opt, should be [{$this->xargs[$opt][1]}]\n");
00671                             break;
00672                      }
00673               }
00674               
00675               // set config into environment, this allows
00676               // executed tests to find out about the test
00677               // configurations.  config file or args overwrite
00678               // env var config settings
00679               $this->env = array_merge($this->env,$this->conf);
00680               if (!$this->conf['TEST_WEB'] && !$this->conf['TEST_PHP_EXECUTABLE']) {
00681                      $this->writemsg($this->help());
00682                      exit(0);
00683               }
00684        }
00685 
00686        function removeSensitiveEnvVars()
00687        {
00688               # delete sensitive env vars
00689               $this->env['SSH_CLIENT']='deleted';
00690               $this->env['SSH_AUTH_SOCK']='deleted';
00691               $this->env['SSH_TTY']='deleted';
00692        }
00693        
00694        function setEnvConfigVar($name)
00695        {
00696               if (isset($this->env[$name])) {
00697                      $this->conf[$name] = $this->env[$name];
00698               }
00699        }
00700        
00701        function initializeConfiguration()
00702        {
00703               foreach ($this->xargs as $arg=>$arg_info) {
00704                      if ($arg_info[0]) {
00705                             # initialize the default setting
00706                             $this->conf[$arg_info[0]]=$arg_info[2];
00707                             # get config from environment
00708                             $this->setEnvConfigVar($arg_info[0]);
00709                      }
00710               }
00711        }
00712 
00713        function setTestPaths()
00714        {
00715               // configure test paths from config file or command line
00716               if (@$this->conf['TEST_PATHS']) {
00717                      $this->test_dirs = array();
00718                      if ($this->iswin32) {
00719                             $paths = split(';',$this->conf['TEST_PATHS']);
00720                      } else {
00721                             $paths = split(':|;',$this->conf['TEST_PATHS']);
00722                      }
00723                      foreach($paths as $path) {
00724                             $this->test_dirs[] = realpath($path);
00725                      }
00726               }
00727        }
00728        
00729        function test_sort($a, $b) {
00730               $ta = strpos($a, "{$this->cwd}/tests")===0 ? 1 + (strpos($a, "{$this->cwd}/tests/run-test")===0 ? 1 : 0) : 0;
00731               $tb = strpos($b, "{$this->cwd}/tests")===0 ? 1 + (strpos($b, "{$this->cwd}/tests/run-test")===0 ? 1 : 0) : 0;
00732               if ($ta == $tb) {
00733                      return strcmp($a, $b);
00734               } else {
00735                      return $tb - $ta;
00736               }
00737        }
00738 
00739        function checkRequirements() {
00740               if (version_compare(phpversion(), "5.0") < 0) {
00741                      $this->writemsg(REQ_PHP_VERSION);
00742                      exit;
00743               }
00744 // We might want to check another server so we won't see that server's /tmp
00745 //            if (!file_exists("/tmp")) {
00746 //                   $this->writemsg(TMP_MISSING);
00747 //                   exit;
00748 //            }
00749               if (!function_exists("proc_open")) {
00750                      $this->writemsg(PROC_OPEN_MISSING);
00751                      exit;
00752               }
00753               if (!extension_loaded("pcre")) {
00754                      $this->writemsg(PCRE_MISSING_ERROR);
00755                      exit;
00756               }
00757               if (ini_get('safe_mode')) {
00758                      $this->writemsg(SAFE_MODE_WARNING);
00759               }
00760        }
00761        
00762        //
00763        // Write test context information.
00764        //
00765        function contextHeader()
00766        {
00767               $info = '';
00768               foreach ($this->exec_info as $k=>$v) {
00769                      $info .= sprintf("%-20.s: %s\n",$k,$v);
00770               }
00771               $exts = '';
00772               foreach ($this->exts_to_test as $ext) {
00773                      $exts .="$ext\n              ";
00774               }
00775               $dirs = '';
00776               foreach ($this->test_dirs as $test_dir) {
00777                      $dirs .= "$test_dir\n              ";
00778               }
00779               $conf = '';
00780               foreach ($this->conf as $k=>$v) {
00781                      $conf .= sprintf("%-20.s: %s\n",$k,$v);
00782               }
00783               
00784               $exeinfo = '';
00785               if (!$this->conf['TEST_WEB'])
00786                      $exeinfo = "CWD                 : {$this->cwd}\n".
00787                                    "PHP                 : {$this->conf['TEST_PHP_EXECUTABLE']}\n";
00788               
00789               $this->writemsg("\n$this->ddash\n".
00790                      "$exeinfo$info\n".
00791                      "Test Harness Configuration:\n$conf\n".
00792                      "Extensions  : $exts\n".
00793                      "Test Dirs   : $dirs\n".
00794                      "$this->ddash\n");
00795        }
00796        
00797        function loadFileList()
00798        {
00799               foreach ($this->test_dirs as $dir) {
00800                      if (is_dir($dir)) {
00801                             $this->findFilesInDir($dir, ($dir == 'ext'));
00802                      } else {
00803                             $this->test_files[] = $dir;
00804                      }
00805               }
00806               usort($this->test_files,array($this,"test_sort"));
00807               $this->writemsg("found ".count($this->test_files)." files\n");
00808        }
00809        
00810        function moveTestFiles()
00811        {
00812               if (!$this->conf['TEST_BASE_PATH'] ||
00813                      $this->conf['TEST_BASE_PATH'] == $this->conf['TEST_PHP_SRCDIR']) return;
00814               $this->writemsg("moving files from {$this->conf['TEST_PHP_SRCDIR']} to {$this->conf['TEST_BASE_PATH']}\n");
00815               $l = strlen($this->conf['TEST_PHP_SRCDIR']);
00816               $files = array();
00817               $dirs = array();
00818               foreach ($this->test_files as $file) {
00819                      if (strpos($file,$this->conf['TEST_PHP_SRCDIR'])==0) {
00820                             $newlocation = $this->conf['TEST_BASE_PATH'].substr($file,$l);
00821                             $files[] = $newlocation;
00822                             $dirs[dirname($file)] = dirname($newlocation);
00823                      } else {
00824                             // XXX what to do with test files outside the
00825                             // php source directory?  Need to map them into
00826                             // the new directory somehow.
00827                      }
00828               }
00829               foreach ($dirs as $src=>$new) {
00830                      mkpath($new);
00831                      copyfiles($src,$new);
00832               }
00833               $this->test_files = $files;
00834        }
00835        
00836        function findFilesInDir($dir,$is_ext_dir=FALSE,$ignore=FALSE)
00837        {
00838               $skip = array('.', '..', 'CVS');
00839               $o = opendir($dir) or $this->error("cannot open directory: $dir");
00840               while (($name = readdir($o)) !== FALSE) {
00841                      if (in_array($name, $skip)) continue;
00842                      if (is_dir("$dir/$name")) {
00843                             $skip_ext = ($is_ext_dir && !in_array($name, $this->exts_to_test));
00844                             if ($skip_ext) {
00845                                    $this->exts_skipped++;
00846                             }
00847                             $this->findFilesInDir("$dir/$name", FALSE, $ignore || $skip_ext);
00848                      }
00849        
00850                      // Cleanup any left-over tmp files from last run.
00851                      if (substr($name, -4) == '.tmp') {
00852                             @unlink("$dir/$name");
00853                             continue;
00854                      }
00855        
00856                      // Otherwise we're only interested in *.phpt files.
00857                      if (substr($name, -5) == '.phpt') {
00858                             if ($ignore) {
00859                                    $this->ignored_by_ext++;
00860                             } else {
00861                                    $testfile = realpath("$dir/$name");
00862                                    $this->test_files[] = $testfile;
00863                             }
00864                      }
00865               }
00866               closedir($o);
00867        }
00868        
00869        function runHeader()
00870        {
00871               $this->writemsg("TIME START " . date('Y-m-d H:i:s', $this->start_time) . "\n".$this->ddash."\n");
00872               if (count($this->test_to_run)) {
00873                      $this->writemsg("Running selected tests.\n");
00874               } else {
00875                      $this->writemsg("Running all test files.\n");
00876               }
00877        }
00878        
00879        function run()
00880        {
00881               $this->start_time = time();
00882               $this->runHeader();
00883               // Run selected tests.
00884               if (count($this->test_to_run)) {
00885                      
00886                      foreach($this->test_to_run as $name=>$runnable) {
00887                             if(!preg_match("/\.phpt$/", $name))
00888                                    continue;
00889                             if ($runnable) {
00890                                    $this->test_results[$name] = $this->run_test($name);
00891                             }
00892                      }
00893               } else {
00894                      foreach ($this->test_files as $name) {
00895                             $this->test_results[$name] = $this->run_test($name);
00896                      }
00897               }
00898               $this->end_time = time();
00899        }
00900 
00901        function summarizeResults()
00902        {
00903               if (count($this->test_results) == 0) {
00904                      $this->writemsg("No tests were run.\n");
00905                      return;
00906               }
00907               
00908               $n_total = count($this->test_results);
00909               $n_total += $this->ignored_by_ext;
00910               
00911               $sum_results = array('PASSED'=>0, 'SKIPPED'=>0, 'FAILED'=>0);
00912               foreach ($this->test_results as $v) {
00913                      $sum_results[$v]++;
00914               }
00915               $sum_results['SKIPPED'] += $this->ignored_by_ext;
00916               $percent_results = array();
00917               while (list($v,$n) = each($sum_results)) {
00918                      $percent_results[$v] = (100.0 * $n) / $n_total;
00919               }
00920               
00921               $this->writemsg("\n".$this->ddash."\n".
00922                      "TIME END " . date('Y-m-d H:i:s', $this->end_time) . "\n".
00923                      $this->ddash."\n".
00924                      "TEST RESULT SUMMARY\n".
00925                      $this->sdash."\n".
00926                      "Exts skipped    : " . sprintf("%4d",$this->exts_skipped) . "\n".
00927                      "Exts tested     : " . sprintf("%4d",$this->exts_tested) . "\n".
00928                      $this->sdash."\n".
00929                      "Number of tests : " . sprintf("%4d",$n_total) . "\n".
00930                      "Tests skipped   : " . sprintf("%4d (%2.1f%%)",$sum_results['SKIPPED'],$percent_results['SKIPPED']) . "\n".
00931                      "Tests failed    : " . sprintf("%4d (%2.1f%%)",$sum_results['FAILED'],$percent_results['FAILED']) . "\n".
00932                      "Tests passed    : " . sprintf("%4d (%2.1f%%)",$sum_results['PASSED'],$percent_results['PASSED']) . "\n".
00933                      $this->sdash."\n".
00934                      "Time taken      : " . sprintf("%4d seconds", $this->end_time - $this->start_time) . "\n".
00935                      $this->ddash."\n");
00936               
00937               $failed_test_summary = '';
00938               if ($this->failed_tests) {
00939                      $failed_test_summary .= "\n".$this->ddash."\n".
00940                             "FAILED TEST SUMMARY\n".$this->sdash."\n";
00941                      foreach ($this->failed_tests as $failed_test_data) {
00942                             $failed_test_summary .=  $failed_test_data['test_name'] . "\n";
00943                      }
00944                      $failed_test_summary .=  $this->ddash."\n";
00945               }
00946               
00947               if ($failed_test_summary && !$this->conf['NO_PHPTEST_SUMMARY']) {
00948                      $this->writemsg($failed_test_summary);
00949               }
00950 
00951               /* We got failed Tests, offer the user to send and e-mail to QA team, unless NO_INTERACTION is set */
00952               if ($sum_results['FAILED'] && !$this->conf['NO_INTERACTION']) {
00953                      $fp = fopen("php://stdin", "r+");
00954                      $this->writemsg("\nPlease allow this report to be send to the PHP QA\nteam. This will give us a better understanding in how\n");
00955                      $this->writemsg("PHP's test cases are doing.\n");
00956                      $this->writemsg("(choose \"s\" to just save the results to a file)? [Yns]: ");
00957                      flush();
00958                      $user_input = fgets($fp, 10);
00959                      $just_save_results = (strtolower($user_input[0]) == 's');
00960                      
00961                      if ($just_save_results || strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y') {
00962                             /*  
00963                              * Collect information about the host system for our report
00964                              * Fetch phpinfo() output so that we can see the PHP enviroment
00965                              * Make an archive of all the failed tests
00966                              * Send an email
00967                              */
00968 
00969                             /* Ask the user to provide an email address, so that QA team can contact the user */
00970                             if (!strncasecmp($user_input, 'y', 1) || strlen(trim($user_input)) == 0) {
00971                                    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): ";
00972                                    flush();
00973                                    $fp = fopen("php://stdin", "r+");
00974                                    $user_email = trim(fgets($fp, 1024));
00975                                    $user_email = str_replace("@", " at ", str_replace(".", " dot ", $user_email));
00976                             }
00977               
00978                             $failed_tests_data = '';
00979                             $sep = "\n" . str_repeat('=', 80) . "\n";
00980                             
00981                             $failed_tests_data .= $failed_test_summary . "\n";
00982                             
00983                             if (array_sum($this->failed_tests)) {
00984                                    foreach ($this->failed_tests as $test_info) {
00985                                           $failed_tests_data .= $sep . $test_info['name'];
00986                                           $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']));
00987                                           $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']));
00988                                           $failed_tests_data .= $sep . "\n\n";
00989                                    }
00990                                    $status = "failed";
00991                             } else {
00992                                    $status = "success";
00993                             }
00994                             
00995                             $failed_tests_data .= "\n" . $sep . 'BUILD ENVIRONMENT' . $sep;
00996                             $failed_tests_data .= "OS:\n". PHP_OS. "\n\n";
00997                             $automake = $autoconf = $libtool = $compiler = 'N/A';
00998 
00999                             if (!$this->iswin32) {
01000                                    $automake = shell_exec('automake --version');
01001                                    $autoconf = shell_exec('autoconf --version');
01002                                    /* Always use the generated libtool - Mac OSX uses 'glibtool' */
01003                                    $libtool = shell_exec('./libtool --version');
01004                                    /* Try the most common flags for 'version' */
01005                                    $flags = array('-v', '-V', '--version');
01006                                    $cc_status=0;
01007                                    foreach($flags AS $flag) {
01008                                           system($this->env['CC']." $flag >/dev/null 2>&1", $cc_status);
01009                                           if($cc_status == 0) {
01010                                                  $compiler = shell_exec($this->env['CC']." $flag 2>&1");
01011                                                  break;
01012                                           }
01013                                    }
01014                             }
01015                             
01016                             $failed_tests_data .= "Automake:\n$automake\n";
01017                             $failed_tests_data .= "Autoconf:\n$autoconf\n";
01018                             $failed_tests_data .= "Libtool:\n$libtool\n";
01019                             $failed_tests_data .= "Compiler:\n$compiler\n";
01020                             $failed_tests_data .= "Bison:\n". @shell_exec('bison --version'). "\n";
01021                             $failed_tests_data .= "\n\n";
01022 
01023                             if (isset($user_email)) {
01024                                    $failed_tests_data .= "User's E-mail: ".$user_email."\n\n";
01025                             }
01026 
01027                             $failed_tests_data .= $sep . "PHPINFO" . $sep;
01028                             $failed_tests_data .= shell_exec($this->conf['TEST_PHP_EXECUTABLE'].' -dhtml_errors=0 -i');
01029                             
01030                             $compression = 0;
01031 
01032                             if ($just_save_results ||
01033                                    !post_result_data("status=$status&version=".urlencode(TESTED_PHP_VERSION),$failed_tests_data)) {
01034                                    $output_file = 'php_test_results_' . date('Ymd_Hi') . ( $compression ? '.txt.gz' : '.txt' );
01035                                    $fp = fopen($output_file, "w");
01036                                    fwrite($fp, $failed_tests_data);
01037                                    fclose($fp);
01038                             
01039                                    if (!$just_save_results)
01040                                           echo "\nThe test script was unable to automatically send the report to PHP's QA Team\n";
01041                                    echo "Please send ".$output_file." to ".PHP_QA_EMAIL." manually, thank you.\n";
01042                             } else {
01043                                    fwrite($fp, "\nThank you for helping to make PHP better.\n");
01044                                    fclose($fp);
01045                             }
01046                      }
01047               }
01048                
01049               if($this->conf['REPORT_EXIT_STATUS'] and $sum_results['FAILED']) {
01050                      exit(1);
01051               }
01052        }
01053 
01054        function getINISettings(&$section_text)
01055        {
01056               $ini_settings = $this->ini_overwrites;
01057               // Any special ini settings 
01058               // these may overwrite the test defaults...
01059               if (array_key_exists('INI', $section_text)) {
01060                      settings2array(preg_split( "/[\n\r]+/", $section_text['INI']), $ini_settings);
01061               }
01062               return $ini_settings;
01063        }
01064 
01065        function getINIParams(&$section_text)
01066        {
01067               if (!$section_text) return '';
01068               // XXX php5 current has a problem doing this in one line
01069               // it fails with Only variables can be passed by reference
01070               // on test ext\calendar\tests\jdtojewish.phpt
01071               // return settings2params($this->getINISettings($section_text));
01072               $ini = $this->getINISettings($section_text);
01073               return settings2params($ini);
01074        }
01075 
01076        function calculateDocumentRoot()
01077        {
01078               if ($this->conf['TEST_WEB'] || $this->test_executable_iscgi) {
01079                      // configure DOCUMENT_ROOT for web tests
01080                      // this assumes that directories from the base url
01081                      // matches directory depth from the base path
01082                      $parts = parse_url($this->conf['TEST_WEB_BASE_URL']);
01083                      $depth = substr_count($parts['path'],'/');
01084                      $docroot = $this->conf['TEST_BASE_PATH'];
01085                      for ($i=0 ; $i < $depth; $i++) $docroot = dirname($docroot);
01086                      $this->conf['TEST_DOCUMENT_ROOT']=$docroot;
01087                      $this->conf['TEST_BASE_SCRIPT_NAME']=$parts['path'];
01088                      $this->conf['TEST_SERVER_URL']=substr($this->conf['TEST_WEB_BASE_URL'],0,strlen($this->conf['TEST_WEB_BASE_URL'])-strlen($parts['path']));
01089               } else {
01090                      $this->conf['TEST_DOCUMENT_ROOT']='';
01091                      $this->conf['TEST_BASE_SCRIPT_NAME']='';
01092                      $this->conf['TEST_SERVER_URL']='';
01093               }
01094        }
01095 
01096        function evalSettings($filename,$data) {
01097               // we eval the section so we can allow dynamic env vars
01098               // for cgi testing
01099               $filename = str_replace('\\','/',$filename);
01100               $cwd = str_replace('\\','/',$this->cwd);
01101               $filepath = dirname($filename);
01102               $scriptname = substr($filename,strlen($this->conf['TEST_DOCUMENT_ROOT']));
01103               // eval fails if no newline
01104               return eval("$data\n");
01105        }
01106        
01107        function getENVSettings(&$section_text,$testfile)
01108        {
01109               $env = $this->env;
01110               // Any special environment settings 
01111               // these may overwrite the test defaults...
01112               if (array_key_exists('ENV', $section_text)) {
01113                      $sect = $this->evalSettings($testfile,$section_text['ENV']);
01114                      //print "data evaled:\n$sect\n";
01115                      settings2array(preg_split( "/[\n\r]+/", $sect), $env);
01116               }
01117               return $env;
01118        }
01119 
01120        function getEvalTestSettings($section_text,$testfile)
01121        {
01122               $rq = array();
01123               // Any special environment settings 
01124               // these may overwrite the test defaults...
01125               if ($section_text) {
01126                      $sect = $this->evalSettings($testfile,$section_text);
01127                      //print "data evaled:\n$sect\n";
01128                      settings2array(preg_split( "/[\n\r]+/", $sect), $rq);
01129               }
01130               return $rq;
01131        }
01132        
01133        //
01134        // Load the sections of the test file.
01135        //
01136        function getSectionText($file)
01137        {
01138               // Load the sections of the test file.
01139               $section_text = array(
01140                      'TEST'   => '(unnamed test)',
01141                      'SKIPIF' => '',
01142                      'GET'    => '',
01143                      'ARGS'   => '',
01144                      '_FILE'   => $file,
01145                      '_DIR'    => realpath(dirname($file)),
01146               );
01147        
01148               $fp = @fopen($file, "r")
01149                             or $this->error("Cannot open test file: $file");
01150        
01151               $section = '';
01152               while (!feof($fp)) {
01153                      $line = fgets($fp);
01154                      // Match the beginning of a section.
01155                      if (ereg('^--([A-Z]+)--',$line,$r)) {
01156                             $section = $r[1];
01157                             $section_text[$section] = '';
01158                             continue;
01159                      }
01160                      
01161                      // Add to the section text.
01162                      $section_text[$section] .= $line;
01163               }
01164               fclose($fp);
01165               foreach ($section_text as $k=>$v) {
01166                      // for POST data ,we only want to trim the last new line!
01167                      if ($k == 'POST' && preg_match('/^(.*?)\r?\n$/Ds',$v,$matches)) {
01168                             $section_text[$k]=$matches[1];
01169                      } else {
01170                             $section_text[$k]=trim($v);
01171                      }
01172               }
01173               return $section_text;
01174        }
01175 
01176        //
01177        // Check if test should be skipped.
01178        //
01179        function getSkipReason($file,&$section_text,$docgi=false)
01180        {
01181               // if the test uses POST or GET, and it's not the cgi
01182               // executable, skip
01183               if ($docgi && !$this->conf['TEST_WEB'] && !$this->test_executable_iscgi) {
01184                      $this->showstatus($section_text['TEST'], 'SKIPPED', 'CGI Test needs CGI Binary');
01185                      return "SKIPPED";
01186               }
01187               // if we're doing web testing, then we wont be able to set
01188               // ini setting on the command line.  be sure the executables
01189               // ini settings are compatible with the test, or skip
01190               if (($docgi || $this->conf['TEST_WEB']) &&
01191                      isset($section_text['INI']) && $section_text['INI']) {
01192                      $settings = $this->getINISettings($section_text);
01193                      foreach ($settings as $k=>$v) {
01194                             if (strcasecmp($v,'off')==0 || !$v) $v='';
01195                             $haveval = isset($this->inisettings[$k]['local_value']);
01196                             if ($k == 'include_path') {
01197                                    // we only want to know that src directory
01198                                    // is in the include path
01199                                    if (strpos($this->inisettings[$k]['local_value'],$this->cwd))
01200                                           continue;
01201                             }
01202                             if (($haveval && $this->inisettings[$k]['local_value'] != $v) || (!$haveval && $v)) {
01203                                    $this->showstatus($section_text['TEST'], 'SKIPPED', "Test requires ini setting $k=[$v], not [".($haveval?$this->inisettings[$k]['local_value']:'')."]");
01204                                    return "SKIPPED";
01205                             }
01206                      }
01207               }
01208               // now handle a SKIPIF section
01209               if ($section_text['SKIPIF']) {
01210                      $output = trim($this->runscript($section_text['SKIPIF'],$this->test_executable_iscgi,realpath(dirname($file))),true);
01211                      if (!$output) return NULL;
01212                      if ($this->conf['TEST_PHP_DETAILED'] > 2)
01213                             print "SKIPIF: [$output]\n";
01214                      if (eregi("^skip", $output)){
01215                      
01216                             $reason = (ereg("^skip[[:space:]]*(.+)\$", $output)) ? ereg_replace("^skip[[:space:]]*(.+)\$", "\\1", $output) : FALSE;
01217                             $this->showstatus($section_text['TEST'], 'SKIPPED', $reason);
01218                             return 'SKIPPED';
01219                      }
01220                      if (eregi("^info", $output)) {
01221                             $reason = (ereg("^info[[:space:]]*(.+)\$", $output)) ? ereg_replace("^info[[:space:]]*(.+)\$", "\\1", $output) : FALSE;
01222                             if ($reason) {
01223                                    $tested .= " (info: $reason)";
01224                             }
01225                      }
01226               }
01227               return NULL;
01228        }
01229 
01230        //
01231        //  Run an individual test case.
01232        //
01233        function run_test($file)
01234        {
01235               if ($this->conf['TEST_PHP_DETAILED'])
01236                      $this->writemsg("\n=================\nTEST $file\n");
01237        
01238               $section_text = $this->getSectionText($file);
01239        
01240               if ($this->iswin32)
01241                      $shortname = str_replace($this->conf['TEST_BASE_PATH'].'\\', '', $file);
01242               else
01243                      $shortname = str_replace($this->conf['TEST_BASE_PATH'].'/', '', $file);
01244               $tested = $section_text['TEST']." [$shortname]";
01245        
01246               if ($this->conf['TEST_WEB']) {
01247                      $tmp_file   = ereg_replace('\.phpt$','.'.$this->conf['TEST_WEB_EXT'],$file);
01248                      $uri = $this->conf['TEST_BASE_SCRIPT_NAME'].str_replace($this->conf['TEST_BASE_PATH'], '', $tmp_file);
01249                      $uri = str_replace('\\', '/', $uri);
01250               } else {
01251                      $tmp_file   = ereg_replace('\.phpt$','.php',$file);
01252               }
01253               @unlink($tmp_file);
01254        
01255               // unlink old test results  
01256               @unlink(ereg_replace('\.phpt$','.diff',$file));
01257               @unlink(ereg_replace('\.phpt$','.log',$file));
01258               @unlink(ereg_replace('\.phpt$','.exp',$file));
01259               @unlink(ereg_replace('\.phpt$','.out',$file));
01260        
01261               if (!$this->conf['TEST_WEB']) {
01262                      // Reset environment from any previous test.
01263                      $env = $this->getENVSettings($section_text,$tmp_file);
01264                      $ini_overwrites = $this->getINIParams($section_text);
01265               }
01266               
01267               // if this is a cgi test, prepare for it
01268               $query_string = '';
01269               $havepost = array_key_exists('POST', $section_text) && !empty($section_text['POST']);
01270               // allow empty query_string requests
01271               $haveget = array_key_exists('GET', $section_text) && !empty($section_text['GET']);
01272               $do_cgi = array_key_exists('CGI', $section_text) || $haveget || $havepost;
01273 
01274               $skipreason = $this->getSkipReason($file,$section_text,$do_cgi);
01275               if ($skipreason == 'SKIPPED') {
01276                      return $skipreason;
01277               }
01278 
01279               // We've satisfied the preconditions - run the test!
01280               file_put_contents($tmp_file,$section_text['FILE']);
01281 
01282               $post = NULL;
01283               $args = "";
01284 
01285               $headers = array();
01286               if ($this->conf['TEST_WEB']) {
01287                      $request = $this->getEvalTestSettings(@$section_text['REQUEST'],$tmp_file);
01288                      $headers = $this->getEvalTestSettings(@$section_text['HEADERS'],$tmp_file);
01289 
01290                      $method = isset($request['method'])?$request['method']:$havepost?'POST':'GET';
01291                      $query_string = $haveget?$section_text['GET']:'';
01292               
01293                      $options = array();
01294                      $options['method']=$method;
01295                      if (isset($this->conf['timeout']))    $options['timeout']    = $this->conf['timeout'];
01296                      if (isset($this->conf['proxy_host'])) $options['proxy_host'] = $this->conf['proxy_host'];
01297                      if (isset($this->conf['proxy_port'])) $options['proxy_port'] = $this->conf['proxy_port'];
01298                      if (isset($this->conf['proxy_user'])) $options['proxy_user'] = $this->conf['proxy_user'];
01299                      if (isset($this->conf['proxy_pass'])) $options['proxy_pass'] = $this->conf['proxy_pass'];
01300                      
01301                      $post = $havepost?$section_text['POST']:NULL;
01302                      $url = $this->conf['TEST_SERVER_URL'];
01303                      if (isset($request['SCRIPT_NAME']))
01304                             $url .= $request['SCRIPT_NAME'];
01305                      else
01306                             $url .= $uri;
01307                      if (isset($request['PATH_INFO']))
01308                             $url .= $request['PATH_INFO'];
01309                      if (isset($request['FRAGMENT']))
01310                             $url .= '#'.$request['FRAGMENT'];
01311                      if (isset($request['QUERY_STRING']))
01312                             $query_string = $request['QUERY_STRING'];
01313                      if ($query_string)
01314                             $url .= '?'.$query_string;
01315                      if ($this->conf['TEST_PHP_DETAILED'])
01316                             $this->writemsg("\nURL  = $url\n");
01317               } else if ($do_cgi) {
01318                      $query_string = $haveget?$section_text['GET']:'';
01319                      
01320                      if (!array_key_exists('GATEWAY_INTERFACE', $env))
01321                             $env['GATEWAY_INTERFACE']='CGI/1.1';
01322                      if (!array_key_exists('SERVER_SOFTWARE', $env))
01323                             $env['SERVER_SOFTWARE']='PHP Test Harness';
01324                      if (!array_key_exists('SERVER_SOFTWARE', $env))
01325                             $env['SERVER_NAME']='127.0.0.1';
01326                      if (!array_key_exists('REDIRECT_STATUS', $env))
01327                             $env['REDIRECT_STATUS']='200';
01328                      if (!array_key_exists('SERVER_NAME', $env))
01329                             $env['QUERY_STRING']=$query_string;
01330                      if (!array_key_exists('PATH_TRANSLATED', $env) &&
01331                             !array_key_exists('SCRIPT_FILENAME', $env)) {
01332                             $env['PATH_TRANSLATED']=$tmp_file;
01333                             $env['SCRIPT_FILENAME']=$tmp_file;
01334                      }
01335                      if (!array_key_exists('PATH_TRANSLATED', $env))
01336                             $env['PATH_TRANSLATED']='';
01337                      if (!array_key_exists('PATH_INFO', $env))
01338                             $env['PATH_INFO']='';
01339                      if (!array_key_exists('SCRIPT_NAME', $env))
01340                             $env['SCRIPT_NAME']='';
01341                      if (!array_key_exists('SCRIPT_FILENAME', $env))
01342                             $env['SCRIPT_FILENAME']='';
01343               
01344                      if (array_key_exists('POST', $section_text) && (!$haveget || !empty($section_text['POST']))) {
01345                             $post = $section_text['POST'];
01346                             $content_length = strlen($post);
01347                             if (!array_key_exists('REQUEST_METHOD', $env))
01348                                    $env['REQUEST_METHOD']='POST';
01349                             if (!array_key_exists('CONTENT_TYPE', $env))
01350                                    $env['CONTENT_TYPE']='application/x-www-form-urlencoded';
01351                             if (!array_key_exists('CONTENT_LENGTH', $env))
01352                                    $env['CONTENT_LENGTH']=$content_length;
01353                      } else {
01354                             if (!array_key_exists('REQUEST_METHOD', $env))
01355                                    $env['REQUEST_METHOD']='GET';
01356                             if (!array_key_exists('CONTENT_TYPE', $env))
01357                                    $env['CONTENT_TYPE']='';
01358                             if (!array_key_exists('CONTENT_LENGTH', $env))
01359                                    $env['CONTENT_LENGTH']='';
01360                      }
01361                      if ($this->conf['TEST_PHP_DETAILED'] > 1)
01362                             $this->writemsg("\nCONTENT_LENGTH  = " . $env['CONTENT_LENGTH'] . 
01363                                           "\nCONTENT_TYPE    = " . $env['CONTENT_TYPE'] . 
01364                                           "\nPATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . 
01365                                           "\nPATH_INFO       = " . $env['PATH_INFO'] . 
01366                                           "\nQUERY_STRING    = " . $env['QUERY_STRING'] . 
01367                                           "\nREDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . 
01368                                           "\nREQUEST_METHOD  = " . $env['REQUEST_METHOD'] . 
01369                                           "\nSCRIPT_NAME     = " . $env['SCRIPT_NAME'] . 
01370                                           "\nSCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . "\n");
01371                      /* not cgi spec to put query string on command line,
01372                         but used by a couple tests to catch a security hole
01373                         in older php versions.  At least IIS can be configured
01374                         to do this. */
01375                      $args = $env['QUERY_STRING'];
01376                      $args = "$ini_overwrites $tmp_file \"$args\" 2>&1";
01377               } else {
01378                      $args = $section_text['ARGS'] ? $section_text['ARGS'] : '';
01379                      $args = "$ini_overwrites $tmp_file $args 2>&1";
01380               }
01381 
01382               if ($this->conf['TEST_WEB']) {
01383                      // we want headers also, so fopen
01384                      $r = new HTTPRequest($url,$headers,$options,$post);
01385                      //$out = preg_replace("/\r\n/","\n",$r->response);
01386                      $out = $r->response;
01387                      $headers = $r->response_headers;
01388                      //print $r->outgoing_payload."\n";
01389                      //print $r->incoming_payload."\n";
01390               } else {
01391                      $out = execute($this->conf['TEST_PHP_EXECUTABLE'],$args,$post,$this->cwd,$env);
01392                      // if this is a cgi, remove the headers first
01393                      if ($this->test_executable_iscgi
01394                              && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) {
01395                             $out = $match[2];
01396                             $rh = preg_split("/[\n\r]+/",$match[1]);
01397                             $headers = array();
01398                             foreach ($rh as $line) {
01399                                    if (strpos($line, ':')!==false) {
01400                                           $line = explode(":", $line, 2);
01401                                           $headers[trim($line[0])] = trim($line[1]);
01402                                    }
01403                             }
01404                      }
01405               }
01406               
01407               if ($this->conf['TEST_PHP_DETAILED'] > 2) {
01408                      echo "HEADERS: ";
01409                      print_r($headers);
01410                      echo "OUTPUT: \n$out\n";
01411                      
01412               }
01413                      
01414               // Does the output match what is expected?
01415               $output = trim($out);
01416               $output = preg_replace('/\r\n/',"\n",$output);
01417 
01418               $failed = FALSE;
01419 
01420               if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
01421                      if (isset($section_text['EXPECTF'])) {
01422                             $wanted = $section_text['EXPECTF'];
01423                      } else {
01424                             $wanted = $section_text['EXPECTREGEX'];
01425                      }
01426                      $wanted_re = preg_replace('/\r\n/',"\n",$wanted);
01427                      if (isset($section_text['EXPECTF'])) {
01428                             $wanted_re = preg_quote($wanted_re, '/');
01429                             // Stick to basics
01430                             $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
01431                             $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
01432                             $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
01433                             $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
01434                             $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
01435                             $wanted_re = str_replace("%c", ".", $wanted_re);
01436                             // %f allows two points "-.0.0" but that is the best *simple* expression
01437                      }
01438        /* DEBUG YOUR REGEX HERE
01439                      var_dump($wanted_re);
01440                      print(str_repeat('=', 80) . "\n");
01441                      var_dump($output);
01442        */
01443                      $failed = !preg_match("/^$wanted_re\$/s", $output);
01444               }
01445               
01446               $skipexpect = false;
01447               if (!$failed && $this->conf['TEST_WEB'] && isset($section_text['EXPECTHEADERS'])) {
01448                      $want = array();
01449                      $lines = preg_split("/[\n\r]+/",$section_text['EXPECTHEADERS']);
01450                      $wanted='';
01451             foreach ($lines as $line) {
01452                 if (strpos($line, ':')!==false) {
01453                     $line = explode(":", $line, 2);
01454                     $want[trim($line[0])] = trim($line[1]);
01455                                    $wanted .= trim($line[0]).': '.trim($line[1])."\n";
01456                 }
01457             }
01458                      $output='';
01459                      foreach ($want as $k=>$v) {
01460                             $output .= "$k: {$headers[$k]}\n";
01461                             if (!isset($headers[$k]) || $headers[$k] != $v) {
01462                                    $failed = TRUE;
01463                             }
01464                      }
01465                      
01466                      // different servers may do different things on non-200 results
01467                      // for instance, IIS will deliver it's own error pages, so we
01468                      // cannot expect to match up the EXPECT section.  We may however,
01469                      // want to match EXPECT on more than 200 results, so this may
01470                      // need to change later.
01471                      $skipexpect = isset($headers['Status']) && $headers['Status'] != 200;
01472               }
01473                      
01474               if (!$failed && !$skipexpect && isset($section_text['EXPECT'])) {
01475                      $wanted = $section_text['EXPECT'];
01476                      $wanted = preg_replace('/\r\n/',"\n",$wanted);
01477                      $failed = (0 != strcmp($output,$wanted));
01478               }
01479               
01480               if (!$failed) {
01481                      @unlink($tmp_file);
01482                      $this->showstatus($tested, 'PASSED');
01483                      return 'PASSED';
01484               }
01485                      
01486               // Test failed so we need to report details.
01487               $this->showstatus($tested, 'FAILED');
01488        
01489               $this->failed_tests[] = array(
01490                                                  'name' => $file,
01491                                                  'test_name' => $tested,
01492                                                  'output' => ereg_replace('\.phpt$','.log', $file),
01493                                                  'diff'   => ereg_replace('\.phpt$','.diff', $file)
01494                                                  );
01495        
01496               if ($this->conf['TEST_PHP_DETAILED'])
01497                      $this->writemsg(generate_diff($wanted,$output)."\n");
01498                      
01499               // write .exp
01500               if (strpos($this->conf['TEST_PHP_LOG_FORMAT'],'E') !== FALSE) {
01501                      $logname = ereg_replace('\.phpt$','.exp',$file);
01502                      file_put_contents($logname,$wanted);
01503               }
01504        
01505               // write .out
01506               if (strpos($this->conf['TEST_PHP_LOG_FORMAT'],'O') !== FALSE) {
01507                      $logname = ereg_replace('\.phpt$','.out',$file);
01508                      file_put_contents($logname,$output);
01509               }
01510        
01511               // write .diff
01512               if (strpos($this->conf['TEST_PHP_LOG_FORMAT'],'D') !== FALSE) {
01513                      $logname = ereg_replace('\.phpt$','.diff',$file);
01514                      file_put_contents($logname,generate_diff($wanted,$output));
01515               }
01516        
01517               // write .log
01518               if (strpos($this->conf['TEST_PHP_LOG_FORMAT'],'L') !== FALSE) {
01519                      $logname = ereg_replace('\.phpt$','.log',$file);
01520                      file_put_contents($logname,
01521                                           "\n---- EXPECTED OUTPUT\n$wanted\n".
01522                                           "---- ACTUAL OUTPUT\n$output\n".
01523                                           "---- FAILED\n");
01524                      // display emacs/msvc error output
01525                      if (strpos($this->conf['TEST_PHP_LOG_FORMAT'],'C') !== FALSE) {
01526                             $this->error_report($file,$logname,$tested);
01527                      }
01528               }
01529               return 'FAILED';
01530        }
01531 
01532        //
01533        //  Write an error in a format recognizable to Emacs or MSVC.
01534        //
01535        function error_report($testname,$logname,$tested) 
01536        {
01537               $testname = realpath($testname);
01538               $logname  = realpath($logname);
01539               switch ($this->conf['TEST_PHP_ERROR_STYLE']) {
01540               default:
01541               case 'MSVC':
01542                      $this->writemsg($testname . "(1) : $tested\n");
01543                      $this->writemsg($logname . "(1) :  $tested\n");
01544                      break;
01545               case 'EMACS':
01546                      $this->writemsg($testname . ":1: $tested\n");
01547                      $this->writemsg($logname . ":1:  $tested\n");
01548                      break;
01549               }
01550        }
01551 
01552        function error($message)
01553        {
01554               $this->writemsg("ERROR: {$message}\n");
01555               exit(1);
01556        }
01557 }
01558 
01559 $test = new testHarness();
01560 /*
01561  * Local variables:
01562  * tab-width: 4
01563  * c-basic-offset: 4
01564  * End:
01565  * vim600: fdm=marker
01566  * vim: noet sw=4 ts=4
01567  */
01568 ?>