Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -69,6 +69,7 @@ 'LinesOfALargeFile' => 'filesystem/linesofalarge/LinesOfALargeFile.php', 'LinesOfALargeFileTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeFileTestCase.php', 'MFilterTestHelper' => 'utils/__tests__/MFilterTestHelper.php', + 'PHPASTParserTestCase' => 'parser/xhpast/__tests__/PHPASTParserTestCase.php', 'PhageAgentBootloader' => 'phage/bootloader/PhageAgentBootloader.php', 'PhageAgentTestCase' => 'phage/__tests__/PhageAgentTestCase.php', 'PhagePHPAgent' => 'phage/agent/PhagePHPAgent.php', @@ -476,6 +477,7 @@ 'LinesOfALargeExecFutureTestCase' => 'PhutilTestCase', 'LinesOfALargeFile' => 'LinesOfALarge', 'LinesOfALargeFileTestCase' => 'PhutilTestCase', + 'PHPASTParserTestCase' => 'PhutilTestCase', 'PhageAgentTestCase' => 'PhutilTestCase', 'PhagePHPAgentBootloader' => 'PhageAgentBootloader', 'PhutilAWSEC2Future' => 'PhutilAWSFuture', Index: src/parser/xhpast/__tests__/PHPASTParserTestCase.php =================================================================== --- /dev/null +++ src/parser/xhpast/__tests__/PHPASTParserTestCase.php @@ -0,0 +1,131 @@ +assertSkipped(pht('xhpast is not built or not up to date.')); + } + + $dir = dirname(__FILE__).'/data/'; + foreach (Filesystem::listDirectory($dir) as $file) { + if (preg_match('/\.test$/', $file)) { + $this->executeParserTest($file, Filesystem::readFile($dir.$file)); + } + } + } + + private function executeParserTest($name, $data) { + $data = explode("\n", $data, 2); + if (count($data) !== 2) { + throw new Exception( + pht('Expected multiple lines in parser test file "%s".', $name)); + } + + $head = head($data); + $body = last($data); + + if (!preg_match('/^#/', $head)) { + throw new Exception( + pht( + 'Expected first line of parser test file "%s" to begin with "#" '. + 'and specify test options.', + $name)); + } + + $head = preg_replace('/^#\s*/', '', $head); + + $options_parser = new PhutilSimpleOptions(); + $options = $options_parser->parse($head); + + $type = null; + foreach ($options as $key => $value) { + switch ($key) { + case 'pass': + case 'fail-syntax': + case 'fail-parse': + if ($type !== null) { + throw new Exception( + pht( + 'Test file "%s" unexpectedly specifies multiple expected ', + 'test outcomes.', + $name)); + } + $type = $key; + break; + default: + throw new Exception( + pht( + 'Test file "%s" has unknown option "%s" in its options '. + 'string.', + $name, + $key)); + } + } + + if ($type === null) { + throw new Exception( + pht( + 'Test file "%s" does not specify a test result (like "pass") in '. + 'its options string.', + $name)); + } + + $future = xhpast_get_parser_future($body); + list($err, $stdout, $stderr) = $future->resolve(); + + switch ($type) { + case 'pass': + case 'fail-parse': + $this->assertEqual(0, $err, pht('Exit code for "%s".', $name)); + + $expect_name = preg_replace('/\.test$/', '.expect', $name); + + $dir = dirname(__FILE__).'/data/'; + $expect = Filesystem::readFile($dir.$expect_name); + + $expect = json_decode($expect, true); + if (!is_array($expect)) { + throw new Exception( + pht( + 'Test ".expect" file "%s" for test "%s" is not valid JSON.', + $expect_name, + $name)); + } + + $stdout = json_decode($stdout, true); + if (!is_array($stdout)) { + throw new Exception( + pht( + 'Output for test file "%s" is not valid JSON.', + $name)); + } + + $json = new PhutilJSON(); + + $expect_nice = $json->encodeFormatted($expect); + $stdout_nice = $json->encodeFormatted($stdout); + + if ($type == 'pass') { + $this->assertEqual( + $expect_nice, + $stdout_nice, + pht('Parser output for "%s".', $name)); + } else { + $this->assertEqual( + false, + ($expect_nice == $stdout_nice), + pht('Expected parser to parse "%s" incorrectly.', $name)); + } + break; + case 'fail-syntax': + $this->assertEqual(1, $err, pht('Exit code for "%s".', $name)); + $this->assertEqual( + true, + (bool)preg_match('/syntax error/', $stderr), + pht('Expect "syntax error" in stderr or "%s".', $name)); + break; + } + } + +} Index: src/parser/xhpast/__tests__/data/base-fail-parse.php.expect =================================================================== --- /dev/null +++ src/parser/xhpast/__tests__/data/base-fail-parse.php.expect @@ -0,0 +1,4 @@ +{ + "tree" : [], + "stream" : [] +} Index: src/parser/xhpast/__tests__/data/base-fail-parse.php.test =================================================================== --- /dev/null +++ src/parser/xhpast/__tests__/data/base-fail-parse.php.test @@ -0,0 +1,2 @@ +# fail-parse +