run.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. error_reporting(E_ALL | E_STRICT);
  3. ini_set('short_open_tag', false);
  4. if ('cli' !== php_sapi_name()) {
  5. die('This script is designed for running on the command line.');
  6. }
  7. function showHelp($error) {
  8. die($error . "\n\n" .
  9. <<<OUTPUT
  10. This script has to be called with the following signature:
  11. php run.php [--no-progress] testType pathToTestFiles
  12. The test type must be one of: PHP5, PHP7 or Symfony.
  13. The following options are available:
  14. --no-progress Disables showing which file is currently tested.
  15. OUTPUT
  16. );
  17. }
  18. $options = array();
  19. $arguments = array();
  20. // remove script name from argv
  21. array_shift($argv);
  22. foreach ($argv as $arg) {
  23. if ('-' === $arg[0]) {
  24. $options[] = $arg;
  25. } else {
  26. $arguments[] = $arg;
  27. }
  28. }
  29. if (count($arguments) !== 2) {
  30. showHelp('Too little arguments passed!');
  31. }
  32. $showProgress = true;
  33. $verbose = false;
  34. foreach ($options as $option) {
  35. if ($option === '--no-progress') {
  36. $showProgress = false;
  37. } elseif ($option === '--verbose') {
  38. $verbose = true;
  39. } else {
  40. showHelp('Invalid option passed!');
  41. }
  42. }
  43. $testType = $arguments[0];
  44. $dir = $arguments[1];
  45. switch ($testType) {
  46. case 'Symfony':
  47. $version = 'Php5';
  48. $fileFilter = function($path) {
  49. return preg_match('~\.php(?:\.cache)?$~', $path) && false === strpos($path, 'skeleton');
  50. };
  51. $codeExtractor = function($file, $code) {
  52. return $code;
  53. };
  54. break;
  55. case 'PHP5':
  56. case 'PHP7':
  57. $version = $testType === 'PHP5' ? 'Php5' : 'Php7';
  58. $fileFilter = function($path) {
  59. return preg_match('~\.phpt$~', $path);
  60. };
  61. $codeExtractor = function($file, $code) {
  62. if (preg_match('~(?:
  63. # skeleton files
  64. ext.gmp.tests.001
  65. | ext.skeleton.tests.001
  66. # multibyte encoded files
  67. | ext.mbstring.tests.zend_multibyte-01
  68. | Zend.tests.multibyte.multibyte_encoding_001
  69. | Zend.tests.multibyte.multibyte_encoding_004
  70. | Zend.tests.multibyte.multibyte_encoding_005
  71. # token_get_all bug (https://bugs.php.net/bug.php?id=60097)
  72. | Zend.tests.bug47516
  73. # pretty print difference due to INF vs 1e1000
  74. | ext.standard.tests.general_functions.bug27678
  75. | tests.lang.bug24640
  76. # pretty print difference due to nop statements
  77. | ext.mbstring.tests.htmlent
  78. | ext.standard.tests.file.fread_basic
  79. # tests using __halt_compiler as semi reserved keyword
  80. | Zend.tests.grammar.semi_reserved_001
  81. | Zend.tests.grammar.semi_reserved_002
  82. | Zend.tests.grammar.semi_reserved_005
  83. )\.phpt$~x', $file)) {
  84. return null;
  85. }
  86. if (!preg_match('~--FILE--\s*(.*?)--[A-Z]+--~s', $code, $matches)) {
  87. return null;
  88. }
  89. if (preg_match('~--EXPECT(?:F|REGEX)?--\s*(?:Parse|Fatal) error~', $code)) {
  90. return null;
  91. }
  92. return $matches[1];
  93. };
  94. break;
  95. default:
  96. showHelp('Test type must be one of: PHP5, PHP7 or Symfony');
  97. }
  98. require_once dirname(__FILE__) . '/../lib/PhpParser/Autoloader.php';
  99. PhpParser\Autoloader::register();
  100. $parserName = 'PhpParser\Parser\\' . $version;
  101. $parser = new $parserName(new PhpParser\Lexer\Emulative);
  102. $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
  103. $nodeDumper = new PhpParser\NodeDumper;
  104. $parseFail = $ppFail = $compareFail = $count = 0;
  105. $readTime = $parseTime = $ppTime = $reparseTime = $compareTime = 0;
  106. $totalStartTime = microtime(true);
  107. foreach (new RecursiveIteratorIterator(
  108. new RecursiveDirectoryIterator($dir),
  109. RecursiveIteratorIterator::LEAVES_ONLY)
  110. as $file) {
  111. if (!$fileFilter($file)) {
  112. continue;
  113. }
  114. $startTime = microtime(true);
  115. $code = file_get_contents($file);
  116. $readTime += microtime(true) - $startTime;
  117. if (null === $code = $codeExtractor($file, $code)) {
  118. continue;
  119. }
  120. set_time_limit(10);
  121. ++$count;
  122. if ($showProgress) {
  123. echo substr(str_pad('Testing file ' . $count . ': ' . substr($file, strlen($dir)), 79), 0, 79), "\r";
  124. }
  125. try {
  126. $startTime = microtime(true);
  127. $stmts = $parser->parse($code);
  128. $parseTime += microtime(true) - $startTime;
  129. $startTime = microtime(true);
  130. $code = '<?php' . "\n" . $prettyPrinter->prettyPrint($stmts);
  131. $ppTime += microtime(true) - $startTime;
  132. try {
  133. $startTime = microtime(true);
  134. $ppStmts = $parser->parse($code);
  135. $reparseTime += microtime(true) - $startTime;
  136. $startTime = microtime(true);
  137. $same = $nodeDumper->dump($stmts) == $nodeDumper->dump($ppStmts);
  138. $compareTime += microtime(true) - $startTime;
  139. if (!$same) {
  140. echo $file, ":\n Result of initial parse and parse after pretty print differ\n";
  141. if ($verbose) {
  142. echo "Pretty printer output:\n=====\n$code\n=====\n\n";
  143. }
  144. ++$compareFail;
  145. }
  146. } catch (PhpParser\Error $e) {
  147. echo $file, ":\n Parse of pretty print failed with message: {$e->getMessage()}\n";
  148. if ($verbose) {
  149. echo "Pretty printer output:\n=====\n$code\n=====\n\n";
  150. }
  151. ++$ppFail;
  152. }
  153. } catch (PhpParser\Error $e) {
  154. echo $file, ":\n Parse failed with message: {$e->getMessage()}\n";
  155. ++$parseFail;
  156. }
  157. }
  158. if (0 === $parseFail && 0 === $ppFail && 0 === $compareFail) {
  159. $exit = 0;
  160. echo "\n\n", 'All tests passed.', "\n";
  161. } else {
  162. $exit = 1;
  163. echo "\n\n", '==========', "\n\n", 'There were: ', "\n";
  164. if (0 !== $parseFail) {
  165. echo ' ', $parseFail, ' parse failures.', "\n";
  166. }
  167. if (0 !== $ppFail) {
  168. echo ' ', $ppFail, ' pretty print failures.', "\n";
  169. }
  170. if (0 !== $compareFail) {
  171. echo ' ', $compareFail, ' compare failures.', "\n";
  172. }
  173. }
  174. echo "\n",
  175. 'Tested files: ', $count, "\n",
  176. "\n",
  177. 'Reading files took: ', $readTime, "\n",
  178. 'Parsing took: ', $parseTime, "\n",
  179. 'Pretty printing took: ', $ppTime, "\n",
  180. 'Reparsing took: ', $reparseTime, "\n",
  181. 'Comparing took: ', $compareTime, "\n",
  182. "\n",
  183. 'Total time: ', microtime(true) - $totalStartTime, "\n",
  184. 'Maximum memory usage: ', memory_get_peak_usage(true), "\n";
  185. exit($exit);