224 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| error_reporting(E_ALL | E_STRICT);
 | |
| ini_set('short_open_tag', false);
 | |
| 
 | |
| if ('cli' !== php_sapi_name()) {
 | |
|     die('This script is designed for running on the command line.');
 | |
| }
 | |
| 
 | |
| function showHelp($error) {
 | |
|     die($error . "\n\n" .
 | |
| <<<OUTPUT
 | |
| This script has to be called with the following signature:
 | |
| 
 | |
|     php run.php [--no-progress] testType pathToTestFiles
 | |
| 
 | |
| The test type must be one of: PHP5, PHP7 or Symfony.
 | |
| 
 | |
| The following options are available:
 | |
| 
 | |
|     --no-progress    Disables showing which file is currently tested.
 | |
| 
 | |
| OUTPUT
 | |
|     );
 | |
| }
 | |
| 
 | |
| $options = array();
 | |
| $arguments = array();
 | |
| 
 | |
| // remove script name from argv
 | |
| array_shift($argv);
 | |
| 
 | |
| foreach ($argv as $arg) {
 | |
|     if ('-' === $arg[0]) {
 | |
|         $options[] = $arg;
 | |
|     } else {
 | |
|         $arguments[] = $arg;
 | |
|     }
 | |
| }
 | |
| 
 | |
| if (count($arguments) !== 2) {
 | |
|     showHelp('Too little arguments passed!');
 | |
| }
 | |
| 
 | |
| $showProgress = true;
 | |
| $verbose = false;
 | |
| foreach ($options as $option) {
 | |
|     if ($option === '--no-progress') {
 | |
|         $showProgress = false;
 | |
|     } elseif ($option === '--verbose') {
 | |
|         $verbose = true;
 | |
|     } else {
 | |
|         showHelp('Invalid option passed!');
 | |
|     }
 | |
| }
 | |
| 
 | |
| $testType = $arguments[0];
 | |
| $dir = $arguments[1];
 | |
| 
 | |
| switch ($testType) {
 | |
|     case 'Symfony':
 | |
|         $version = 'Php5';
 | |
|         $fileFilter = function($path) {
 | |
|             return preg_match('~\.php(?:\.cache)?$~', $path) && false === strpos($path, 'skeleton');
 | |
|         };
 | |
|         $codeExtractor = function($file, $code) {
 | |
|             return $code;
 | |
|         };
 | |
|         break;
 | |
|     case 'PHP5':
 | |
|     case 'PHP7':
 | |
|     $version = $testType === 'PHP5' ? 'Php5' : 'Php7';
 | |
|         $fileFilter = function($path) {
 | |
|             return preg_match('~\.phpt$~', $path);
 | |
|         };
 | |
|         $codeExtractor = function($file, $code) {
 | |
|             if (preg_match('~(?:
 | |
| # skeleton files
 | |
|   ext.gmp.tests.001
 | |
| | ext.skeleton.tests.001
 | |
| # multibyte encoded files
 | |
| | ext.mbstring.tests.zend_multibyte-01
 | |
| | Zend.tests.multibyte.multibyte_encoding_001
 | |
| | Zend.tests.multibyte.multibyte_encoding_004
 | |
| | Zend.tests.multibyte.multibyte_encoding_005
 | |
| # token_get_all bug (https://bugs.php.net/bug.php?id=60097)
 | |
| | Zend.tests.bug47516
 | |
| # pretty print difference due to INF vs 1e1000
 | |
| | ext.standard.tests.general_functions.bug27678
 | |
| | tests.lang.bug24640
 | |
| # pretty print difference due to nop statements
 | |
| | ext.mbstring.tests.htmlent
 | |
| | ext.standard.tests.file.fread_basic
 | |
| # tests using __halt_compiler as semi reserved keyword
 | |
| | Zend.tests.grammar.semi_reserved_001
 | |
| | Zend.tests.grammar.semi_reserved_002
 | |
| | Zend.tests.grammar.semi_reserved_005
 | |
| )\.phpt$~x', $file)) {
 | |
|                 return null;
 | |
|             }
 | |
| 
 | |
|             if (!preg_match('~--FILE--\s*(.*?)--[A-Z]+--~s', $code, $matches)) {
 | |
|                 return null;
 | |
|             }
 | |
|             if (preg_match('~--EXPECT(?:F|REGEX)?--\s*(?:Parse|Fatal) error~', $code)) {
 | |
|                 return null;
 | |
|             }
 | |
| 
 | |
|             return $matches[1];
 | |
|         };
 | |
|         break;
 | |
|     default:
 | |
|         showHelp('Test type must be one of: PHP5, PHP7 or Symfony');
 | |
| }
 | |
| 
 | |
| require_once dirname(__FILE__) . '/../lib/PhpParser/Autoloader.php';
 | |
| PhpParser\Autoloader::register();
 | |
| 
 | |
| $parserName    = 'PhpParser\Parser\\' . $version;
 | |
| $parser        = new $parserName(new PhpParser\Lexer\Emulative);
 | |
| $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
 | |
| $nodeDumper    = new PhpParser\NodeDumper;
 | |
| 
 | |
| $parseFail = $ppFail = $compareFail = $count = 0;
 | |
| 
 | |
| $readTime = $parseTime = $ppTime = $reparseTime = $compareTime = 0;
 | |
| $totalStartTime = microtime(true);
 | |
| 
 | |
| foreach (new RecursiveIteratorIterator(
 | |
|              new RecursiveDirectoryIterator($dir),
 | |
|              RecursiveIteratorIterator::LEAVES_ONLY)
 | |
|          as $file) {
 | |
|     if (!$fileFilter($file)) {
 | |
|         continue;
 | |
|     }
 | |
| 
 | |
|     $startTime = microtime(true);
 | |
|     $code = file_get_contents($file);
 | |
|     $readTime += microtime(true) - $startTime;
 | |
| 
 | |
|     if (null === $code = $codeExtractor($file, $code)) {
 | |
|         continue;
 | |
|     }
 | |
| 
 | |
|     set_time_limit(10);
 | |
| 
 | |
|     ++$count;
 | |
| 
 | |
|     if ($showProgress) {
 | |
|         echo substr(str_pad('Testing file ' . $count . ': ' . substr($file, strlen($dir)), 79), 0, 79), "\r";
 | |
|     }
 | |
| 
 | |
|     try {
 | |
|         $startTime = microtime(true);
 | |
|         $stmts = $parser->parse($code);
 | |
|         $parseTime += microtime(true) - $startTime;
 | |
| 
 | |
|         $startTime = microtime(true);
 | |
|         $code = '<?php' . "\n" . $prettyPrinter->prettyPrint($stmts);
 | |
|         $ppTime += microtime(true) - $startTime;
 | |
| 
 | |
|         try {
 | |
|             $startTime = microtime(true);
 | |
|             $ppStmts = $parser->parse($code);
 | |
|             $reparseTime += microtime(true) - $startTime;
 | |
| 
 | |
|             $startTime = microtime(true);
 | |
|             $same = $nodeDumper->dump($stmts) == $nodeDumper->dump($ppStmts);
 | |
|             $compareTime += microtime(true) - $startTime;
 | |
| 
 | |
|             if (!$same) {
 | |
|                 echo $file, ":\n    Result of initial parse and parse after pretty print differ\n";
 | |
|                 if ($verbose) {
 | |
|                     echo "Pretty printer output:\n=====\n$code\n=====\n\n";
 | |
|                 }
 | |
| 
 | |
|                 ++$compareFail;
 | |
|             }
 | |
|         } catch (PhpParser\Error $e) {
 | |
|             echo $file, ":\n    Parse of pretty print failed with message: {$e->getMessage()}\n";
 | |
|             if ($verbose) {
 | |
|                 echo "Pretty printer output:\n=====\n$code\n=====\n\n";
 | |
|             }
 | |
| 
 | |
|             ++$ppFail;
 | |
|         }
 | |
|     } catch (PhpParser\Error $e) {
 | |
|         echo $file, ":\n    Parse failed with message: {$e->getMessage()}\n";
 | |
| 
 | |
|         ++$parseFail;
 | |
|     }
 | |
| }
 | |
| 
 | |
| if (0 === $parseFail && 0 === $ppFail && 0 === $compareFail) {
 | |
|     $exit = 0;
 | |
|     echo "\n\n", 'All tests passed.', "\n";
 | |
| } else {
 | |
|     $exit = 1;
 | |
|     echo "\n\n", '==========', "\n\n", 'There were: ', "\n";
 | |
|     if (0 !== $parseFail) {
 | |
|         echo '    ', $parseFail,   ' parse failures.',        "\n";
 | |
|     }
 | |
|     if (0 !== $ppFail) {
 | |
|         echo '    ', $ppFail,      ' pretty print failures.', "\n";
 | |
|     }
 | |
|     if (0 !== $compareFail) {
 | |
|         echo '    ', $compareFail, ' compare failures.',      "\n";
 | |
|     }
 | |
| }
 | |
| 
 | |
| echo "\n",
 | |
|      'Tested files:         ', $count,        "\n",
 | |
|      "\n",
 | |
|      'Reading files took:   ', $readTime,    "\n",
 | |
|      'Parsing took:         ', $parseTime,   "\n",
 | |
|      'Pretty printing took: ', $ppTime,      "\n",
 | |
|      'Reparsing took:       ', $reparseTime, "\n",
 | |
|      'Comparing took:       ', $compareTime, "\n",
 | |
|      "\n",
 | |
|      'Total time:           ', microtime(true) - $totalStartTime, "\n",
 | |
|      'Maximum memory usage: ', memory_get_peak_usage(true), "\n";
 | |
| 
 | |
| exit($exit);
 | 
