Page MenuHomePhabricator

D17819.id42863.diff
No OneTemporary

D17819.id42863.diff

diff --git a/src/parser/xhpast/__tests__/PHPASTParserTestCase.php b/src/parser/xhpast/__tests__/PHPASTParserTestCase.php
--- a/src/parser/xhpast/__tests__/PHPASTParserTestCase.php
+++ b/src/parser/xhpast/__tests__/PHPASTParserTestCase.php
@@ -80,7 +80,6 @@
switch ($type) {
case 'pass':
- case 'fail-parse':
$this->assertEqual(0, $err, pht('Exit code for "%s".', $name));
if (!strlen($expect)) {
@@ -89,16 +88,6 @@
}
try {
- $expect = phutil_json_decode($expect);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht(
- 'Expect data for test "%s" is not valid JSON.',
- $name),
- $ex);
- }
-
- try {
$stdout = phutil_json_decode($stdout);
} catch (PhutilJSONParserException $ex) {
throw new PhutilProxyException(
@@ -108,21 +97,12 @@
$ex);
}
- $json = new PhutilJSON();
-
- $expect_nice = $json->encodeFormatted($expect);
- $stdout_nice = $json->encodeFormatted($stdout);
+ $stdout_nice = $this->newReadableAST($stdout, $data);
- if ($type == 'pass') {
- $this->assertEqual(
- $expect_nice,
- $stdout_nice,
- pht('Parser output for "%s".', $name));
- } else {
- $this->assertFalse(
- ($expect_nice == $stdout_nice),
- pht('Expected parser to parse "%s" incorrectly.', $name));
- }
+ $this->assertEqual(
+ $expect,
+ $stdout_nice,
+ pht('Parser output for "%s".', $name));
break;
case 'fail-syntax':
$this->assertEqual(1, $err, pht('Exit code for "%s".', $name));
@@ -130,7 +110,141 @@
(bool)preg_match('/syntax error/', $stderr),
pht('Expect "syntax error" in stderr or "%s".', $name));
break;
+ default:
+ throw new Exception(
+ pht(
+ 'Unknown PHPAST parser test type "%s"!',
+ $type));
+ }
+ }
+
+ /**
+ * Build a human-readable, stable, relatively diff-able string representing
+ * an AST (both the node tree itself and the accompanying token stream) for
+ * use in unit tests.
+ */
+ private function newReadableAST(array $data, $source) {
+ $tree = new XHPASTTree($data['tree'], $data['stream'], $source);
+
+ $out = array();
+ $out[] = $this->newReadableTree($tree);
+ $out[] = $this->newReadableDivider();
+ $out[] = $this->newReadableStream($tree);
+ $out = implode('', $out);
+
+ return $out;
+ }
+
+ private function newReadableTree(XHPASTTree $tree) {
+ $root = $tree->getRootNode();
+
+ $depth = 0;
+ $list = $this->newReadableTreeLines($root, $depth);
+
+ return implode('', $list);
+ }
+
+ private function newReadableDivider() {
+ return str_repeat('-', 80)."\n";
+ }
+
+ private function newReadableStream(XHPASTTree $tree) {
+ $tokens = $tree->getRawTokenStream();
+
+ // Identify the longest token name this stream uses so we can format the
+ // output into two columns.
+ $max_len = 0;
+ foreach ($tokens as $token) {
+ $max_len = max($max_len, strlen($token->getTypeName()));
+ }
+
+ $out = array();
+
+ $tokens = $tree->getRawTokenStream();
+ foreach ($tokens as $token) {
+ $value = $token->getValue();
+ $vector = $this->getEscapedValueVector($value);
+
+ $vector = array_merge(
+ array(
+ str_pad($token->getTypeName(), $max_len),
+ ' ',
+ ),
+ $vector);
+
+ $out[] = $this->wrapVector($vector);
+ }
+
+ $out = implode('', $out);
+
+ return $out;
+ }
+
+ private function newReadableTreeLines(AASTNode $node, $depth) {
+ $out = array();
+
+ try {
+ $type_name = $node->getTypeName();
+ } catch (Exception $ex) {
+ $type_name = sprintf('<INVALID TYPE "%s">', $node->getTypeID());
+ }
+
+ $out[] = str_repeat(' ', $depth).'+ '.$type_name."\n";
+
+ $children = $node->getChildren();
+ if ($children) {
+ foreach ($children as $child) {
+ foreach ($this->newReadableTreeLines($child, $depth + 1) as $line) {
+ $out[] = $line;
+ }
+ }
+ } else {
+ $value = $node->getConcreteString();
+ $vector = $this->getEscapedValueVector($value);
+ $out[] = $this->wrapVector($vector, $depth + 1);
+ }
+
+ return $out;
+ }
+
+ private function getEscapedValueVector($value) {
+ if (!$value) {
+ return array('<null>');
}
+
+ $vector = phutil_utf8v_combined($value);
+ foreach ($vector as $key => $character) {
+ $vector[$key] = addcslashes($character, "\r\n\t\"\\");
+ }
+
+ return $vector;
+ }
+
+ private function wrapVector(array $vector, $indent = 0, $width = 80) {
+ $lines = array();
+ $prefix = str_repeat(' ', $indent);
+
+ $line = $prefix.'> "';
+ $len = strlen($line);
+ foreach ($vector as $character) {
+ // We're just wrapping based on bytes. This isn't the most correct
+ // wrapping for human language, but sufficient and stable for unit
+ // tests, which will rarely have long sequences of combining or
+ // multibyte characters.
+ $charlen = strlen($character);
+ if ($len + $charlen > ($width - 1)) {
+ $lines[] = $line."\"\n";
+ $line = $prefix.'. "';
+ $len = strlen($line);
+ }
+
+ $line .= $character;
+ $len += $charlen;
+ }
+
+ $lines[] = $line."\"\n";
+
+ return implode('', $lines);
}
}
diff --git a/src/parser/xhpast/__tests__/data/a-self-test.php.test b/src/parser/xhpast/__tests__/data/a-self-test.php.test
new file mode 100644
--- /dev/null
+++ b/src/parser/xhpast/__tests__/data/a-self-test.php.test
@@ -0,0 +1,60 @@
+<?php
+
+$the_senate = <<<EOTRAGEDY
+Did you ever hear the tragedy of Darth Plagueis The Wise? I
+thought not. It’s not a story the Jedi would tell you. It’s a Sith legend.
+Darth Plagueis was a Dark Lord of the Sith, so powerful and so wise he could
+use the Force to influence the midichlorians to create life... He had such a
+knowledge of the dark side that he could even keep the ones he cared about from
+dying. The dark side of the Force is a pathway to many abilities some consider
+to be unnatural. He became so powerful... the only thing he was afraid of was
+losing his power, which eventually, of course, he did. Unfortunately, he taught
+his apprentice everything he knew, then his apprentice killed him in his sleep.
+Ironic. He could save others from death, but not himself.
+EOTRAGEDY;
+
+~~~~~~~~~~
+pass
+~~~~~~~~~~
++ n_PROGRAM
+ + n_STATEMENT_LIST
+ + n_OPEN_TAG
+ > "<?php"
+ + n_STATEMENT
+ + n_BINARY_EXPRESSION
+ + n_VARIABLE
+ > "$the_senate"
+ + n_OPERATOR
+ > "="
+ + n_HEREDOC
+ > "<<<EOTRAGEDY\nDid you ever hear the tragedy of Darth Plagueis The Wise?"
+ . " I\nthought not. It’s not a story the Jedi would tell you. It’s a S"
+ . "ith legend.\nDarth Plagueis was a Dark Lord of the Sith, so powerful an"
+ . "d so wise he could\nuse the Force to influence the midichlorians to cre"
+ . "ate life... He had such a\nknowledge of the dark side that he could eve"
+ . "n keep the ones he cared about from\ndying. The dark side of the Force "
+ . "is a pathway to many abilities some consider\nto be unnatural. He becam"
+ . "e so powerful... the only thing he was afraid of was\nlosing his power,"
+ . " which eventually, of course, he did. Unfortunately, he taught\nhis app"
+ . "rentice everything he knew, then his apprentice killed him in his sleep"
+ . ".\nIronic. He could save others from death, but not himself.\nEOTRAGEDY"
+--------------------------------------------------------------------------------
+> "T_OPEN_TAG <?php"
+> "T_WHITESPACE \n\n"
+> "T_VARIABLE $the_senate"
+> "T_WHITESPACE "
+> "= ="
+> "T_WHITESPACE "
+> "T_HEREDOC <<<EOTRAGEDY\nDid you ever hear the tragedy of Darth Plagueis T"
+. "he Wise? I\nthought not. It’s not a story the Jedi would tell you. It’s "
+. "a Sith legend.\nDarth Plagueis was a Dark Lord of the Sith, so powerful and "
+. "so wise he could\nuse the Force to influence the midichlorians to create lif"
+. "e... He had such a\nknowledge of the dark side that he could even keep the o"
+. "nes he cared about from\ndying. The dark side of the Force is a pathway to m"
+. "any abilities some consider\nto be unnatural. He became so powerful... the o"
+. "nly thing he was afraid of was\nlosing his power, which eventually, of cours"
+. "e, he did. Unfortunately, he taught\nhis apprentice everything he knew, then"
+. " his apprentice killed him in his sleep.\nIronic. He could save others from "
+. "death, but not himself.\nEOTRAGEDY"
+> "; ;"
+> "T_WHITESPACE \n\n"
diff --git a/src/parser/xhpast/__tests__/data/base-fail-parse.php.test b/src/parser/xhpast/__tests__/data/base-fail-parse.php.test
deleted file mode 100644
--- a/src/parser/xhpast/__tests__/data/base-fail-parse.php.test
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-~~~~~~~~~~
-fail-parse
-~~~~~~~~~~
-{
- "tree": [],
- "stream": []
-}
diff --git a/src/parser/xhpast/__tests__/data/php-heredoc-terminal.php.test b/src/parser/xhpast/__tests__/data/php-heredoc-terminal.php.test
deleted file mode 100644
--- a/src/parser/xhpast/__tests__/data/php-heredoc-terminal.php.test
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-<<<HEREDOC
-HEREDOC;
-~~~~~~~~~~
-fail-parse, rtrim
-~~~~~~~~~~
-{
- "tree": [
- 9000,
- 0,
- 2,
- [
- [
- 9006,
- 0,
- 2,
- [
- [
- 9007,
- 0,
- 0
- ],
- [
- 9004,
- 1,
- 2,
- [
- [
- 9098,
- 1,
- 1
- ]
- ]
- ]
- ]
- ]
- ]
- ],
- "stream": [
- [
- 371,
- 6
- ],
- [
- 378,
- 18
- ],
- [
- 59,
- 1
- ]
- ]
-}

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 12, 3:34 PM (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7589063
Default Alt Text
D17819.id42863.diff (9 KB)

Event Timeline