Skip to content

Commit ea69633

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 fb65ea9 commit ea69633

File tree

1 file changed

+305
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)