Skip to content

Commit

Permalink
Add test for SymfonyContainerResultCacheMetaExtension
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Wirone committed Jan 8, 2025
1 parent fb65ea9 commit 2bc5a26
Showing 1 changed file with 304 additions and 0 deletions.
304 changes: 304 additions & 0 deletions tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
<?php declare(strict_types = 1);

namespace PHPStan\Symfony;

use PHPStan\Testing\PHPStanTestCase;
use function count;
use function file_put_contents;
use function sys_get_temp_dir;
use function tempnam;

final class SymfonyContainerResultCacheMetaExtensionTest extends PHPStanTestCase
{

private static string $configFilePath;

/**
* This test has to check if hash of the Symfony Container is correctly calculated,
* in order to do that we need to dynamically create a temporary configuration file
* because `PHPStanTestCase::getContainer()` caches container under key calculated from
* additional config files' paths, so we can't reuse the same config file between tests.
*/
public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/../../extension.neon',
self::$configFilePath,
];
}

/**
* @param list<string> $sameHashXmlContents
*
* @dataProvider provideContainerHashIsCalculatedCorrectlyCases
*/
public function testContainerHashIsCalculatedCorrectly(
array $sameHashXmlContents,
string $invalidatingXmlContent
): void
{
$hash = null;

self::assertGreaterThan(0, count($sameHashXmlContents));

foreach ($sameHashXmlContents as $xmlContent) {
$currentHash = $this->calculateSymfonyContainerHash($xmlContent);

if ($hash === null) {
$hash = $this->calculateSymfonyContainerHash($xmlContent);
} else {
self::assertSame($hash, $currentHash);
}
}

self::assertNotSame($hash, $this->calculateSymfonyContainerHash($invalidatingXmlContent));
}

/**
* @return iterable<string, array{list<string>, string}>
*/
public static function provideContainerHashIsCalculatedCorrectlyCases(): iterable
{
yield 'service "class" changes' => [
[
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
<service id="Bar" class="Bar" />
</services>
</container>
XML,
// Swapping services order in XML file does not affect the calculated hash
<<<'XML'
<container>
<services>
<service id="Bar" class="Bar" />
<service id="Foo" class="Foo" />
</services>
</container>
XML,
],
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
<service id="Bar" class="BarAdapter" />
</services>
</container>
XML,
];

yield 'service visibility changes' => [
[
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" public="true" />
</services>
</container>
XML,
// Placement of XML attributes does not affect the calculated hash
<<<'XML'
<container>
<services>
<service id="Foo" public="true" class="Foo" />
</services>
</container>
XML,
],
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" public="false" />
</services>
</container>
XML,
];

yield 'service syntheticity changes' => [
[
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" synthetic="false" />
</services>
</container>
XML,
],
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" synthetic="true" />
</services>
</container>
XML,
];

yield 'service alias changes' => [
[
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
<service id="Bar" class="Bar" />
<service id="Baz" alias="Foo" />
</services>
</container>
XML,
<<<'XML'
<container>
<services>
<service id="Baz" alias="Foo" />
<service id="Foo" class="Foo" />
<service id="Bar" class="Bar" />
</services>
</container>
XML,
],
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
<service id="Bar" class="Bar" />
<service id="Baz" alias="Bar" />
</services>
</container>
XML,
];

yield 'new service added' => [
[
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
</services>
</container>
XML,
],
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
<service id="Bar" class="Bar" />
</services>
</container>
XML,
];

yield 'service removed' => [
[
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
<service id="Bar" class="Bar" />
</services>
</container>
XML,
],
<<<'XML'
<container>
<services>
<service id="Foo" class="Foo" />
</services>
</container>
XML,
];

yield 'parameter value changes' => [
[
<<<'XML'
<container>
<parameters>
<parameter key="foo">foo</parameter>
<parameter key="bar">bar</parameter>
</parameters>
</container>
XML,
// Swapping parameters order in XML file does not affect the calculated hash
<<<'XML'
<container>
<parameters>
<parameter key="bar">bar</parameter>
<parameter key="foo">foo</parameter>
</parameters>
</container>
XML,
],
<<<'XML'
<container>
<parameters>
<parameter key="foo">foo</parameter>
<parameter key="bar">buzz</parameter>
</parameters>
</container>
XML,
];

yield 'new parameter added' => [
[
<<<'XML'
<container>
<parameters>
<parameter key="foo">foo</parameter>
</parameters>
</container>
XML,
],
<<<'XML'
<container>
<parameters>
<parameter key="foo">foo</parameter>
<parameter key="bar">bar</parameter>
</parameters>
</container>
XML,
];

yield 'parameter removed' => [
[
<<<'XML'
<container>
<parameters>
<parameter key="foo">foo</parameter>
<parameter key="bar">bar</parameter>
</parameters>
</container>
XML,
],
<<<'XML'
<container>
<parameters>
<parameter key="foo">foo</parameter>
</parameters>
</container>
XML,
];
}

private function calculateSymfonyContainerHash(string $xmlContent): string
{
$symfonyContainerXmlPath = tempnam(sys_get_temp_dir(), 'phpstan-meta-extension-test-container-xml-');
self::$configFilePath = tempnam(sys_get_temp_dir(), 'phpstan-meta-extension-test-config-') . '.neon';

file_put_contents(
self::$configFilePath,
<<<NEON
parameters:
symfony:
containerXmlPath: '$symfonyContainerXmlPath'
NEON,
);
file_put_contents($symfonyContainerXmlPath, $xmlContent);

$metaExtension = new SymfonyContainerResultCacheMetaExtension(
self::getContainer()->getByType(ParameterMap::class),
self::getContainer()->getByType(ServiceMap::class),
);

return $metaExtension->getHash();
}

}

0 comments on commit 2bc5a26

Please sign in to comment.