| | (based on the PHP 3 test framework by Rasmus Lerdorf) | +----------------------------------------------------------------------+ */ /* * TODO: * - look for test-specific php.ini files * - implement module skipping for PEAR * - do not test PEAR components if base class and/or component class cannot be instanciated */ ob_implicit_flush(); define('TEST_PASSED', 0); define('TEST_FAILED', -1); define('TEST_SKIPPED', -2); define('TEST_INTERNAL_ERROR', -3); define('EXT_DIR_NAME','/php4/ext/'); define('EXT_DIR_NAME_LEN',strlen(EXT_DIR_NAME)); initialize(); /* $opts = parse_options(&$argc, &$argv); if ($opts['help']) { usage(); exit; } */ do_testing($argc, $argv); exit; /*****************************************************************************/ function usage() { dowriteln("Usage: run-tests.php [-d] [-h] [dir|file...]"); } /* * Please use dowrite() and dowriteln() for all screen output. * This makes it easier to convert to HTML output later. */ function dowrite($str) { global $term_bold, $term_norm; $str = str_replace("%b", $term_bold, $str); $str = str_replace("%B", $term_norm, $str); print $str; } function dowriteln($str) { dowrite("$str\n"); } function create_compiled_in_modules_list() { global $compiled_in_modules; $compiled_in_modules = @get_loaded_extensions(); } function extract_module_name_from_path($path) { if ($pos1=strpos($path,EXT_DIR_NAME)) { $pos3=strpos($path,'/',$pos1+EXT_DIR_NAME_LEN); return substr($path,$pos2=$pos1+EXT_DIR_NAME_LEN,$pos3-$pos2); } } function create_found_tests_4_modules_list() { global $modules_2_test,$testdirs; foreach ($testdirs AS $path) { if ($mod_name=extract_module_name_from_path($path)) $modules_2_test[$mod_name]=TRUE; } } function create_modules_2_test_list() { global $compiled_in_modules,$modules_2_test,$modules_available; foreach ($compiled_in_modules AS $value) if ($modules_2_test[$value]) $modules_available[]=$value; } function initialize() { global $term, $windows_p, $php, $skip, $testdirs, $tmpfile, $skipped, $failed, $passed, $total, $term_bold, $term_norm, $tests_in_dir; // XXX Should support HTML output as well. $term = getenv("TERM"); if (ereg('^(xterm|vt220)', $term)) { $term_bold = sprintf("%c%c%c%c", 27, 91, 49, 109); $term_norm = sprintf("%c%c%c", 27, 91, 109); } elseif (ereg('^vt100', $term)) { $term_bold = sprintf("%c%c%c%c", 27, 91, 49, 109); $term_norm = sprintf("%c%c%c", 27, 91, 109); } else { $term_bold = $term_norm = ""; } if((substr(PHP_OS, 0, 3) == "WIN")) { $windows_p = true; $term = getenv("COMSPEC"); $null = getenv("TOP_BUILDDIR"); $php = ($null ? $null : getcwd()) . "/php.exe"; unset($null); } else { if (isset($GLOBALS["TOP_BUILDDIR"])) { $php = $GLOBALS["TOP_BUILDDIR"]."/php"; } else { $php = getcwd() . '/php'; } } create_compiled_in_modules_list(); if (!is_executable($php) && !$windows_p) { dowriteln("PHP CGI binary ($php) is not executable."); dowriteln("Please compile PHP as a CGI executable and try again."); exit; } $skip = array( "CVS" => 1 ); $testdirs = array(); $tmpfile = array(); $tests_in_dir = array(); register_shutdown_function("delete_tmpfiles"); $skipped = $failed = $passed = $total = 0; } function &parse_options(&$argc, &$argv) { $options = array(); while ($argc > 0 && ($opt = substr($argv[0], 0, 2)) == "--") { $opt = array_shift($argv); $argc--; if ($arg == "--") { return $options; } if (ereg('^--([^=]+)=(.*)$', $opt, $matches)) { $opt = $matches[1]; $arg = $matches[2]; } else { $arg = true; } $options[$opt] = $arg; } return $options; } function do_testing($argc, &$argv) { global $term, $windows_p, $php, $skip, $testdirs, $tmpfile, $opts, $skipped, $failed, $passed, $total, $term_bold, $term_norm, $skipped_extensions; if ($argc > 1) { if (is_dir($argv[1])) { $dir = $argv[1]; } else { for ($i = 1; $i < $argc; $i++) { switch (run_test($argv[$i])) { case TEST_SKIPPED: case TEST_INTERNAL_ERROR: $skipped++; break; case TEST_FAILED: $failed++; break; case TEST_PASSED: $passed++; break; } $total++; } } } else { // $dir = $GLOBALS["TOP_SRCDIR"]; // XXX ??? where should this variable be set? $dir=str_replace('\\','/',trim(($windows_p ? getenv("TEST_DIR"):`pwd`))); } if (isset($dir) && $dir) { find_testdirs($dir); create_found_tests_4_modules_list(); create_modules_2_test_list(); for ($i = 0; $i < sizeof($testdirs); $i++) { run_tests_in_dir($testdirs[$i]); } } $counting = $total - $skipped; if ($counting <= 0) { dowriteln("No tests were run."); return; } $total_d = (double)$total; $counting_d = (double)$counting; $passed_p = 100 * ($passed / $counting_d); $failed_p = 100 * ($failed / $counting_d); $skipped_p = 100 * ($skipped / $total_d); $passed_pstr = sprintf($passed_p < 10.0 ? "%1.1f" : "%3.0f", $passed_p); $failed_pstr = sprintf($failed_p < 10.0 ? "%1.1f" : "%3.0f", $failed_p); $skipped_pstr = sprintf($skipped_p < 10.0 ? "%1.1f" : "%3.0f", $skipped_p); dowriteln("TEST RESULT SUMMARY"); dowriteln("============================="); dowriteln(sprintf("Number of tests: %4d", $total)); dowriteln(sprintf("Tests skipped: %4d (%s%%)", $skipped, $skipped_pstr)); dowriteln(sprintf("Tests failed: %4d (%s%%)", $failed, $failed_pstr)); dowriteln(sprintf("Tests passed: %4d (%s%%)", $passed, $passed_pstr)); dowriteln("============================="); dowriteln("Skipped ".sizeof($skipped_extensions)." extensions."); dowriteln("PHP Version: ".phpversion()); } function find_testdirs($dir = '.', $first_pass = true) { global $testdirs, $skip; if ($first_pass && is_dir($dir)) { $testdirs[] = $dir; } $dp = @opendir($dir); if (!$dp) { print "Warning: could not open directory $dir\n"; return false; } while ($ent = readdir($dp)) { $path = "$dir/$ent"; if ((isset($skip[$ent]) && $skip[$ent]) || substr($ent, 0, 1) == "." || !is_dir($path) ) { continue; } if (strstr("/$path/", "/tests/")) { $testdirs[] = $path; } find_testdirs($path, false); } closedir($dp); } function run_tests_in_dir($dir = '.') { global $skip, $skipped, $failed, $passed, $total, $opts, $tests_in_dir,$modules_available,$skipped_extensions; $dp = opendir($dir); if (!$dp) { print "Warning: could not run tests in $dir\n"; return false; } $testfiles = array(); while ($ent = readdir($dp)) { if ((isset($skip[$ent]) && $skip[$ent]) || substr($ent, 0, 1) == "." || substr($ent, -5) != ".phpt") { continue; } $testfiles[] = "$dir/$ent"; if(isset($tests_in_dir[$dir])) $tests_in_dir[$dir]++; else $tests_in_dir[$dir]=1; } closedir($dp); if (isset($tests_in_dir[$dir]) && ($tests_in_dir[$dir] == 0)) { return true; } $oskipped = $skipped; if (sizeof($testfiles) == 0) { return; } if ($mod_name=extract_module_name_from_path($dir)) { if ($ext_found=in_array($mod_name,$modules_available)) dowriteln("Testing extension: $mod_name"); else $skipped_extensions[$mod_name]=TRUE; } if ($ext_found!==FALSE) { dowriteln("%bRunning tests in $dir%B"); dowriteln("=================".str_repeat("=", strlen($dir))); sort($testfiles); for ($i = 0; $i < sizeof($testfiles); $i++) { switch (run_test($testfiles[$i])) { case TEST_SKIPPED: case TEST_INTERNAL_ERROR: $skipped++; break; case TEST_FAILED: $failed++; break; case TEST_PASSED: $passed++; break; } $total++; } if ($oskipped + (isset($tests_in_dir[$dir])?$tests_in_dir[$dir]:0) == $skipped) { $skippednow = $skipped - $oskipped; dowriteln("[all $skippednow test(s) skipped]"); } dowriteln(""); } return true; } function skip_headers($fp) { while (!feof($fp)) { if (trim(fgets($fp, 1024)) == "") { break; } } } function delete_tmpfiles() { global $tmpfile; reset($tmpfile); while (list($k, $v) = each($tmpfile)) { if (file_exists($v)) { //print "unlink($v): "; var_dump(unlink($v)); unlink($v); } } } /** * Compares two files, ignoring blank lines. * * @param $file1 string name of first file to compare * @param $file2 string name of second file to compare * * @return bool whether the files were "equal" */ function compare_results($file1, $file2) { $data1 = $data2 = ""; if (!($fp1 = @fopen($file1, "r")) || !($fp2 = @fopen($file2, "r"))) { return false; } while (!(feof($fp1) || feof($fp2))) { if (!feof($fp1) && trim($line1 = fgets($fp1, 10240)) != "") { //print "adding line1 $line1\n"; $data1 .= trim($line1); } if (!feof($fp2) && trim($line2 = fgets($fp2, 10240)) != "") { //print "adding line2 $line2\n"; $data2 .= trim($line2); } } fclose($fp1); fclose($fp2); if ((trim($data1) != trim($data2)) || ($data1=='' && $data2=='')) { //print "data1=";var_dump($data1); //print "data2=";var_dump($data2); return false; } return true; } function run_test($file) { global $php, $tmpfile, $term_bold, $term_norm, $term, $windows_p; $variables = array("TEST", "POST", "GET", "FILE", "EXPECT", "SKIPIF", "OUTPUT"); $fp = @fopen($file, "r"); if (!$fp) { return TEST_INTERNAL_ERROR; } $tmpdir = dirname($file); $tmpfix = "phpt."; $tmpfile["FILE"] = tempnam($tmpdir, $tmpfix); $tmpfile["SKIPIF"] = tempnam($tmpdir, $tmpfix); $tmpfile["POST"] = tempnam($tmpdir, $tmpfix); $tmpfile["EXPECT"] = tempnam($tmpdir, $tmpfix); $tmpfile["OUTPUT"] = tempnam($tmpdir, $tmpfix); while ($line = fgets($fp, 4096)) { if (ereg('^--([A-Z]+)--', $line, $matches)) { $var = $matches[1]; if (isset($tmpfile[$var]) && $tmpfile[$var]) { $fps[$var] = @fopen($tmpfile[$var], "w"); } else { $$var = ''; } } else { if (isset($var) && $var) { if ($var == "POST") { $line = trim($line); } if (isset($fps[$var]) && $fps[$var]) { fwrite($fps[$var], $line); } else { $$var .= $line; } } } } if(isset($fps) && is_array($fps)) { reset($fps); while (list($k, $v) = each($fps)) { if (is_resource($v)) { fclose($v); } } } putenv("PHP_TEST=1"); putenv("REDIRECT_STATUS=1"); putenv("CONTENT_LENGTH="); putenv("QUERY_STRING=".(isset($GET)?$GET:"")); $include_path = ini_get("include_path"); if (isset($fps["SKIPIF"])) { $tmpfile["SKIPIF_OUTPUT"] = tempnam($tmpdir, $tmpfix); putenv("REQUEST_METHOD=GET"); putenv("CONTENT_TYPE="); putenv("PATH_TRANSLATED=$tmpfile[SKIPIF]"); putenv("SCRIPT_FILENAME=$tmpfile[SKIPIF]"); $skipifcmd = "$php -q -f $tmpfile[SKIPIF] > $tmpfile[SKIPIF_OUTPUT]"; system($skipifcmd, $ret); $sp = @fopen($tmpfile["SKIPIF_OUTPUT"], "r"); if ($sp) { skip_headers($sp); $skip = trim(fgets($sp, 1024)); fclose($sp); if ($skip == "skip") { delete_tmpfiles(); return TEST_SKIPPED; } } } putenv("PATH_TRANSLATED=$tmpfile[FILE]"); putenv("SCRIPT_FILENAME=$tmpfile[FILE]"); if (isset($fps["POST"])) { putenv("REQUEST_METHOD=POST"); putenv("CONTENT_TYPE=application/x-www-form-urlencoded"); putenv("CONTENT_LENGTH=".filesize($tmpfile["POST"])); } else { putenv("REQUEST_METHOD=GET"); putenv("CONTENT_TYPE="); putenv("CONTENT_LENGTH="); } if (isset($fps["POST"])) { if(!$windows_p) { $cmd = "2>&1 $php -q $tmpfile[FILE] < $tmpfile[POST]"; } else { $cmd = "$term /c " . realpath($php) ." -q $tmpfile[FILE] < $tmpfile[POST]"; } } else { if(!$windows_p) { $cmd = "2>&1 $php -q $tmpfile[FILE]"; } else { $cmd = "$term /c " . realpath($php) ." -q $tmpfile[FILE]";; } } $ofp = @fopen($tmpfile["OUTPUT"], "w"); if (!$ofp) { dowriteln("Error: could not write to output file"); delete_tmpfiles(); return TEST_INTERNAL_ERROR; } //echo $cmd; $cp = popen($cmd, "r"); if (!$cp) { dowriteln("Error: could not execute: $cmd"); delete_tmpfiles(); return TEST_INTERNAL_ERROR; } skip_headers($cp); while ($data = fread($cp, 2048)) { fwrite($ofp, $data); } fclose($ofp); pclose($cp); $desc = isset($TEST)?trim($TEST):""; $outfile = ereg_replace('\.phpt$', '.out', $file); $expectfile = ereg_replace('\.phpt$', '.exp', $file); $phpfile = ereg_replace('\.phpt$', '.php', $file); if (compare_results($tmpfile["OUTPUT"], $tmpfile["EXPECT"])) { $status = TEST_PASSED; $text = "passed"; $pre = $post = ""; if (file_exists($outfile)) { unlink($outfile); } if (file_exists($expectfile)) { unlink($expectfile); } if (file_exists($phpfile)) { unlink($phpfile); } } else { //system("env"); $status = TEST_FAILED; $text = "failed"; $pre = $term_bold; $post = $term_norm; $desc .= " (".basename($file).")"; if (file_exists($outfile)) { unlink($outfile); } copy($tmpfile["OUTPUT"], $outfile); copy($tmpfile["EXPECT"], $expectfile); copy($tmpfile["FILE"], $phpfile); } dowriteln(sprintf("%s%-68s ... %s%s", $pre, substr($desc, 0, 68), $text, $post)); // if ($status == TEST_FAILED) { // for ($i = 0; $i < sizeof($variables); $i++) { // $var = $variables[$i]; // print "$var:\n"; // if ($tmpfile[$var]) { // if (file_exists($tmpfile[$var])) { // system("cat ".$tmpfile[$var]); // } // } else { // print $$var; // } // } // print "--\n\n"; // } delete_tmpfiles(); return $status; } ?>