2016-10-06 11:01:41 +03:00
|
|
|
<?php
|
2016-12-19 01:49:28 +03:00
|
|
|
/*---------------------------------------------------------------------------------------------
|
|
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
|
2016-10-06 11:01:41 +03:00
|
|
|
// TODO autoload classes
|
2017-01-10 00:33:39 +03:00
|
|
|
require_once(__DIR__ . "/../src/TokenStreamProviderFactory.php");
|
|
|
|
require_once(__DIR__ . "/../src/Parser.php");
|
|
|
|
require_once(__DIR__ . "/../src/Token.php");
|
2016-10-06 21:08:12 +03:00
|
|
|
require_once(__DIR__ . "/LexerInvariantsTest.php");
|
2016-10-06 11:01:41 +03:00
|
|
|
|
2017-01-10 00:33:39 +03:00
|
|
|
use PhpParser\Node;
|
2017-01-16 22:44:39 +03:00
|
|
|
use PhpParser\Token;
|
2016-10-06 11:01:41 +03:00
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
use PhpParser\TokenKind;
|
|
|
|
|
|
|
|
class ParserInvariantsTest extends LexerInvariantsTest {
|
2016-11-02 03:35:23 +03:00
|
|
|
const FILENAME_PATTERN = __dir__ . "/cases/{parser,}/*.php";
|
2016-10-06 11:01:41 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
public static function sourceFileNodeProvider() {
|
|
|
|
$testFiles = array();
|
2016-11-02 03:35:23 +03:00
|
|
|
$testCases = glob(self::FILENAME_PATTERN, GLOB_BRACE);
|
|
|
|
|
|
|
|
foreach ($testCases as $filename) {
|
2016-12-29 00:46:00 +03:00
|
|
|
$parser = new \PhpParser\Parser();
|
|
|
|
$testFiles[basename($filename)] = [$filename, $parser->parseSourceFile(file_get_contents($filename))];
|
2016-10-10 02:21:14 +03:00
|
|
|
}
|
|
|
|
return $testFiles;
|
|
|
|
}
|
2016-10-06 11:01:41 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
public static function tokensArrayProvider() {
|
|
|
|
$testFiles = array();
|
2016-11-02 03:35:23 +03:00
|
|
|
$testCases = glob(self::FILENAME_PATTERN, GLOB_BRACE);
|
|
|
|
|
|
|
|
foreach ($testCases as $filename) {
|
2016-12-29 00:46:00 +03:00
|
|
|
$parser = new \PhpParser\Parser();
|
|
|
|
$sourceFileNode = $parser->parseSourceFile(file_get_contents($filename));
|
2016-10-06 11:01:41 +03:00
|
|
|
$tokensArray = array();
|
2016-12-25 01:41:12 +03:00
|
|
|
foreach ($sourceFileNode->getDescendantNodesAndTokens() as $child) {
|
2016-10-06 11:01:41 +03:00
|
|
|
if ($child instanceof \PhpParser\Token) {
|
2017-01-23 10:39:00 +03:00
|
|
|
$tokensArray[] = $child;
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
}
|
2016-10-10 02:21:14 +03:00
|
|
|
$testFiles[basename($filename)] = [$filename, $tokensArray];
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
2016-10-10 02:21:14 +03:00
|
|
|
return $testFiles;
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testSourceFileNodeLengthEqualsDocumentLength($filename, Node $sourceFileNode) {
|
2016-10-10 02:21:14 +03:00
|
|
|
$this->assertEquals(
|
2017-01-13 22:00:51 +03:00
|
|
|
filesize($filename), $sourceFileNode->getFullWidth(),
|
2016-10-10 02:21:14 +03:00
|
|
|
"Invariant: The tree length exactly matches the file length.");
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testNodesAllHaveAtLeastOneChild($filename, Node $sourceFileNode) {
|
2016-10-10 02:21:14 +03:00
|
|
|
|
2016-12-25 01:41:12 +03:00
|
|
|
foreach ($sourceFileNode->getDescendantNodesAndTokens() as $child) {
|
2016-10-10 02:21:14 +03:00
|
|
|
if ($child instanceof Node) {
|
2016-10-16 07:43:25 +03:00
|
|
|
$encode = json_encode($child);
|
2016-10-10 02:21:14 +03:00
|
|
|
$this->assertGreaterThanOrEqual(
|
2016-12-25 01:41:12 +03:00
|
|
|
1, count($child->getChildNodesAndTokens()),
|
2016-10-16 07:43:25 +03:00
|
|
|
"Invariant: All Nodes have at least one child. $encode"
|
2016-10-10 02:21:14 +03:00
|
|
|
);
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testEveryNodeSpanIsSumOfChildSpans($filename, Node $sourceFileNode) {
|
2017-01-23 02:48:01 +03:00
|
|
|
$treeElements = iterator_to_array($sourceFileNode->getDescendantNodesAndTokens(), false);
|
2017-01-23 10:39:00 +03:00
|
|
|
$treeElements[] = $sourceFileNode;
|
2016-10-10 02:21:14 +03:00
|
|
|
|
|
|
|
foreach ($treeElements as $element) {
|
|
|
|
if ($element instanceof Node) {
|
|
|
|
$expectedLength = 0;
|
2016-12-25 01:41:12 +03:00
|
|
|
foreach ($element->getChildNodesAndTokens() as $child) {
|
2016-10-10 02:21:14 +03:00
|
|
|
if ($child instanceof Node) {
|
2017-01-13 22:00:51 +03:00
|
|
|
$expectedLength += $child->getFullWidth();
|
2016-11-13 22:54:51 +03:00
|
|
|
} elseif ($child instanceof \PhpParser\Token) {
|
2016-10-10 02:21:14 +03:00
|
|
|
$expectedLength += $child->length;
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
}
|
2016-10-10 02:21:14 +03:00
|
|
|
$this->assertEquals(
|
2017-01-13 22:00:51 +03:00
|
|
|
$expectedLength, $element->getFullWidth(),
|
2016-10-10 02:21:14 +03:00
|
|
|
"Invariant: Span of any Node is span of child nodes and tokens."
|
|
|
|
);
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-06 21:08:12 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testParentOfNodeHasSameChildNode($filename, Node $sourceFileNode) {
|
|
|
|
foreach ($sourceFileNode->getDescendantNodesAndTokens() as $child) {
|
2016-10-10 02:21:14 +03:00
|
|
|
if ($child instanceof Node) {
|
|
|
|
$this->assertContains(
|
2016-12-25 01:41:12 +03:00
|
|
|
$child, $child->parent->getChildNodesAndTokens(),
|
2016-10-10 02:21:14 +03:00
|
|
|
"Invariant: Parent of Node contains same child node."
|
|
|
|
);
|
2016-10-06 21:08:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testEachChildHasExactlyOneParent($filename, Node $sourceFileNode) {
|
2017-01-23 02:48:01 +03:00
|
|
|
$allTreeElements = iterator_to_array($sourceFileNode->getDescendantNodesAndTokens(), false);
|
2017-01-23 10:39:00 +03:00
|
|
|
$allTreeElements[] = $sourceFileNode;
|
2016-10-06 21:08:12 +03:00
|
|
|
|
2017-01-17 00:33:13 +03:00
|
|
|
foreach ($sourceFileNode->getDescendantNodesAndTokens() as $childWithParent) {
|
2016-10-10 02:21:14 +03:00
|
|
|
$count = 0;
|
2017-01-17 00:33:13 +03:00
|
|
|
foreach ($allTreeElements as $element) {
|
2016-10-10 02:21:14 +03:00
|
|
|
if ($element instanceof Node) {
|
2017-01-17 00:33:13 +03:00
|
|
|
$values = iterator_to_array($element->getChildNodesAndTokens(), false);
|
|
|
|
if (in_array($childWithParent, $values, true)) {
|
2016-10-10 02:21:14 +03:00
|
|
|
$count++;
|
2016-10-06 21:08:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->assertEquals(
|
2016-10-10 02:21:14 +03:00
|
|
|
1, $count,
|
|
|
|
"Invariant: each child has exactly one parent.");
|
2016-10-06 21:08:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-16 22:44:39 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
|
|
|
public function testEveryChildIsNodeOrTokenType($filename, Node $sourceFileNode) {
|
2017-01-23 02:48:01 +03:00
|
|
|
$treeElements = iterator_to_array($sourceFileNode->getDescendantNodesAndTokens(), false);
|
2017-01-23 10:39:00 +03:00
|
|
|
$treeElements[] = $sourceFileNode;
|
2017-01-16 22:44:39 +03:00
|
|
|
|
|
|
|
foreach ($sourceFileNode->getDescendantNodes() as $descendant) {
|
|
|
|
foreach ($descendant->getChildNodesAndTokens() as $child) {
|
|
|
|
if ($child instanceof Node || $child instanceof Token) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$this->fail("Invariant: Every child is Node or Token type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testRootNodeHasNoParent($filename, Node $sourceFileNode) {
|
2016-10-10 02:21:14 +03:00
|
|
|
$this->assertEquals(
|
|
|
|
null, $sourceFileNode->parent,
|
|
|
|
"Invariant: Root node of tree has no parent.");
|
|
|
|
}
|
2016-10-06 21:08:12 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testRootNodeIsNeverAChild($filename, Node $sourceFileNode) {
|
2017-01-23 02:48:01 +03:00
|
|
|
$treeElements = iterator_to_array($sourceFileNode->getDescendantNodesAndTokens(), false);
|
2017-01-23 10:39:00 +03:00
|
|
|
$treeElements[] = $sourceFileNode;
|
2016-10-10 02:21:14 +03:00
|
|
|
|
|
|
|
foreach($treeElements as $element) {
|
|
|
|
if ($element instanceof Node) {
|
|
|
|
$this->assertNotContains(
|
2016-12-25 01:41:12 +03:00
|
|
|
$sourceFileNode, $element->getChildNodesAndTokens(),
|
2016-10-10 02:21:14 +03:00
|
|
|
"Invariant: root node of tree is never a child.");
|
2016-10-06 21:08:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-09 09:25:29 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
2016-12-25 01:41:12 +03:00
|
|
|
public function testEveryNodeHasAKind($filename, Node $sourceFileNode) {
|
2017-01-23 02:48:01 +03:00
|
|
|
$treeElements = iterator_to_array($sourceFileNode->getDescendantNodes(), false);
|
2017-01-23 10:39:00 +03:00
|
|
|
$treeElements[] = $sourceFileNode;
|
2016-10-10 02:21:14 +03:00
|
|
|
|
|
|
|
foreach($treeElements as $element) {
|
2016-12-27 04:36:20 +03:00
|
|
|
$this->assertNotNull(
|
2017-01-17 00:33:13 +03:00
|
|
|
$element->getKind(),
|
2016-12-27 04:36:20 +03:00
|
|
|
"Invariant: Every Node has a Kind");
|
2016-10-09 09:25:29 +03:00
|
|
|
}
|
|
|
|
}
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|