Skip to content

Commit a142413

Browse files
committed
E2E tests for result cache
1 parent d3668d4 commit a142413

File tree

9 files changed

+4402
-1
lines changed

9 files changed

+4402
-1
lines changed

.github/workflows/build.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,44 @@ jobs:
311311
time vendor/bin/phing phpstan-result-cache
312312
time vendor/bin/phing phpstan-result-cache
313313
314+
result-cache-e2e-tests:
315+
name: "Result cache E2E tests"
316+
317+
runs-on: ubuntu-latest
318+
319+
strategy:
320+
matrix:
321+
php-version:
322+
- "7.4"
323+
324+
steps:
325+
- name: "Checkout"
326+
uses: "actions/[email protected]"
327+
328+
- name: "Install PHP"
329+
uses: "shivammathur/[email protected]"
330+
with:
331+
coverage: "none"
332+
php-version: "${{ matrix.php-version }}"
333+
extensions: mbstring
334+
335+
- name: "Cache dependencies"
336+
uses: "actions/[email protected]"
337+
with:
338+
path: "~/.composer/cache"
339+
key: "php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}"
340+
restore-keys: "php-${{ matrix.php-version }}-composer-"
341+
342+
- name: "Install dependencies"
343+
run: "composer update --no-interaction --no-progress --no-suggest"
344+
345+
- name: "Tests"
346+
run: |
347+
git clone https://github.com/nikic/PHP-Parser.git tests/e2e/PHP-Parser && \
348+
git -C tests/e2e/PHP-Parser checkout v3.1.5 && \
349+
composer install --working-dir tests/e2e/PHP-Parser
350+
vendor/bin/phpunit -c tests/phpunit.xml tests/e2e/ResultCacheEndToEndTest.php
351+
314352
compiler-tests:
315353
name: "Compiler Tests"
316354

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
]
7171
},
7272
"classmap": [
73+
"tests/e2e",
7374
"tests/PHPStan"
7475
]
7576
},

phpcs.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
<rule ref="SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator"/>
5757
<rule ref="SlevomatCodingStandard.Files.TypeNameMatchesFileName">
5858
<properties>
59-
<property name="rootNamespaces" type="array" value="compiler/src =>PHPStan\Compiler,src=>PHPStan,tests/PHPStan=>PHPStan"/>
59+
<property name="rootNamespaces" type="array" value="compiler/src =>PHPStan\Compiler,src=>PHPStan,tests/PHPStan=>PHPStan, tests/e2e => PHPStan\Tests"/>
6060
</properties>
6161
</rule>
6262
<rule ref="SlevomatCodingStandard.Classes.ModernClassNameReference"/>
@@ -87,6 +87,8 @@
8787
<exclude-pattern>src/Reflection/BetterReflection/SourceLocator/AutoloadSourceLocator.php</exclude-pattern>
8888
</rule>
8989
<exclude-pattern>tests/*/data</exclude-pattern>
90+
<exclude-pattern>tests/e2e/resultCache_1.php</exclude-pattern>
91+
<exclude-pattern>tests/e2e/resultCache_2.php</exclude-pattern>
9092
<exclude-pattern>tests/*/traits</exclude-pattern>
9193
<exclude-pattern>tests/tmp</exclude-pattern>
9294
<exclude-pattern>tests/notAutoloaded</exclude-pattern>

tests/e2e/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/PHP-Parser
2+
/tmp

tests/e2e/ResultCacheEndToEndTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Tests;
4+
5+
use Nette\Utils\Json;
6+
use PHPStan\File\SimpleRelativePathHelper;
7+
use PHPUnit\Framework\TestCase;
8+
use function escapeshellarg;
9+
use function file_get_contents;
10+
use function file_put_contents;
11+
12+
class ResultCacheEndToEndTest extends TestCase
13+
{
14+
15+
public function testResultCache(): void
16+
{
17+
chdir(__DIR__ . '/PHP-Parser');
18+
$this->runPhpstan(0);
19+
$this->assertResultCache(__DIR__ . '/resultCache_1.php');
20+
21+
$this->runPhpstan(0);
22+
$this->assertResultCache(__DIR__ . '/resultCache_1.php');
23+
24+
$lexerPath = __DIR__ . '/PHP-Parser/lib/PhpParser/Lexer.php';
25+
$lexerCode = file_get_contents($lexerPath);
26+
$originalLexerCode = $lexerCode;
27+
if ($lexerCode === false) {
28+
throw new \PHPStan\ShouldNotHappenException();
29+
}
30+
31+
$lexerCode = str_replace('@param string $code', '', $lexerCode);
32+
$lexerCode = str_replace('public function startLexing($code', 'public function startLexing(\\PhpParser\\Node\\Expr\\MethodCall $code', $lexerCode);
33+
file_put_contents($lexerPath, $lexerCode);
34+
35+
$result = $this->runPhpstan(1);
36+
$this->assertSame(2, $result['totals']['file_errors']);
37+
$this->assertSame(0, $result['totals']['errors']);
38+
$this->assertSame('Parameter #1 $source of function token_get_all expects string, PhpParser\Node\Expr\MethodCall given.', $result['files'][__DIR__ . '/PHP-Parser/lib/PhpParser/Lexer.php']['messages'][0]['message']);
39+
$this->assertSame('Parameter #1 $code of method PhpParser\Lexer::startLexing() expects PhpParser\Node\Expr\MethodCall, string given.', $result['files'][__DIR__ . '/PHP-Parser/lib/PhpParser/ParserAbstract.php']['messages'][0]['message']);
40+
$this->assertResultCache(__DIR__ . '/resultCache_2.php');
41+
42+
file_put_contents($lexerPath, $originalLexerCode);
43+
$this->runPhpstan(0);
44+
$this->assertResultCache(__DIR__ . '/resultCache_1.php');
45+
}
46+
47+
/**
48+
* @param int $expectedExitCode
49+
* @return mixed[]
50+
*/
51+
private function runPhpstan(int $expectedExitCode): array
52+
{
53+
exec(sprintf(
54+
'%s %s analyse -c %s -l 5 --no-progress --error-format json lib 2>&1',
55+
escapeshellarg(PHP_BINARY),
56+
escapeshellarg(__DIR__ . '/../../bin/phpstan'),
57+
escapeshellarg(__DIR__ . '/phpstan.neon')
58+
), $outputLines, $exitCode);
59+
$output = implode("\n", $outputLines);
60+
61+
try {
62+
$json = Json::decode($output, Json::FORCE_ARRAY);
63+
} catch (\Nette\Utils\JsonException $e) {
64+
$this->fail(sprintf('%s: %s', $e->getMessage(), $output));
65+
}
66+
67+
if ($exitCode !== $expectedExitCode) {
68+
$this->fail($output);
69+
}
70+
71+
return $json;
72+
}
73+
74+
/**
75+
* @param mixed[] $resultCache
76+
* @return mixed[]
77+
*/
78+
private function transformResultCache(array $resultCache): array
79+
{
80+
$new = [];
81+
foreach ($resultCache['dependencies'] as $file => $data) {
82+
$new[$this->relativizePath($file)] = array_map(function (string $file): string {
83+
return $this->relativizePath($file);
84+
}, $data['dependentFiles']);
85+
}
86+
87+
return $new;
88+
}
89+
90+
private function relativizePath(string $path): string
91+
{
92+
$helper = new SimpleRelativePathHelper(__DIR__ . '/PHP-Parser');
93+
return $helper->getRelativePath($path);
94+
}
95+
96+
private function assertResultCache(string $expectedCachePath): void
97+
{
98+
$resultCachePath = __DIR__ . '/tmp/resultCache.php';
99+
$resultCache = $this->transformResultCache(require $resultCachePath);
100+
$expectedResultCachePath = require $expectedCachePath;
101+
$this->assertSame($expectedResultCachePath, $resultCache);
102+
}
103+
104+
}

tests/e2e/baseline.neon

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: "#^Parameter \\#1 \\$autoload_function of function spl_autoload_register expects callable\\(string\\)\\: void, array\\('PhpParser\\\\\\\\Autoloader', 'autoload'\\) given\\.$#"
5+
count: 1
6+
path: PHP-Parser/lib/PhpParser/Autoloader.php
7+
8+
-
9+
message: "#^PHPDoc tag @param references unknown parameter\\: \\$interfaces$#"
10+
count: 1
11+
path: PHP-Parser/lib/PhpParser/Builder/Class_.php
12+
13+
-
14+
message: "#^PHPDoc tag @param references unknown parameter\\: \\$interfaces$#"
15+
count: 1
16+
path: PHP-Parser/lib/PhpParser/Builder/Interface_.php
17+
18+
-
19+
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$stmts\\.$#"
20+
count: 1
21+
path: PHP-Parser/lib/PhpParser/Builder/Interface_.php
22+
23+
-
24+
message: "#^Result of && is always false\\.$#"
25+
count: 1
26+
path: PHP-Parser/lib/PhpParser/BuilderAbstract.php
27+
28+
-
29+
message: "#^Method PhpParser\\\\BuilderAbstract\\:\\:normalizeValue\\(\\) should return PhpParser\\\\Node\\\\Expr but returns PhpParser\\\\Node\\.$#"
30+
count: 1
31+
path: PHP-Parser/lib/PhpParser/BuilderAbstract.php
32+
33+
-
34+
message: "#^Else branch is unreachable because previous condition is always true\\.$#"
35+
count: 1
36+
path: PHP-Parser/lib/PhpParser/BuilderAbstract.php
37+
38+
-
39+
message: "#^Access to an undefined property PhpParser\\\\BuilderAbstract\\:\\:\\$flags\\.$#"
40+
count: 2
41+
path: PHP-Parser/lib/PhpParser/BuilderAbstract.php
42+
43+
-
44+
message: "#^PHPDoc tag @param has invalid value \\(string\\|Node\\\\Name Name to alias\\)\\: Unexpected token \"Name\", expected variable at offset 88$#"
45+
count: 1
46+
path: PHP-Parser/lib/PhpParser/BuilderFactory.php
47+
48+
-
49+
message: "#^Expression \"@\\$undefinedVariable\" on a separate line does not do anything\\.$#"
50+
count: 1
51+
path: PHP-Parser/lib/PhpParser/Lexer.php
52+
53+
-
54+
message: "#^Undefined variable\\: \\$undefinedVariable$#"
55+
count: 1
56+
path: PHP-Parser/lib/PhpParser/Lexer.php
57+
58+
-
59+
message: "#^Unreachable statement \\- code above always terminates\\.$#"
60+
count: 1
61+
path: PHP-Parser/lib/PhpParser/Lexer.php
62+
63+
-
64+
message: "#^Empty array passed to foreach\\.$#"
65+
count: 1
66+
path: PHP-Parser/lib/PhpParser/Lexer/Emulative.php
67+
68+
-
69+
message: "#^Method PhpParser\\\\Node\\\\Expr\\\\Closure\\:\\:getStmts\\(\\) should return array\\<PhpParser\\\\Node\\\\Stmt\\> but returns array\\<PhpParser\\\\Node\\>\\.$#"
70+
count: 1
71+
path: PHP-Parser/lib/PhpParser/Node/Expr/Closure.php
72+
73+
-
74+
message: "#^Unsafe usage of new static\\(\\)\\.$#"
75+
count: 4
76+
path: PHP-Parser/lib/PhpParser/Node/Name.php
77+
78+
-
79+
message: "#^Method PhpParser\\\\Node\\\\Stmt\\\\ClassMethod\\:\\:getStmts\\(\\) should return array\\<PhpParser\\\\Node\\\\Stmt\\> but returns array\\<PhpParser\\\\Node\\>\\|null\\.$#"
80+
count: 1
81+
path: PHP-Parser/lib/PhpParser/Node/Stmt/ClassMethod.php
82+
83+
-
84+
message: "#^Method PhpParser\\\\Node\\\\Stmt\\\\Function_\\:\\:getStmts\\(\\) should return array\\<PhpParser\\\\Node\\\\Stmt\\> but returns array\\<PhpParser\\\\Node\\>\\.$#"
85+
count: 1
86+
path: PHP-Parser/lib/PhpParser/Node/Stmt/Function_.php
87+
88+
-
89+
message: "#^PHPDoc tag @param for parameter \\$attributes with type array\\|null is not subtype of native type array\\.$#"
90+
count: 1
91+
path: PHP-Parser/lib/PhpParser/Node/Stmt/TryCatch.php
92+
93+
-
94+
message: "#^Method PhpParser\\\\NodeVisitor\\\\NameResolver\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
95+
count: 1
96+
path: PHP-Parser/lib/PhpParser/NodeVisitor/NameResolver.php
97+
98+
-
99+
message: "#^Method PhpParser\\\\NodeVisitor\\\\NameResolver\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
100+
count: 1
101+
path: PHP-Parser/lib/PhpParser/NodeVisitor/NameResolver.php
102+
103+
-
104+
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$name\\.$#"
105+
count: 1
106+
path: PHP-Parser/lib/PhpParser/NodeVisitor/NameResolver.php
107+
108+
-
109+
message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$namespacedName\\.$#"
110+
count: 1
111+
path: PHP-Parser/lib/PhpParser/NodeVisitor/NameResolver.php
112+
113+
-
114+
message: "#^Method PhpParser\\\\NodeVisitorAbstract\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
115+
count: 1
116+
path: PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php
117+
118+
-
119+
message: "#^Method PhpParser\\\\NodeVisitorAbstract\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#"
120+
count: 1
121+
path: PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php
122+
123+
-
124+
message: "#^Method PhpParser\\\\NodeVisitorAbstract\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|false\\|null but return statement is missing\\.$#"
125+
count: 1
126+
path: PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php
127+
128+
-
129+
message: "#^Method PhpParser\\\\NodeVisitorAbstract\\:\\:afterTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#"
130+
count: 1
131+
path: PHP-Parser/lib/PhpParser/NodeVisitorAbstract.php
132+
133+
-
134+
message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\:\\:\\$class\\.$#"
135+
count: 1
136+
path: PHP-Parser/lib/PhpParser/Parser/Php5.php
137+
138+
-
139+
message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\:\\:\\$name\\.$#"
140+
count: 1
141+
path: PHP-Parser/lib/PhpParser/Parser/Php5.php
142+
143+
-
144+
message: "#^Variable \\$s might not be defined\\.$#"
145+
count: 3
146+
path: PHP-Parser/lib/PhpParser/Parser/Php5.php
147+
148+
-
149+
message: "#^Variable \\$s might not be defined\\.$#"
150+
count: 3
151+
path: PHP-Parser/lib/PhpParser/Parser/Php7.php
152+
153+
-
154+
message: "#^Property PhpParser\\\\ParserAbstract\\:\\:\\$endAttributes \\(array\\) does not accept string\\.$#"
155+
count: 1
156+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
157+
158+
-
159+
message: "#^Property PhpParser\\\\ParserAbstract\\:\\:\\$endAttributeStack \\(array\\<array\\>\\) does not accept array\\<int, string\\>\\.$#"
160+
count: 1
161+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
162+
163+
-
164+
message: "#^Comparison operation \"\\<\" between array\\|float\\|int\\<0, max\\> and int results in an error\\.$#"
165+
count: 2
166+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
167+
168+
-
169+
message: "#^Comparison operation \"\\>\\=\" between \\(array\\|float\\|int\\) and 0 results in an error\\.$#"
170+
count: 3
171+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
172+
173+
-
174+
message: "#^Comparison operation \"\\<\" between \\(array\\|float\\|int\\) and int results in an error\\.$#"
175+
count: 1
176+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
177+
178+
-
179+
message: "#^Variable \\$tokenValue might not be defined\\.$#"
180+
count: 1
181+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
182+
183+
-
184+
message: "#^Variable \\$action might not be defined\\.$#"
185+
count: 1
186+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
187+
188+
-
189+
message: "#^Negated boolean expression is always true\\.$#"
190+
count: 1
191+
path: PHP-Parser/lib/PhpParser/ParserAbstract.php
192+
193+
-
194+
message: "#^Strict comparison using \\=\\=\\= between null and PhpParser\\\\Node will always evaluate to false\\.$#"
195+
count: 1
196+
path: PHP-Parser/lib/PhpParser/PrettyPrinterAbstract.php
197+
198+
-
199+
message: "#^Else branch is unreachable because previous condition is always true\\.$#"
200+
count: 1
201+
path: PHP-Parser/lib/PhpParser/PrettyPrinterAbstract.php
202+
203+
-
204+
message: "#^Argument of an invalid type PhpParser\\\\Node supplied for foreach, only iterables are supported\\.$#"
205+
count: 1
206+
path: PHP-Parser/lib/PhpParser/Serializer/XML.php
207+

tests/e2e/phpstan.neon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
includes:
2+
- baseline.neon
3+
4+
parameters:
5+
featureToggles:
6+
resultCache: true
7+
tmpDir: tmp

0 commit comments

Comments
 (0)