Skip to content

Commit 43d493b

Browse files
committed
Add test for SymfonyContainerResultCacheMetaExtension
This test ensures that hash calculated for Symfony's DI container remains the same or changes under provided conditions. This test is significantly slower than other unit tests, this is caused by rebuilding Nette container for each Symfony DI container's XML content - it's required in order to get fresh parameter/service maps from `self::getContainer()->getByType()`, because `self::getContainer()` caches containers for each `self::getAdditionalConfigFiles()` unique set, and with the same Nette config/container it would be retrieved from cache, so the hash correctness couldn't be verified properly.
1 parent c9f3147 commit 43d493b

File tree

1 file changed

+304
-0
lines changed

1 file changed

+304
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use PHPStan\Testing\PHPStanTestCase;
6+
use function count;
7+
use function file_put_contents;
8+
use function sys_get_temp_dir;
9+
use function tempnam;
10+
11+
final class SymfonyContainerResultCacheMetaExtensionTest extends PHPStanTestCase
12+
{
13+
14+
private static string $configFilePath;
15+
16+
/**
17+
* This test has to check if hash of the Symfony Container is correctly calculated,
18+
* in order to do that we need to dynamically create a temporary configuration file
19+
* because `PHPStanTestCase::getContainer()` caches container under key calculated from
20+
* additional config files' paths, so we can't reuse the same config file between tests.
21+
*/
22+
public static function getAdditionalConfigFiles(): array
23+
{
24+
return [
25+
__DIR__ . '/../../extension.neon',
26+
self::$configFilePath,
27+
];
28+
}
29+
30+
/**
31+
* @param list<string> $sameHashXmlContents
32+
*
33+
* @dataProvider provideContainerHashIsCalculatedCorrectlyCases
34+
*/
35+
public function testContainerHashIsCalculatedCorrectly(
36+
array $sameHashXmlContents,
37+
string $invalidatingXmlContent
38+
): void
39+
{
40+
$hash = null;
41+
42+
self::assertGreaterThan(0, count($sameHashXmlContents));
43+
44+
foreach ($sameHashXmlContents as $xmlContent) {
45+
$currentHash = $this->calculateSymfonyContainerHash($xmlContent);
46+
47+
if ($hash === null) {
48+
$hash = $currentHash;
49+
} else {
50+
self::assertSame($hash, $currentHash);
51+
}
52+
}
53+
54+
self::assertNotSame($hash, $this->calculateSymfonyContainerHash($invalidatingXmlContent));
55+
}
56+
57+
/**
58+
* @return iterable<string, array{list<string>, string}>
59+
*/
60+
public static function provideContainerHashIsCalculatedCorrectlyCases(): iterable
61+
{
62+
yield 'service "class" changes' => [
63+
[
64+
<<<'XML'
65+
<container>
66+
<services>
67+
<service id="Foo" class="Foo" />
68+
<service id="Bar" class="Bar" />
69+
</services>
70+
</container>
71+
XML,
72+
// Swapping services order in XML file does not affect the calculated hash
73+
<<<'XML'
74+
<container>
75+
<services>
76+
<service id="Bar" class="Bar" />
77+
<service id="Foo" class="Foo" />
78+
</services>
79+
</container>
80+
XML,
81+
],
82+
<<<'XML'
83+
<container>
84+
<services>
85+
<service id="Foo" class="Foo" />
86+
<service id="Bar" class="BarAdapter" />
87+
</services>
88+
</container>
89+
XML,
90+
];
91+
92+
yield 'service visibility changes' => [
93+
[
94+
<<<'XML'
95+
<container>
96+
<services>
97+
<service id="Foo" class="Foo" public="true" />
98+
</services>
99+
</container>
100+
XML,
101+
// Placement of XML attributes does not affect the calculated hash
102+
<<<'XML'
103+
<container>
104+
<services>
105+
<service id="Foo" public="true" class="Foo" />
106+
</services>
107+
</container>
108+
XML,
109+
],
110+
<<<'XML'
111+
<container>
112+
<services>
113+
<service id="Foo" class="Foo" public="false" />
114+
</services>
115+
</container>
116+
XML,
117+
];
118+
119+
yield 'service syntheticity changes' => [
120+
[
121+
<<<'XML'
122+
<container>
123+
<services>
124+
<service id="Foo" class="Foo" synthetic="false" />
125+
</services>
126+
</container>
127+
XML,
128+
],
129+
<<<'XML'
130+
<container>
131+
<services>
132+
<service id="Foo" class="Foo" synthetic="true" />
133+
</services>
134+
</container>
135+
XML,
136+
];
137+
138+
yield 'service alias changes' => [
139+
[
140+
<<<'XML'
141+
<container>
142+
<services>
143+
<service id="Foo" class="Foo" />
144+
<service id="Bar" class="Bar" />
145+
<service id="Baz" alias="Foo" />
146+
</services>
147+
</container>
148+
XML,
149+
<<<'XML'
150+
<container>
151+
<services>
152+
<service id="Baz" alias="Foo" />
153+
<service id="Foo" class="Foo" />
154+
<service id="Bar" class="Bar" />
155+
</services>
156+
</container>
157+
XML,
158+
],
159+
<<<'XML'
160+
<container>
161+
<services>
162+
<service id="Foo" class="Foo" />
163+
<service id="Bar" class="Bar" />
164+
<service id="Baz" alias="Bar" />
165+
</services>
166+
</container>
167+
XML,
168+
];
169+
170+
yield 'new service added' => [
171+
[
172+
<<<'XML'
173+
<container>
174+
<services>
175+
<service id="Foo" class="Foo" />
176+
</services>
177+
</container>
178+
XML,
179+
],
180+
<<<'XML'
181+
<container>
182+
<services>
183+
<service id="Foo" class="Foo" />
184+
<service id="Bar" class="Bar" />
185+
</services>
186+
</container>
187+
XML,
188+
];
189+
190+
yield 'service removed' => [
191+
[
192+
<<<'XML'
193+
<container>
194+
<services>
195+
<service id="Foo" class="Foo" />
196+
<service id="Bar" class="Bar" />
197+
</services>
198+
</container>
199+
XML,
200+
],
201+
<<<'XML'
202+
<container>
203+
<services>
204+
<service id="Foo" class="Foo" />
205+
</services>
206+
</container>
207+
XML,
208+
];
209+
210+
yield 'parameter value changes' => [
211+
[
212+
<<<'XML'
213+
<container>
214+
<parameters>
215+
<parameter key="foo">foo</parameter>
216+
<parameter key="bar">bar</parameter>
217+
</parameters>
218+
</container>
219+
XML,
220+
// Swapping parameters order in XML file does not affect the calculated hash
221+
<<<'XML'
222+
<container>
223+
<parameters>
224+
<parameter key="bar">bar</parameter>
225+
<parameter key="foo">foo</parameter>
226+
</parameters>
227+
</container>
228+
XML,
229+
],
230+
<<<'XML'
231+
<container>
232+
<parameters>
233+
<parameter key="foo">foo</parameter>
234+
<parameter key="bar">buzz</parameter>
235+
</parameters>
236+
</container>
237+
XML,
238+
];
239+
240+
yield 'new parameter added' => [
241+
[
242+
<<<'XML'
243+
<container>
244+
<parameters>
245+
<parameter key="foo">foo</parameter>
246+
</parameters>
247+
</container>
248+
XML,
249+
],
250+
<<<'XML'
251+
<container>
252+
<parameters>
253+
<parameter key="foo">foo</parameter>
254+
<parameter key="bar">bar</parameter>
255+
</parameters>
256+
</container>
257+
XML,
258+
];
259+
260+
yield 'parameter removed' => [
261+
[
262+
<<<'XML'
263+
<container>
264+
<parameters>
265+
<parameter key="foo">foo</parameter>
266+
<parameter key="bar">bar</parameter>
267+
</parameters>
268+
</container>
269+
XML,
270+
],
271+
<<<'XML'
272+
<container>
273+
<parameters>
274+
<parameter key="foo">foo</parameter>
275+
</parameters>
276+
</container>
277+
XML,
278+
];
279+
}
280+
281+
private function calculateSymfonyContainerHash(string $xmlContent): string
282+
{
283+
$symfonyContainerXmlPath = tempnam(sys_get_temp_dir(), 'phpstan-meta-extension-test-container-xml-');
284+
self::$configFilePath = tempnam(sys_get_temp_dir(), 'phpstan-meta-extension-test-config-') . '.neon';
285+
286+
file_put_contents(
287+
self::$configFilePath,
288+
<<<NEON
289+
parameters:
290+
symfony:
291+
containerXmlPath: '$symfonyContainerXmlPath'
292+
NEON,
293+
);
294+
file_put_contents($symfonyContainerXmlPath, $xmlContent);
295+
296+
$metaExtension = new SymfonyContainerResultCacheMetaExtension(
297+
self::getContainer()->getByType(ParameterMap::class),
298+
self::getContainer()->getByType(ServiceMap::class),
299+
);
300+
301+
return $metaExtension->getHash();
302+
}
303+
304+
}

0 commit comments

Comments
 (0)