Skip to content

Commit 6a1a057

Browse files
committed
Tests: Improve test DX
- Generate snapshot if file does not exist rather than whatever it was doing before. - Upgrade phpunit - Removed CallbackTestListener
1 parent 092205e commit 6a1a057

17 files changed

+339
-189
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"description": "Tolerant PHP-to-AST parser designed for IDE usage scenarios",
44
"type": "library",
55
"require": {
6-
"php": ">=7.2"
6+
"php": ">=8.1"
77
},
88
"require-dev": {
9-
"phpunit/phpunit": "^8.5.15",
9+
"phpunit/phpunit": "^10.0",
1010
"phpstan/phpstan": "^1.8"
1111
},
1212
"license": "MIT",

src/Parser.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,8 @@ private function parseClassElementFn() {
643643
$modifiers = $this->parseModifiers();
644644

645645
$token = $this->getCurrentToken();
646+
//dump(Token::getTokenKindNameFromValue($token->kind));
647+
646648
switch ($token->kind) {
647649
case TokenKind::ConstKeyword:
648650
return $this->parseClassConstDeclaration($parentNode, $modifiers);

tests/CallbackTestListener.php

Lines changed: 0 additions & 21 deletions
This file was deleted.

tests/LexicalGrammarTest.php

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,28 @@
1111
use PHPUnit\Framework\BaseTestListener;
1212
use PHPUnit\Framework\AssertionFailedError;
1313

14-
require_once __DIR__ . '/CallbackTestListener.php';
15-
1614
class LexicalGrammarTest extends TestCase {
1715
private $expectedTokensFile;
1816
private $tokens;
1917
const FILE_PATTERN = __DIR__ . "/cases/lexical/*";
20-
public function run(TestResult $result = null) : TestResult {
21-
if (!isset($GLOBALS["GIT_CHECKOUT_LEXER"])) {
22-
$GLOBALS["GIT_CHECKOUT_LEXER"] = true;
23-
exec("git -C " . dirname(self::FILE_PATTERN) . " checkout *.php.tokens");
24-
}
25-
26-
$result->addListener(new CallbackTestListener(function (Test $test) {
27-
if (isset($test->expectedTokensFile) && isset($test->tokens)) {
28-
file_put_contents($test->expectedTokensFile, str_replace("\r\n", "\n", $test->tokens));
29-
}
30-
}));
31-
32-
$result = parent::run($result);
33-
return $result;
34-
}
35-
36-
3718
/**
3819
* @dataProvider lexicalProvider
3920
*/
4021
public function testOutputTokenClassificationAndLength($testCaseFile, $expectedTokensFile) {
4122
$fileContents = file_get_contents($testCaseFile);
42-
if (!file_exists($expectedTokensFile)) {
43-
file_put_contents($expectedTokensFile, $fileContents);
44-
exec("git add " . $expectedTokensFile);
45-
}
4623

47-
$expectedTokens = str_replace("\r\n", "\n", file_get_contents($expectedTokensFile));
4824
$lexer = \Microsoft\PhpParser\TokenStreamProviderFactory::GetTokenStreamProvider($fileContents);
4925
$GLOBALS["SHORT_TOKEN_SERIALIZE"] = true;
5026
$tokens = str_replace("\r\n", "\n", json_encode($lexer->getTokensArray(), JSON_PRETTY_PRINT));
5127
$GLOBALS["SHORT_TOKEN_SERIALIZE"] = false;
52-
$this->expectedTokensFile = $expectedTokensFile;
53-
$this->tokens = $tokens;
28+
29+
if (!file_exists($expectedTokensFile)) {
30+
file_put_contents($expectedTokensFile, $tokens);
31+
self::markTestSkipped('Snapshot updated');
32+
}
33+
34+
$expectedTokens = str_replace("\r\n", "\n", file_get_contents($expectedTokensFile));
35+
5436
$this->assertEquals($expectedTokens, $tokens, "input: $testCaseFile\r\nexpected: $expectedTokensFile");
5537
}
5638

tests/ParserGrammarTest.php

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,63 +12,45 @@
1212
use PHPUnit\Framework\TestListenerDefaultImplementation;
1313
use PHPUnit\Framework\AssertionFailedError;
1414

15-
require_once __DIR__ . '/CallbackTestListener.php';
16-
1715
class ParserGrammarTest extends TestCase {
1816
private $expectedTokensFile;
1917
private $expectedDiagnosticsFile;
2018
private $tokens;
2119
private $diagnostics;
22-
public function run(TestResult $result = null) : TestResult {
23-
if (!isset($GLOBALS["GIT_CHECKOUT_PARSER"])) {
24-
$GLOBALS["GIT_CHECKOUT_PARSER"] = true;
25-
exec("git -C " . dirname(self::FILE_PATTERN) . " checkout *.php.tree *.php.diag");
26-
}
27-
28-
$result->addListener(new CallbackTestListener(function (Test $test) {
29-
if (isset($test->expectedTokensFile) && isset($test->tokens)) {
30-
file_put_contents($test->expectedTokensFile, str_replace("\r\n", "\n", $test->tokens));
31-
}
32-
if (isset($test->expectedDiagnosticsFile) && isset($test->diagnostics)) {
33-
file_put_contents($test->expectedDiagnosticsFile, str_replace("\r\n", "\n", $test->diagnostics));
34-
}
35-
}));
36-
37-
$result = parent::run($result);
38-
return $result;
39-
}
4020

4121
/**
4222
* @dataProvider treeProvider
4323
*/
4424
public function testOutputTreeClassificationAndLength($testCaseFile, $expectedTokensFile, $expectedDiagnosticsFile) {
45-
$this->expectedTokensFile = $expectedTokensFile;
46-
$this->expectedDiagnosticsFile = $expectedDiagnosticsFile;
47-
4825
$fileContents = file_get_contents($testCaseFile);
49-
if (!file_exists($expectedTokensFile)) {
50-
file_put_contents($expectedTokensFile, $fileContents);
51-
exec("git add " . $expectedTokensFile);
52-
}
53-
54-
if (!file_exists($expectedDiagnosticsFile)) {
55-
file_put_contents($expectedDiagnosticsFile, $fileContents);
56-
exec("git add " . $expectedDiagnosticsFile);
57-
}
5826

5927
$parser = new \Microsoft\PhpParser\Parser();
6028
$sourceFileNode = $parser->parseSourceFile($fileContents);
6129

62-
$expectedTokens = str_replace("\r\n", "\n", file_get_contents($expectedTokensFile));
63-
$expectedDiagnostics = str_replace("\r\n", "\n", file_get_contents($expectedDiagnosticsFile));
64-
6530
$GLOBALS["SHORT_TOKEN_SERIALIZE"] = true;
6631
$tokens = str_replace("\r\n", "\n", json_encode($sourceFileNode, JSON_PRETTY_PRINT));
6732
$diagnostics = str_replace("\r\n", "\n", json_encode(\Microsoft\PhpParser\DiagnosticsProvider::getDiagnostics($sourceFileNode), JSON_PRETTY_PRINT));
6833
$GLOBALS["SHORT_TOKEN_SERIALIZE"] = false;
6934

70-
$this->tokens = $tokens;
71-
$this->diagnostics = $diagnostics;
35+
$skip = false;
36+
if (!file_exists($expectedTokensFile)) {
37+
file_put_contents($expectedTokensFile, $tokens);
38+
$skip = true;
39+
} else {
40+
$expectedTokens = trim(str_replace("\r\n", "\n", file_get_contents($expectedTokensFile)));
41+
}
42+
43+
44+
if (!file_exists($expectedDiagnosticsFile)) {
45+
file_put_contents($expectedDiagnosticsFile, $diagnostics);
46+
$skip = true;
47+
} else {
48+
$expectedDiagnostics = trim(str_replace("\r\n", "\n", file_get_contents($expectedDiagnosticsFile)));
49+
}
50+
51+
if ($skip) {
52+
self::markTestSkipped('Snapshot generated');
53+
}
7254

7355
$tokensOutputStr = "input doc:\r\n$fileContents\r\n\r\ninput: $testCaseFile\r\nexpected: $expectedTokensFile";
7456
$diagnosticsOutputStr = "input doc:\r\n$fileContents\r\n\r\ninput: $testCaseFile\r\nexpected: $expectedDiagnosticsFile";
@@ -81,11 +63,13 @@ public function testOutputTreeClassificationAndLength($testCaseFile, $expectedTo
8163
const PHP74_FILE_PATTERN = __DIR__ . "/cases/parser74/*";
8264
const PHP80_FILE_PATTERN = __DIR__ . "/cases/parser80/*";
8365
const PHP81_FILE_PATTERN = __DIR__ . "/cases/parser81/*";
66+
const PHP84_FILE_PATTERN = __DIR__ . "/cases/parser84/*";
8467

8568
const PATTERNS_FOR_MINIMUM_PHP_VERSION = [
8669
[70400, self::PHP74_FILE_PATTERN],
8770
[80000, self::PHP80_FILE_PATTERN],
8871
[80100, self::PHP81_FILE_PATTERN],
72+
[80400, self::PHP84_FILE_PATTERN],
8973
];
9074

9175
public function treeProvider() {
@@ -100,12 +84,14 @@ public function treeProvider() {
10084
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
10185
}
10286

103-
foreach (self::PATTERNS_FOR_MINIMUM_PHP_VERSION as list($minVersionId, $filePattern)) {
104-
if (PHP_VERSION_ID >= $minVersionId) {
105-
$testCases = glob($filePattern . ".php");
106-
foreach ($testCases as $testCase) {
107-
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
108-
}
87+
foreach (self::PATTERNS_FOR_MINIMUM_PHP_VERSION as [$minVersionId, $filePattern]) {
88+
if (PHP_VERSION_ID < $minVersionId) {
89+
continue;
90+
}
91+
92+
$testCases = glob($filePattern . ".php");
93+
foreach ($testCases as $testCase) {
94+
$testProviderArray[basename($testCase)] = [$testCase, $testCase . ".tree", $testCase . ".diag"];
10995
}
11096
}
11197

tests/cases/parser/objectCreationExpression12.php.diag

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,11 @@
1010
"message": "Unexpected 'extends'",
1111
"start": 37,
1212
"length": 7
13+
},
14+
{
15+
"kind": 0,
16+
"message": "';' expected.",
17+
"start": 46,
18+
"length": 0
1319
}
1420
]

tests/cases/parser/objectCreationExpression12.php.tree

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,30 +96,39 @@
9696
{
9797
"ExpressionStatement": {
9898
"expression": {
99-
"SubscriptExpression": {
100-
"postfixExpression": {
101-
"QualifiedName": {
102-
"globalSpecifier": null,
103-
"relativeSpecifier": null,
104-
"nameParts": [
105-
{
106-
"kind": "Name",
107-
"textLength": 1
108-
}
109-
]
99+
"QualifiedName": {
100+
"globalSpecifier": null,
101+
"relativeSpecifier": null,
102+
"nameParts": [
103+
{
104+
"kind": "Name",
105+
"textLength": 1
110106
}
111-
},
112-
"openBracketOrBrace": {
113-
"kind": "OpenBraceToken",
114-
"textLength": 1
115-
},
116-
"accessExpression": null,
117-
"closeBracketOrBrace": {
118-
"kind": "CloseBraceToken",
119-
"textLength": 1
120-
}
107+
]
121108
}
122109
},
110+
"semicolon": {
111+
"error": "MissingToken",
112+
"kind": "SemicolonToken",
113+
"textLength": 0
114+
}
115+
}
116+
},
117+
{
118+
"CompoundStatementNode": {
119+
"openBrace": {
120+
"kind": "OpenBraceToken",
121+
"textLength": 1
122+
},
123+
"statements": [],
124+
"closeBrace": {
125+
"kind": "CloseBraceToken",
126+
"textLength": 1
127+
}
128+
}
129+
},
130+
{
131+
"EmptyStatement": {
123132
"semicolon": {
124133
"kind": "SemicolonToken",
125134
"textLength": 1

tests/cases/parser/programStructure13.php.tree

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,37 @@
44
{
55
"InlineHtml": {
66
"scriptSectionEndTag": null,
7-
"text": {
8-
"kind": "InlineHtml",
9-
"textLength": 7
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 2
11+
}
12+
}
13+
},
14+
{
15+
"ExpressionStatement": {
16+
"expression": {
17+
"QualifiedName": {
18+
"globalSpecifier": null,
19+
"relativeSpecifier": null,
20+
"nameParts": [
21+
{
22+
"kind": "Name",
23+
"textLength": 3
24+
}
25+
]
26+
}
27+
},
28+
"semicolon": null
29+
}
30+
},
31+
{
32+
"InlineHtml": {
33+
"scriptSectionEndTag": {
34+
"kind": "ScriptSectionEndTag",
35+
"textLength": 2
1036
},
37+
"text": null,
1138
"scriptSectionStartTag": null
1239
}
1340
}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
[]
1+
[
2+
{
3+
"kind": 0,
4+
"message": "';' expected.",
5+
"start": 6,
6+
"length": 0
7+
}
8+
]

tests/cases/parser/programStructure21.php.tree

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,32 @@
44
{
55
"InlineHtml": {
66
"scriptSectionEndTag": null,
7-
"text": {
8-
"kind": "InlineHtml",
9-
"textLength": 6
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 2
11+
}
12+
}
13+
},
14+
{
15+
"ExpressionStatement": {
16+
"expression": {
17+
"QualifiedName": {
18+
"globalSpecifier": null,
19+
"relativeSpecifier": null,
20+
"nameParts": [
21+
{
22+
"kind": "Name",
23+
"textLength": 3
24+
}
25+
]
26+
}
1027
},
11-
"scriptSectionStartTag": null
28+
"semicolon": {
29+
"error": "MissingToken",
30+
"kind": "SemicolonToken",
31+
"textLength": 0
32+
}
1233
}
1334
}
1435
],

0 commit comments

Comments
 (0)