RunTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 5 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available through the world-wide-web at the following url: |
  11. // | http://www.php.net/license/3_0.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | [email protected] so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Tomas V.V.Cox <[email protected]> |
  17. // | Greg Beaver <[email protected]> |
  18. // | |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: RunTest.php,v 1.3.2.4 2005/02/17 17:47:55 cellog Exp $
  22. //
  23. /**
  24. * Simplified version of PHP's test suite
  25. * -- EXPERIMENTAL --
  26. Try it with:
  27. $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
  28. TODO:
  29. Actually finish the development and testing
  30. */
  31. require_once 'PEAR.php';
  32. require_once 'PEAR/Config.php';
  33. define('DETAILED', 1);
  34. putenv("PHP_PEAR_RUNTESTS=1");
  35. class PEAR_RunTest
  36. {
  37. var $_logger;
  38. /**
  39. * An object that supports the PEAR_Common->log() signature, or null
  40. * @param PEAR_Common|null
  41. */
  42. function PEAR_RunTest($logger = null)
  43. {
  44. $this->_logger = $logger;
  45. }
  46. //
  47. // Run an individual test case.
  48. //
  49. function run($file, $ini_settings = '')
  50. {
  51. $cwd = getcwd();
  52. $conf = &PEAR_Config::singleton();
  53. $php = $conf->get('php_bin');
  54. //var_dump($php);exit;
  55. global $log_format, $info_params, $ini_overwrites;
  56. $info_params = '';
  57. $log_format = 'LEOD';
  58. // Load the sections of the test file.
  59. $section_text = array(
  60. 'TEST' => '(unnamed test)',
  61. 'SKIPIF' => '',
  62. 'GET' => '',
  63. 'ARGS' => '',
  64. );
  65. if (!is_file($file) || !$fp = fopen($file, "r")) {
  66. return PEAR::raiseError("Cannot open test file: $file");
  67. }
  68. $section = '';
  69. while (!feof($fp)) {
  70. $line = fgets($fp);
  71. // Match the beginning of a section.
  72. if (ereg('^--([A-Z]+)--',$line,$r)) {
  73. $section = $r[1];
  74. $section_text[$section] = '';
  75. continue;
  76. }
  77. // Add to the section text.
  78. $section_text[$section] .= $line;
  79. }
  80. fclose($fp);
  81. $shortname = str_replace($cwd.'/', '', $file);
  82. $tested = trim($section_text['TEST'])." [$shortname]";
  83. $tmp = realpath(dirname($file));
  84. $tmp_skipif = $tmp . uniqid('/phpt.');
  85. $tmp_file = ereg_replace('\.phpt$','.php',$file);
  86. $tmp_post = $tmp . uniqid('/phpt.');
  87. @unlink($tmp_skipif);
  88. @unlink($tmp_file);
  89. @unlink($tmp_post);
  90. // unlink old test results
  91. @unlink(ereg_replace('\.phpt$','.diff',$file));
  92. @unlink(ereg_replace('\.phpt$','.log',$file));
  93. @unlink(ereg_replace('\.phpt$','.exp',$file));
  94. @unlink(ereg_replace('\.phpt$','.out',$file));
  95. // Check if test should be skipped.
  96. $info = '';
  97. $warn = false;
  98. if (array_key_exists('SKIPIF', $section_text)) {
  99. if (trim($section_text['SKIPIF'])) {
  100. $this->save_text($tmp_skipif, $section_text['SKIPIF']);
  101. //$extra = substr(PHP_OS, 0, 3) !== "WIN" ?
  102. // "unset REQUEST_METHOD;": "";
  103. //$output = `$extra $php $info_params -f $tmp_skipif`;
  104. $output = `$php $info_params -f $tmp_skipif`;
  105. unlink($tmp_skipif);
  106. if (eregi("^skip", trim($output))) {
  107. $skipreason = "SKIP $tested";
  108. $reason = (eregi("^skip[[:space:]]*(.+)\$", trim($output))) ? eregi_replace("^skip[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
  109. if ($reason) {
  110. $skipreason .= " (reason: $reason)";
  111. }
  112. $this->_logger->log(0, $skipreason);
  113. if (isset($old_php)) {
  114. $php = $old_php;
  115. }
  116. return 'SKIPPED';
  117. }
  118. if (eregi("^info", trim($output))) {
  119. $reason = (ereg("^info[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^info[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
  120. if ($reason) {
  121. $info = " (info: $reason)";
  122. }
  123. }
  124. if (eregi("^warn", trim($output))) {
  125. $reason = (ereg("^warn[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^warn[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
  126. if ($reason) {
  127. $warn = true; /* only if there is a reason */
  128. $info = " (warn: $reason)";
  129. }
  130. }
  131. }
  132. }
  133. // We've satisfied the preconditions - run the test!
  134. $this->save_text($tmp_file,$section_text['FILE']);
  135. $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
  136. $cmd = "$php$ini_settings -f $tmp_file$args 2>&1";
  137. if (isset($this->_logger)) {
  138. $this->_logger->log(2, 'Running command "' . $cmd . '"');
  139. }
  140. $savedir = getcwd(); // in case the test moves us around
  141. if (isset($section_text['RETURNS'])) {
  142. ob_start();
  143. system($cmd, $return_value);
  144. $out = ob_get_contents();
  145. ob_end_clean();
  146. @unlink($tmp_post);
  147. $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
  148. $returnfail = ($return_value != $section_text['RETURNS']);
  149. } else {
  150. $out = `$cmd`;
  151. $returnfail = false;
  152. }
  153. chdir($savedir);
  154. // Does the output match what is expected?
  155. $output = trim($out);
  156. $output = preg_replace('/\r\n/', "\n", $output);
  157. if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
  158. if (isset($section_text['EXPECTF'])) {
  159. $wanted = trim($section_text['EXPECTF']);
  160. } else {
  161. $wanted = trim($section_text['EXPECTREGEX']);
  162. }
  163. $wanted_re = preg_replace('/\r\n/',"\n",$wanted);
  164. if (isset($section_text['EXPECTF'])) {
  165. $wanted_re = preg_quote($wanted_re, '/');
  166. // Stick to basics
  167. $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
  168. $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
  169. $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
  170. $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
  171. $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
  172. $wanted_re = str_replace("%c", ".", $wanted_re);
  173. // %f allows two points "-.0.0" but that is the best *simple* expression
  174. }
  175. /* DEBUG YOUR REGEX HERE
  176. var_dump($wanted_re);
  177. print(str_repeat('=', 80) . "\n");
  178. var_dump($output);
  179. */
  180. if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
  181. @unlink($tmp_file);
  182. $this->_logger->log(0, "PASS $tested$info");
  183. if (isset($old_php)) {
  184. $php = $old_php;
  185. }
  186. return 'PASSED';
  187. }
  188. } else {
  189. $wanted = trim($section_text['EXPECT']);
  190. $wanted = preg_replace('/\r\n/',"\n",$wanted);
  191. // compare and leave on success
  192. $ok = (0 == strcmp($output,$wanted));
  193. if (!$returnfail && $ok) {
  194. @unlink($tmp_file);
  195. $this->_logger->log(0, "PASS $tested$info");
  196. if (isset($old_php)) {
  197. $php = $old_php;
  198. }
  199. return 'PASSED';
  200. }
  201. }
  202. // Test failed so we need to report details.
  203. if ($warn) {
  204. $this->_logger->log(0, "WARN $tested$info");
  205. } else {
  206. $this->_logger->log(0, "FAIL $tested$info");
  207. }
  208. if (isset($section_text['RETURNS'])) {
  209. $GLOBALS['__PHP_FAILED_TESTS__'][] = array(
  210. 'name' => $file,
  211. 'test_name' => $tested,
  212. 'output' => ereg_replace('\.phpt$','.log', $file),
  213. 'diff' => ereg_replace('\.phpt$','.diff', $file),
  214. 'info' => $info,
  215. 'return' => $return_value
  216. );
  217. } else {
  218. $GLOBALS['__PHP_FAILED_TESTS__'][] = array(
  219. 'name' => $file,
  220. 'test_name' => $tested,
  221. 'output' => ereg_replace('\.phpt$','.log', $file),
  222. 'diff' => ereg_replace('\.phpt$','.diff', $file),
  223. 'info' => $info,
  224. );
  225. }
  226. // write .exp
  227. if (strpos($log_format,'E') !== FALSE) {
  228. $logname = ereg_replace('\.phpt$','.exp',$file);
  229. if (!$log = fopen($logname,'w')) {
  230. return PEAR::raiseError("Cannot create test log - $logname");
  231. }
  232. fwrite($log,$wanted);
  233. fclose($log);
  234. }
  235. // write .out
  236. if (strpos($log_format,'O') !== FALSE) {
  237. $logname = ereg_replace('\.phpt$','.out',$file);
  238. if (!$log = fopen($logname,'w')) {
  239. return PEAR::raiseError("Cannot create test log - $logname");
  240. }
  241. fwrite($log,$output);
  242. fclose($log);
  243. }
  244. // write .diff
  245. if (strpos($log_format,'D') !== FALSE) {
  246. $logname = ereg_replace('\.phpt$','.diff',$file);
  247. if (!$log = fopen($logname,'w')) {
  248. return PEAR::raiseError("Cannot create test log - $logname");
  249. }
  250. fwrite($log, $this->generate_diff($wanted, $output,
  251. isset($section_text['RETURNS']) ? array(trim($section_text['RETURNS']),
  252. $return_value) : null));
  253. fclose($log);
  254. }
  255. // write .log
  256. if (strpos($log_format,'L') !== FALSE) {
  257. $logname = ereg_replace('\.phpt$','.log',$file);
  258. if (!$log = fopen($logname,'w')) {
  259. return PEAR::raiseError("Cannot create test log - $logname");
  260. }
  261. fwrite($log,"
  262. ---- EXPECTED OUTPUT
  263. $wanted
  264. ---- ACTUAL OUTPUT
  265. $output
  266. ---- FAILED
  267. ");
  268. if ($returnfail) {
  269. fwrite($log,"
  270. ---- EXPECTED RETURN
  271. $section_text[RETURNS]
  272. ---- ACTUAL RETURN
  273. $return_value
  274. ");
  275. }
  276. fclose($log);
  277. //error_report($file,$logname,$tested);
  278. }
  279. if (isset($old_php)) {
  280. $php = $old_php;
  281. }
  282. return $warn ? 'WARNED' : 'FAILED';
  283. }
  284. function generate_diff($wanted, $output, $return_value)
  285. {
  286. $w = explode("\n", $wanted);
  287. $o = explode("\n", $output);
  288. $w1 = array_diff_assoc($w,$o);
  289. $o1 = array_diff_assoc($o,$w);
  290. $w2 = array();
  291. $o2 = array();
  292. foreach($w1 as $idx => $val) $w2[sprintf("%03d<",$idx)] = sprintf("%03d- ", $idx+1).$val;
  293. foreach($o1 as $idx => $val) $o2[sprintf("%03d>",$idx)] = sprintf("%03d+ ", $idx+1).$val;
  294. $diff = array_merge($w2, $o2);
  295. ksort($diff);
  296. if ($return_value) {
  297. $extra = "##EXPECTED: $return_value[0]\r\n##RETURNED: $return_value[1]";
  298. } else {
  299. $extra = '';
  300. }
  301. return implode("\r\n", $diff) . $extra;
  302. }
  303. //
  304. // Write the given text to a temporary file, and return the filename.
  305. //
  306. function save_text($filename, $text)
  307. {
  308. if (!$fp = fopen($filename, 'w')) {
  309. return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
  310. }
  311. fwrite($fp,$text);
  312. fclose($fp);
  313. if (1 < DETAILED) echo "
  314. FILE $filename {{{
  315. $text
  316. }}}
  317. ";
  318. }
  319. }
  320. ?>