2016-10-06 11:01:41 +03:00
|
|
|
<?php
|
|
|
|
// TODO autoload classes
|
|
|
|
require_once(__DIR__ . "/../lexer.php");
|
|
|
|
require_once(__DIR__ . "/../parser.php");
|
|
|
|
require_once(__DIR__ . "/../Token.php");
|
2016-10-06 21:08:12 +03:00
|
|
|
require_once(__DIR__ . "/LexerInvariantsTest.php");
|
2016-10-06 11:01:41 +03:00
|
|
|
|
2016-10-15 23:02:41 +03:00
|
|
|
use PhpParser\Node\Node;
|
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-10-13 04:26:42 +03:00
|
|
|
$parser = new \PhpParser\Parser($filename);
|
|
|
|
$testFiles[basename($filename)] = [$filename, $parser->parseSourceFile()];
|
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-10-13 04:26:42 +03:00
|
|
|
$parser = new \PhpParser\Parser($filename);
|
|
|
|
$sourceFileNode = $parser->parseSourceFile();
|
2016-10-06 11:01:41 +03:00
|
|
|
$tokensArray = array();
|
|
|
|
foreach ($sourceFileNode->getAllChildren() as $child) {
|
|
|
|
if ($child instanceof \PhpParser\Token) {
|
|
|
|
array_push($tokensArray, $child);
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
*/
|
|
|
|
public function testSourceFileNodeLengthEqualsDocumentLength($filename, $sourceFileNode) {
|
|
|
|
$this->assertEquals(
|
|
|
|
filesize($filename), $sourceFileNode->getLength(),
|
|
|
|
"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
|
|
|
|
*/
|
|
|
|
public function testNodesAllHaveAtLeastOneChild($filename, $sourceFileNode) {
|
|
|
|
|
|
|
|
foreach ($sourceFileNode->getAllChildren() as $child) {
|
|
|
|
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-10-16 01:02:00 +03:00
|
|
|
1, count($child->getChildren()),
|
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
|
|
|
|
*/
|
|
|
|
public function testEveryNodeSpanIsSumOfChildSpans($filename, $sourceFileNode) {
|
|
|
|
$treeElements = $sourceFileNode->getAllChildren();
|
|
|
|
array_push($treeElements, $sourceFileNode);
|
|
|
|
|
|
|
|
foreach ($treeElements as $element) {
|
|
|
|
if ($element instanceof Node) {
|
|
|
|
$expectedLength = 0;
|
2016-10-16 01:02:00 +03:00
|
|
|
foreach ($element->getChildren() as $child) {
|
2016-10-10 02:21:14 +03:00
|
|
|
if ($child instanceof Node) {
|
|
|
|
$expectedLength += $child->getLength();
|
|
|
|
} else if ($child instanceof \PhpParser\Token) {
|
|
|
|
$expectedLength += $child->length;
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|
|
|
|
}
|
2016-10-10 02:21:14 +03:00
|
|
|
$this->assertEquals(
|
|
|
|
$expectedLength, $element->getLength(),
|
|
|
|
"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
|
|
|
|
*/
|
|
|
|
public function testParentOfNodeHasSameChildNode($filename, $sourceFileNode) {
|
|
|
|
foreach ($sourceFileNode->getAllChildren() as $child) {
|
|
|
|
if ($child instanceof Node) {
|
|
|
|
$this->assertContains(
|
2016-10-16 01:02:00 +03:00
|
|
|
$child, $child->parent->getChildren(),
|
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
|
|
|
|
*/
|
|
|
|
public function testEachChildHasExactlyOneParent($filename, $sourceFileNode) {
|
2016-10-06 21:08:12 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
$treeElements = $sourceFileNode->getAllChildren();
|
|
|
|
array_push($treeElements, $sourceFileNode);
|
2016-10-06 21:08:12 +03:00
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
foreach ($sourceFileNode->getAllChildren() as $child) {
|
|
|
|
$count = 0;
|
|
|
|
foreach ($treeElements as $element) {
|
|
|
|
if ($element instanceof Node) {
|
2016-10-16 01:02:00 +03:00
|
|
|
if (in_array($child, $element->getChildren(), 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-10 02:21:14 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider sourceFileNodeProvider
|
|
|
|
*/
|
|
|
|
public function testRootNodeHasNoParent($filename, $sourceFileNode) {
|
|
|
|
$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
|
|
|
|
*/
|
|
|
|
public function testRootNodeIsNeverAChild($filename, $sourceFileNode) {
|
|
|
|
$treeElements = $sourceFileNode->getAllChildren();
|
|
|
|
array_push($treeElements, $sourceFileNode);
|
|
|
|
|
|
|
|
foreach($treeElements as $element) {
|
|
|
|
if ($element instanceof Node) {
|
|
|
|
$this->assertNotContains(
|
2016-10-16 01:02:00 +03:00
|
|
|
$sourceFileNode, $element->getChildren(),
|
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
|
|
|
|
*/
|
|
|
|
public function testEveryNodeHasAKind($filename, $sourceFileNode) {
|
|
|
|
$treeElements = $sourceFileNode->getAllChildren();
|
|
|
|
array_push($treeElements, $sourceFileNode);
|
|
|
|
|
|
|
|
foreach($treeElements as $element) {
|
|
|
|
if ($element instanceof Node) {
|
|
|
|
$this->assertNotNull(
|
|
|
|
$element->kind,
|
|
|
|
"Invariant: Every Node has a Kind");
|
2016-10-09 09:25:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-06 11:01:41 +03:00
|
|
|
}
|