Skip to content

Commit

Permalink
Merge pull request #421 from cs278/load-dependencies
Browse files Browse the repository at this point in the history
Automatically extract fixture dependencies
  • Loading branch information
greg0ire committed May 2, 2024
2 parents 838207b + 8b3d927 commit b7f5ed4
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 31 deletions.
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ parameters:
- tests/IntegrationTest.php

ignoreErrors:
-
message: "#^Call to an undefined method Doctrine\\\\Bundle\\\\FixturesBundle\\\\Loader\\\\SymfonyFixturesLoader\\:\\:getFixture\\(\\)\\.$#"
count: 1
path: src/Loader/SymfonyFixturesLoader.php

-
message: "#^Call to an undefined static method Doctrine\\\\Bundle\\\\FixturesBundle\\\\Loader\\\\SymfonyBridgeLoader\\:\\:addFixture\\(\\)\\.$#"
count: 1
Expand Down
49 changes: 27 additions & 22 deletions src/Loader/SymfonyFixturesLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
use Doctrine\Common\DataFixtures\FixtureInterface;
use LogicException;
use ReflectionClass;
use RuntimeException;

use function array_key_exists;
use function array_keys;
use function array_values;
use function get_class;
use function sprintf;
Expand Down Expand Up @@ -99,19 +98,22 @@ public function getFixtures(array $groups = []): array
return $fixtures;
}

$filteredFixtures = [];
foreach ($fixtures as $fixture) {
foreach ($groups as $group) {
$fixtureClass = get_class($fixture);
if (isset($this->groupsFixtureMapping[$group][$fixtureClass])) {
$filteredFixtures[$fixtureClass] = $fixture;
continue 2;
}
$requiredFixtures = [];
foreach ($groups as $group) {
if (! isset($this->groupsFixtureMapping[$group])) {
continue;
}

$requiredFixtures += $this->collectDependencies(...array_keys($this->groupsFixtureMapping[$group]));
}

foreach ($filteredFixtures as $fixture) {
$this->validateDependencies($filteredFixtures, $fixture);
$filteredFixtures = [];
foreach ($fixtures as $order => $fixture) {
$fixtureClass = get_class($fixture);
if (isset($requiredFixtures[$fixtureClass])) {
$filteredFixtures[$order] = $fixture;
continue;
}
}

return array_values($filteredFixtures);
Expand All @@ -130,22 +132,25 @@ private function addGroupsFixtureMapping(string $className, array $groups): void
}

/**
* @param string[] $fixtures An array of fixtures with class names as keys
* Collect any dependent fixtures from the given classes.
*
* @throws RuntimeException
* @psalm-return array<string,true>
*/
private function validateDependencies(array $fixtures, FixtureInterface $fixture): void
private function collectDependencies(string ...$fixtureClass): array
{
if (! $fixture instanceof DependentFixtureInterface) {
return;
}
$dependencies = [];

$dependenciesClasses = $fixture->getDependencies();
foreach ($fixtureClass as $class) {
$dependencies[$class] = true;
$fixture = $this->getFixture($class);

foreach ($dependenciesClasses as $class) {
if (! array_key_exists($class, $fixtures)) {
throw new RuntimeException(sprintf('Fixture "%s" was declared as a dependency for fixture "%s", but it was not included in any of the loaded fixture groups.', $class, get_class($fixture)));
if (! $fixture instanceof DependentFixtureInterface) {
continue;
}

$dependencies += $this->collectDependencies(...$fixture->getDependencies());
}

return $dependencies;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures;

use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Bundle\FixturesBundle\ORMFixtureInterface;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;

class WithDeepDependenciesFixtures implements ORMFixtureInterface, DependentFixtureInterface, FixtureGroupInterface
{
public function load(ObjectManager $manager): void
{
// ...
}

/**
* {@inheritDoc}
*/
public function getDependencies(): array
{
return [WithDependenciesFixtures::class, DependentOnRequiredConstructorArgsFixtures::class];
}

/**
* {@inheritDoc}
*/
public static function getGroups(): array
{
return ['groupWithDeepDependencies'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ public function getDependencies(): array
*/
public static function getGroups(): array
{
return ['missingDependencyGroup', 'fulfilledDependencyGroup'];
return ['groupWithDependencies', 'fulfilledDependencyGroup'];
}
}
23 changes: 15 additions & 8 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory;
use Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\DependentOnRequiredConstructorArgsFixtures;
use Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\OtherFixtures;
use Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\RequiredConstructorArgsFixtures;
use Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\WithDeepDependenciesFixtures;
use Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\WithDependenciesFixtures;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\EventManager;
Expand All @@ -19,7 +21,6 @@
use Doctrine\Persistence\ManagerRegistry;
use LogicException;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -173,18 +174,26 @@ public function testFixturesLoaderWithGroupsOptionViaTag(): void
$this->assertCount(0, $loader->getFixtures(['group3']));
}

public function testLoadFixturesViaGroupWithMissingDependency(): void
public function testLoadFixturesViaGroupWithDependenciesNotInGroup(): void
{
$kernel = new IntegrationTestKernel('dev', true);
$kernel->addServices(static function (ContainerBuilder $c): void {
// has a "staging" group via the getGroups() method
$c->autowire(OtherFixtures::class)
->addTag(FixturesCompilerPass::FIXTURE_TAG);

// no getGroups() method
$c->autowire(WithDependenciesFixtures::class)
->addTag(FixturesCompilerPass::FIXTURE_TAG);

$c->autowire(WithDeepDependenciesFixtures::class)
->addTag(FixturesCompilerPass::FIXTURE_TAG);

$c->autowire(RequiredConstructorArgsFixtures::class)
->setArgument('$fooRequiredArg', 'test')
->addTag(FixturesCompilerPass::FIXTURE_TAG);

$c->autowire(DependentOnRequiredConstructorArgsFixtures::class)
->addTag(FixturesCompilerPass::FIXTURE_TAG);

$c->setAlias('test.doctrine.fixtures.loader', new Alias('doctrine.fixtures.loader', true));
});
$kernel->boot();
Expand All @@ -193,10 +202,8 @@ public function testLoadFixturesViaGroupWithMissingDependency(): void
$loader = $container->get('test.doctrine.fixtures.loader');
$this->assertInstanceOf(SymfonyFixturesLoader::class, $loader);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Fixture "Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\OtherFixtures" was declared as a dependency for fixture "Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\WithDependenciesFixtures", but it was not included in any of the loaded fixture groups.');

$loader->getFixtures(['missingDependencyGroup']);
self::assertCount(2, $loader->getFixtures(['groupWithDependencies']));
self::assertCount(5, $loader->getFixtures(['groupWithDeepDependencies']));
}

public function testLoadFixturesViaGroupWithFulfilledDependency(): void
Expand Down

0 comments on commit b7f5ed4

Please sign in to comment.