diff --git a/src/Manifests/ContainerMixinManifest.php b/src/Manifests/ContainerMixinManifest.php index afb6f13..33b876e 100644 --- a/src/Manifests/ContainerMixinManifest.php +++ b/src/Manifests/ContainerMixinManifest.php @@ -120,8 +120,8 @@ public function build(array $extensionMethods, array $extensions) $tags[] = new Method( $methodName, - returnType: $reflectionMethod->hasReturnType() ? (new TypeResolver())->resolve($reflectionMethod->getReturnType()) : new Mixed_(), parameters: $parameters, + returnType: $reflectionMethod->hasReturnType() ? (new TypeResolver())->resolve($reflectionMethod->getReturnType()) : new Mixed_(), ); } @@ -142,8 +142,9 @@ public function build(array $extensionMethods, array $extensions) public function shouldRecompile(): bool { return !is_file($this->containerMixinPath) || - // We check here if the manifest has been generated before changing the installed.json composer file - filemtime($this->containerMixinPath) <= filemtime($this->vendorPath.'/composer/installed.json'); + // We check here if the manifest has been generated before changing the installed.json composer file or the project composer.json + filemtime($this->containerMixinPath) <= filemtime($this->vendorPath.'/composer/installed.json') || + filemtime($this->containerMixinPath) <= filemtime($this->basePath.'/composer.json'); } /** diff --git a/src/Manifests/PackageManifest.php b/src/Manifests/PackageManifest.php index 53e964b..c81c1ca 100644 --- a/src/Manifests/PackageManifest.php +++ b/src/Manifests/PackageManifest.php @@ -120,6 +120,14 @@ public function build() } } + if (is_file($path = $this->basePath.'/composer.json')) { + $package = json_decode(file_get_contents($path), true); + + if (isset($package['extra']['faker'])) { + $packagesToProvide[$package['name'] ?? 'root'] = $package['extra']['faker']; + } + } + $this->write( $packagesToProvide ); @@ -133,8 +141,9 @@ public function build() public function shouldRecompile(): bool { return !is_file($this->manifestPath) || - // We check here if the manifest has been generated before changing the installed.json composer file - filemtime($this->manifestPath) <= filemtime($this->vendorPath.'/composer/installed.json'); + // We check here if the manifest has been generated before changing the installed.json composer file or the project composer.json + filemtime($this->manifestPath) <= filemtime($this->vendorPath.'/composer/installed.json') || + filemtime($this->manifestPath) <= filemtime($this->basePath.'/composer.json'); } /** diff --git a/tests/Support/Concerns/CreatesTemporaryProjects.php b/tests/Support/Concerns/CreatesTemporaryProjects.php new file mode 100644 index 0000000..c16548c --- /dev/null +++ b/tests/Support/Concerns/CreatesTemporaryProjects.php @@ -0,0 +1,65 @@ + $installedPackages]) + ); + + if ($composer !== null) { + file_put_contents($projectPath.'/composer.json', json_encode($composer)); + } + + $this->temporaryProjectPaths[] = $projectPath; + + return $projectPath; + } + + /** + * Remove every temporary project created during the test. + * + * @return void + */ + private function deleteTemporaryProjects(): void + { + foreach ($this->temporaryProjectPaths as $projectPath) { + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($projectPath, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $file) { + $file->isDir() ? rmdir($file->getPathname()) : unlink($file->getPathname()); + } + + rmdir($projectPath); + } + + $this->temporaryProjectPaths = []; + } +} diff --git a/tests/Support/composer.json b/tests/Support/composer.json new file mode 100644 index 0000000..b593485 --- /dev/null +++ b/tests/Support/composer.json @@ -0,0 +1,3 @@ +{ + "name": "xefi/faker-php-tests-support" +} diff --git a/tests/Unit/ContainerMixinManifestTest.php b/tests/Unit/ContainerMixinManifestTest.php index cfae1ea..6dce686 100644 --- a/tests/Unit/ContainerMixinManifestTest.php +++ b/tests/Unit/ContainerMixinManifestTest.php @@ -4,8 +4,12 @@ namespace Xefi\Faker\Tests\Unit; +use Xefi\Faker\Tests\Support\Concerns\CreatesTemporaryProjects; + final class ContainerMixinManifestTest extends TestCase { + use CreatesTemporaryProjects; + protected function setUp(): void { parent::setUp(); @@ -17,6 +21,13 @@ protected function setUp(): void ]); } + protected function tearDown(): void + { + $this->deleteTemporaryProjects(); + + parent::tearDown(); + } + public function testContainerMixinBuild() { @unlink('/tmp/ContainerMixin.php'); @@ -111,6 +122,27 @@ public function testShouldRecompile() unlink('/tmp/ContainerMixin.php'); } + public function testShouldRecompileWhenTheProjectComposerFileChanged() + { + $projectPath = $this->createTemporaryProject(['name' => 'xefi/my-project']); + + $container = new \Xefi\Faker\Container\Container(shouldBuildContainerMixin: false); + $manifest = new \Xefi\Faker\Manifests\ContainerMixinManifest($projectPath, $projectPath.'/ContainerMixin.php'); + $manifest->build($container->getExtensionMethods(), $container->getExtensions()); + touch($projectPath.'/vendor/composer/installed.json', time() - 1); + touch($projectPath.'/composer.json', time() - 1); + + $this->assertFalse($manifest->shouldRecompile()); + + // Test on current time + touch($projectPath.'/composer.json'); + $this->assertTrue($manifest->shouldRecompile()); + + // Test on future + touch($projectPath.'/composer.json', time() + 1); + $this->assertTrue($manifest->shouldRecompile()); + } + public function testNoExtension() { @unlink('/tmp/ContainerMixin.php'); diff --git a/tests/Unit/PackageManifestTest.php b/tests/Unit/PackageManifestTest.php index fc3e1e8..3e2b414 100644 --- a/tests/Unit/PackageManifestTest.php +++ b/tests/Unit/PackageManifestTest.php @@ -5,9 +5,20 @@ namespace Xefi\Faker\Tests\Unit; use Xefi\Faker\Manifests\PackageManifest; +use Xefi\Faker\Tests\Support\Concerns\CreatesTemporaryProjects; +use Xefi\Faker\Tests\Support\TestServiceProvider; final class PackageManifestTest extends TestCase { + use CreatesTemporaryProjects; + + protected function tearDown(): void + { + $this->deleteTemporaryProjects(); + + parent::tearDown(); + } + public function testAssetLoading() { @unlink('/tmp/packages.php'); @@ -41,4 +52,196 @@ public function testShouldRecompile() unlink('/tmp/packages.php'); } + + public function testProjectProvidersAreDiscovered() + { + $projectPath = $this->createTemporaryProject([ + 'name' => 'xefi/my-project', + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ]); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + + $this->assertEquals( + [ + 'xefi/my-project' => [TestServiceProvider::class], + ], + $manifest->providers() + ); + } + + public function testProjectProvidersAreMergedWithTheInstalledPackagesOnes() + { + $projectPath = $this->createTemporaryProject( + [ + 'name' => 'xefi/my-project', + 'extra' => [ + 'faker' => [ + 'providers' => ['Xefi\Faker\Tests\Support\ProjectServiceProvider'], + ], + ], + ], + [ + [ + 'name' => 'xefi/faker-number', + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ], + ] + ); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + + $this->assertEquals( + [ + 'xefi/faker-number' => [TestServiceProvider::class], + 'xefi/my-project' => ['Xefi\Faker\Tests\Support\ProjectServiceProvider'], + ], + $manifest->providers() + ); + } + + public function testProjectProvidersAreKeyedByRootWhenTheProjectHasNoName() + { + $projectPath = $this->createTemporaryProject([ + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ]); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + + $this->assertEquals( + [ + 'root' => [TestServiceProvider::class], + ], + $manifest->providers() + ); + } + + public function testProjectWithoutFakerConfigurationIsIgnored() + { + $projectPath = $this->createTemporaryProject( + [ + 'name' => 'xefi/my-project', + 'extra' => [ + 'branch-alias' => ['dev-master' => '2.0.x-dev'], + ], + ], + [ + [ + 'name' => 'xefi/faker-number', + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ], + ] + ); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + + $this->assertEquals( + [ + 'xefi/faker-number' => [TestServiceProvider::class], + ], + $manifest->providers() + ); + } + + public function testProjectTakesPrecedenceOverAnInstalledPackageOfTheSameName() + { + $projectPath = $this->createTemporaryProject( + [ + 'name' => 'xefi/faker-number', + 'extra' => [ + 'faker' => [ + 'providers' => ['Xefi\Faker\Tests\Support\ProjectServiceProvider'], + ], + ], + ], + [ + [ + 'name' => 'xefi/faker-number', + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ], + ] + ); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + + $this->assertEquals( + [ + 'xefi/faker-number' => ['Xefi\Faker\Tests\Support\ProjectServiceProvider'], + ], + $manifest->providers() + ); + } + + public function testProjectWithoutComposerFileIsIgnored() + { + $projectPath = $this->createTemporaryProject(null, [ + [ + 'name' => 'xefi/faker-number', + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ], + ]); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + $manifest->build(); + + $this->assertEquals( + [ + 'xefi/faker-number' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + require $projectPath.'/packages.php' + ); + } + + public function testShouldRecompileWhenTheProjectComposerFileChanged() + { + $projectPath = $this->createTemporaryProject([ + 'name' => 'xefi/my-project', + 'extra' => [ + 'faker' => [ + 'providers' => [TestServiceProvider::class], + ], + ], + ]); + + $manifest = new PackageManifest($projectPath, $projectPath.'/packages.php'); + $manifest->build(); + touch($projectPath.'/vendor/composer/installed.json', time() - 1); + touch($projectPath.'/composer.json', time() - 1); + + $this->assertFalse($manifest->shouldRecompile()); + + // Test on current time + touch($projectPath.'/composer.json'); + $this->assertTrue($manifest->shouldRecompile()); + + // Test on future + touch($projectPath.'/composer.json', time() + 1); + $this->assertTrue($manifest->shouldRecompile()); + } }