From 068242800a0d1e4f5e9a4fbf281a5b051f53ec0b Mon Sep 17 00:00:00 2001 From: butschster Date: Thu, 10 Aug 2023 20:30:19 +0400 Subject: [PATCH 1/7] Changes behavior for loading system bootloaders. Now system bootloaders init only after Environment is bound to the container --- src/Boot/src/AbstractKernel.php | 7 ++-- .../Attributes/AttributesBootloader.php | 9 +++-- .../src/Bootloader/TokenizerBootloader.php | 15 ++++--- .../TokenizerListenerBootloader.php | 7 +--- src/Tokenizer/src/Config/TokenizerConfig.php | 11 ++++-- tests/Framework/BaseTestCase.php | 19 ++++++++- ...ibutesWithoutAnnotationsBootloaderTest.php | 39 ++++++++++++------- .../Tokenizer/TokenizerBootloaderTest.php | 4 ++ .../Dispatcher/ConsoleDispatcherTest.php | 10 +++-- tests/Framework/Http/ControllerTest.php | 3 +- .../Framework/Tokenizer/Config/ConfigTest.php | 28 +++++++++++++ tests/app/src/Command/DeadCommand.php | 4 +- tests/app/src/Controller/TestController.php | 2 +- tests/app/src/TestApp.php | 22 +++++++++++ 14 files changed, 137 insertions(+), 43 deletions(-) create mode 100644 tests/Framework/Tokenizer/Config/ConfigTest.php diff --git a/src/Boot/src/AbstractKernel.php b/src/Boot/src/AbstractKernel.php index 8d45f486e..b40aab518 100644 --- a/src/Boot/src/AbstractKernel.php +++ b/src/Boot/src/AbstractKernel.php @@ -87,8 +87,6 @@ protected function __construct( $this->finalizer = new Finalizer(); $container->bindSingleton(FinalizerInterface::class, $this->finalizer); - - $this->bootloader->bootload($this->defineSystemBootloaders()); } /** @@ -162,13 +160,14 @@ public function run(?EnvironmentInterface $environment = null): ?self $environment ??= new Environment(); $this->container->bindSingleton(EnvironmentInterface::class, $environment); - $this->fireCallbacks($this->runningCallbacks); - try { // will protect any against env overwrite action $this->container->runScope( [EnvironmentInterface::class => $environment], function (): void { + $this->bootloader->bootload($this->defineSystemBootloaders()); + $this->fireCallbacks($this->runningCallbacks); + $this->bootload(); $this->bootstrap(); diff --git a/src/Framework/Bootloader/Attributes/AttributesBootloader.php b/src/Framework/Bootloader/Attributes/AttributesBootloader.php index e0409de0f..fc3aae442 100644 --- a/src/Framework/Bootloader/Attributes/AttributesBootloader.php +++ b/src/Framework/Bootloader/Attributes/AttributesBootloader.php @@ -18,6 +18,7 @@ use Spiral\Attributes\Psr16CachedReader; use Spiral\Attributes\ReaderInterface; use Spiral\Boot\Bootloader\Bootloader; +use Spiral\Boot\EnvironmentInterface; use Spiral\Config\ConfiguratorInterface; class AttributesBootloader extends Bootloader @@ -32,13 +33,13 @@ public function __construct( ) { } - public function init(): void + public function init(EnvironmentInterface $env): void { $this->config->setDefaults( AttributesConfig::CONFIG, [ 'annotations' => [ - 'support' => true, + 'support' => $env->get('SUPPORT_ANNOTATIONS', true), ], ], ); @@ -68,7 +69,9 @@ private function initReader( $reader = new Psr16CachedReader($reader, $cache); } - if ($config->isAnnotationsReaderEnabled()) { + $supportAnnotations = $config->isAnnotationsReaderEnabled(); + + if ($supportAnnotations) { if (!\interface_exists(DoctrineReaderInterface::class)) { throw new InitializationException( 'Doctrine annotations reader is not available, please install "doctrine/annotations" package', diff --git a/src/Tokenizer/src/Bootloader/TokenizerBootloader.php b/src/Tokenizer/src/Bootloader/TokenizerBootloader.php index b4ad211ae..4114329ae 100644 --- a/src/Tokenizer/src/Bootloader/TokenizerBootloader.php +++ b/src/Tokenizer/src/Bootloader/TokenizerBootloader.php @@ -6,6 +6,7 @@ use Spiral\Boot\Bootloader\Bootloader; use Spiral\Boot\DirectoriesInterface; +use Spiral\Boot\EnvironmentInterface; use Spiral\Config\ConfiguratorInterface; use Spiral\Config\Patch\Append; use Spiral\Core\BinderInterface; @@ -47,7 +48,7 @@ public function __construct( ) { } - public function init(BinderInterface $binder, DirectoriesInterface $dirs): void + public function init(BinderInterface $binder, DirectoriesInterface $dirs, EnvironmentInterface $env): void { $binder->bindInjector(ClassLocator::class, ClassLocatorInjector::class); $binder->bindInjector(EnumLocator::class, EnumLocatorInjector::class); @@ -65,7 +66,11 @@ public function init(BinderInterface $binder, DirectoriesInterface $dirs): void 'tests', 'migrations', ], - ] + 'cache' => [ + 'directory' => $dirs->get('runtime') . 'cache/listeners', + 'enabled' => \filter_var($env->get('TOKENIZER_CACHE_TARGETS', false), \FILTER_VALIDATE_BOOL), + ], + ], ); } @@ -76,7 +81,7 @@ public function addDirectory(string $directory): void { $this->config->modify( TokenizerConfig::CONFIG, - new Append('directories', null, $directory) + new Append('directories', null, $directory), ); } @@ -88,13 +93,13 @@ public function addScopedDirectory(string $scope, string $directory): void if (!isset($this->config->getConfig(TokenizerConfig::CONFIG)['scopes'][$scope])) { $this->config->modify( TokenizerConfig::CONFIG, - new Append('scopes', $scope, []) + new Append('scopes', $scope, []), ); } $this->config->modify( TokenizerConfig::CONFIG, - new Append('scopes.' . $scope, null, $directory) + new Append('scopes.' . $scope, null, $directory), ); } } diff --git a/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php b/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php index 07d73832f..29593117a 100644 --- a/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php +++ b/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php @@ -111,12 +111,9 @@ private function makeCachedLoader( // but not read from there. return $factory->make($classLoader, [ 'memory' => $factory->make(Memory::class, [ - 'directory' => $config->getCacheDirectory() ?? $dirs->get('runtime') . 'cache/listeners', + 'directory' => $config->getCacheDirectory(), ]), - 'readCache' => \filter_var( - $env->get('TOKENIZER_CACHE_TARGETS', $config->isCacheEnabled()), - \FILTER_VALIDATE_BOOL - ), + 'readCache' => $config->isCacheEnabled(), ]); } diff --git a/src/Tokenizer/src/Config/TokenizerConfig.php b/src/Tokenizer/src/Config/TokenizerConfig.php index bd15a6ec2..121606414 100644 --- a/src/Tokenizer/src/Config/TokenizerConfig.php +++ b/src/Tokenizer/src/Config/TokenizerConfig.php @@ -81,6 +81,11 @@ public function getScope(string $scope): array ]; } + public function getScopes(): array + { + return $this->config['scopes'] ?? []; + } + /** * Check if tokenizer listeners cache is enabled. */ @@ -102,16 +107,16 @@ public function getCacheDirectory(): ?string public function isLoadClassesEnabled(): bool { - return (bool) ($this->config['load']['classes'] ?? true); + return (bool)($this->config['load']['classes'] ?? true); } public function isLoadEnumsEnabled(): bool { - return (bool) ($this->config['load']['enums'] ?? false); + return (bool)($this->config['load']['enums'] ?? false); } public function isLoadInterfacesEnabled(): bool { - return (bool) ($this->config['load']['interfaces'] ?? false); + return (bool)($this->config['load']['interfaces'] ?? false); } } diff --git a/tests/Framework/BaseTestCase.php b/tests/Framework/BaseTestCase.php index 500e20613..7f878d3a5 100644 --- a/tests/Framework/BaseTestCase.php +++ b/tests/Framework/BaseTestCase.php @@ -6,9 +6,14 @@ use Spiral\App\TestApp; use Spiral\Core\Container; +use Spiral\Testing\Traits\InteractsWithCore; abstract class BaseTestCase extends \Spiral\Testing\TestCase { + use InteractsWithCore; + + private array $disabledBootloaders = []; + public function rootDirectory(): string { return \realpath(__DIR__.'/../'); @@ -19,7 +24,19 @@ public function createAppInstance(Container $container = new Container()): TestA return TestApp::create( $this->defineDirectories($this->rootDirectory()), false - ); + )->disableBootloader(...$this->disabledBootloaders); + } + + public function withDisabledBootloaders(string ... $bootloader): self + { + $this->disabledBootloaders = $bootloader; + + return $this; + } + + public function getTestEnvVariables(): array + { + return [...static::ENV, ...$this->getEnvVariablesFromConfig()]; } protected function tearDown(): void diff --git a/tests/Framework/Bootloader/Attributes/AttributesWithoutAnnotationsBootloaderTest.php b/tests/Framework/Bootloader/Attributes/AttributesWithoutAnnotationsBootloaderTest.php index d4989e3c8..20642ebf5 100644 --- a/tests/Framework/Bootloader/Attributes/AttributesWithoutAnnotationsBootloaderTest.php +++ b/tests/Framework/Bootloader/Attributes/AttributesWithoutAnnotationsBootloaderTest.php @@ -5,30 +5,39 @@ namespace Framework\Bootloader\Attributes; use Spiral\Attributes\AttributeReader; +use Spiral\Attributes\Composite\SelectiveReader; +use Spiral\Attributes\Psr16CachedReader; use Spiral\Attributes\ReaderInterface; -use Spiral\Bootloader\Attributes\AttributesConfig; -use Spiral\Bootloader\Attributes\Factory; -use Spiral\Config\ConfiguratorInterface; +use Spiral\Testing\Attribute\Env; use Spiral\Tests\Framework\BaseTestCase; final class AttributesWithoutAnnotationsBootloaderTest extends BaseTestCase { - protected function setUp(): void + public const MAKE_APP_ON_STARTUP = false; + + #[Env('SUPPORT_ANNOTATIONS', 'false')] + public function testReaderBindingWithoutCache(): void { - $this->beforeInit(function (ConfiguratorInterface $configurator) { - $configurator->setDefaults(AttributesConfig::CONFIG, [ - 'annotations' => [ - 'support' => false, - ], - ]); - }); - - parent::setUp(); + $this + ->withDisabledBootloaders(\Spiral\Cache\Bootloader\CacheBootloader::class) + ->initApp($this->getTestEnvVariables()); + + $this->assertContainerBoundAsSingleton(ReaderInterface::class, AttributeReader::class); } - public function testReaderBinding(): void + #[Env('SUPPORT_ANNOTATIONS', 'false')] + public function testReaderBindingWithtCache(): void { + $this->initApp($this->getTestEnvVariables()); - $this->assertContainerBoundAsSingleton(ReaderInterface::class, AttributeReader::class); + $this->assertContainerBoundAsSingleton(ReaderInterface::class, Psr16CachedReader::class); + } + + #[Env('SUPPORT_ANNOTATIONS', 'true')] + public function testSelectiveReaderBinding(): void + { + $this->initApp($this->getTestEnvVariables()); + + $this->assertContainerBoundAsSingleton(ReaderInterface::class, SelectiveReader::class); } } diff --git a/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php b/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php index 99d6f46bb..21ea17a23 100644 --- a/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php +++ b/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php @@ -63,6 +63,10 @@ public function testConfig(): void 'tests', 'migrations', ], + 'cache' => [ + 'directory' => $this->getDirectoryByAlias('runtime') .'cache/listeners', + 'enabled' => true + ] ] ); } diff --git a/tests/Framework/Dispatcher/ConsoleDispatcherTest.php b/tests/Framework/Dispatcher/ConsoleDispatcherTest.php index 301de58ae..d83c51f3c 100644 --- a/tests/Framework/Dispatcher/ConsoleDispatcherTest.php +++ b/tests/Framework/Dispatcher/ConsoleDispatcherTest.php @@ -51,8 +51,9 @@ public function testException(): void ); $result = $output->fetch(); - $this->assertStringContainsString('undefined', $result); + $this->assertStringContainsString('This command is dead', $result); $this->assertStringContainsString('DeadCommand.php', $result); + $this->assertStringNotContainsString('throw new \InvalidArgumentException(\'This command is dead\');', $result); $this->assertNotEquals(0, $serveResult); } @@ -70,8 +71,9 @@ public function testExceptionVerbose(): void ); $result = $output->fetch(); - $this->assertStringContainsString('undefined', $result); + $this->assertStringContainsString('This command is dead', $result); $this->assertStringContainsString('DeadCommand.php', $result); + $this->assertStringNotContainsString('throw new \InvalidArgumentException(\'This command is dead\');', $result); $this->assertNotEquals(0, $serveResult); } @@ -90,9 +92,9 @@ public function testExceptionDebug(): void ); $result = $output->fetch(); - $this->assertStringContainsString('undefined', $result); + $this->assertStringContainsString('This command is dead', $result); $this->assertStringContainsString('DeadCommand.php', $result); - $this->assertStringContainsString('$undefined', $result); + $this->assertStringContainsString('throw new \InvalidArgumentException(\'This command is dead\');', $result); $this->assertNotEquals(0, $serveResult); } } diff --git a/tests/Framework/Http/ControllerTest.php b/tests/Framework/Http/ControllerTest.php index 8affb3842..e3ba2d24c 100644 --- a/tests/Framework/Http/ControllerTest.php +++ b/tests/Framework/Http/ControllerTest.php @@ -67,6 +67,7 @@ public function testPayloadActionBad(): void public function test500(): void { - $this->getHttp()->get('/error')->assertStatus(500); + $this->getHttp()->get('/error') + ->assertStatus(500); } } diff --git a/tests/Framework/Tokenizer/Config/ConfigTest.php b/tests/Framework/Tokenizer/Config/ConfigTest.php new file mode 100644 index 000000000..4fb9858b0 --- /dev/null +++ b/tests/Framework/Tokenizer/Config/ConfigTest.php @@ -0,0 +1,28 @@ +getContainer()->get(TokenizerConfig::class); + + $this->assertFalse($config->isCacheEnabled()); + } + + #[Env('TOKENIZER_CACHE_TARGETS', 'true')] + public function testCacheFromEnvEnabled(): void + { + $config = $this->getContainer()->get(TokenizerConfig::class); + + $this->assertTrue($config->isCacheEnabled()); + } +} diff --git a/tests/app/src/Command/DeadCommand.php b/tests/app/src/Command/DeadCommand.php index 4a72b0a89..0fc764b2a 100644 --- a/tests/app/src/Command/DeadCommand.php +++ b/tests/app/src/Command/DeadCommand.php @@ -4,14 +4,16 @@ namespace Spiral\App\Command; +use Spiral\Console\Attribute\AsCommand; use Spiral\Console\Command; +#[AsCommand(name: 'dead')] class DeadCommand extends Command { public const NAME = 'dead'; public function perform(): void { - echo $undefined; + throw new \InvalidArgumentException('This command is dead'); } } diff --git a/tests/app/src/Controller/TestController.php b/tests/app/src/Controller/TestController.php index 8fd52c676..3e2afb5bb 100644 --- a/tests/app/src/Controller/TestController.php +++ b/tests/app/src/Controller/TestController.php @@ -46,7 +46,7 @@ public function input(InputScope $i) public function error(): void { - echo $undefined; + throw new \InvalidArgumentException('Invalid argument'); } public function route(RouteInterface $route) diff --git a/tests/app/src/TestApp.php b/tests/app/src/TestApp.php index 5542e16b2..99575c3ce 100644 --- a/tests/app/src/TestApp.php +++ b/tests/app/src/TestApp.php @@ -16,6 +16,8 @@ class TestApp extends Kernel implements \Spiral\Testing\TestableKernelInterface { + private array $disabledBootloaders = []; + public const LOAD = [ TokenizerListenerBootloader::class, @@ -98,6 +100,16 @@ class TestApp extends Kernel implements \Spiral\Testing\TestableKernelInterface RoutesBootloader::class, ]; + protected function defineBootloaders(): array + { + $bootloaders = static::LOAD; + + // filter out disabled bootloaders + return \array_filter($bootloaders, function (string $bootloader): bool { + return !\in_array($bootloader, $this->disabledBootloaders, true); + }); + } + public function getContainer(): Container { return $this->container; @@ -112,4 +124,14 @@ public function getRegisteredBootloaders(): array { return $this->bootloader->getClasses(); } + + /** + * @param class-string<\Spiral\Boot\Bootloader\Bootloader> ...$bootloader + */ + public function disableBootloader(string ...$bootloader): self + { + $this->disabledBootloaders = \array_merge($this->disabledBootloaders, $bootloader); + + return $this; + } } From 008995fbad66a02c0d8e0e1bd9f9c3c9ca5d8c28 Mon Sep 17 00:00:00 2001 From: butschster Date: Thu, 10 Aug 2023 20:41:35 +0400 Subject: [PATCH 2/7] Adds Tokenizer info console command see #916 --- .../Command/Tokenizer/InfoCommand.php | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/Framework/Command/Tokenizer/InfoCommand.php diff --git a/src/Framework/Command/Tokenizer/InfoCommand.php b/src/Framework/Command/Tokenizer/InfoCommand.php new file mode 100644 index 000000000..28e37ceaf --- /dev/null +++ b/src/Framework/Command/Tokenizer/InfoCommand.php @@ -0,0 +1,59 @@ +info('Included directories:'); + $grid = $this->table(['Directory', 'Scope']); + foreach ($config->getDirectories() as $directory) { + $grid->addRow([\str_replace($dirs->get('root'), '', $directory), '']); + } + foreach ($config->getScopes() as $scope => $data) { + foreach ($data['directories'] ?? [] as $directory) { + $grid->addRow([\str_replace($dirs->get('root'), '', $directory), $scope]); + } + } + + $grid->render(); + + $this->newLine(); + + $this->info('Excluded directories:'); + $grid = $this->table(['Directory', 'Scope']); + foreach ($config->getExcludes() as $directory) { + $grid->addRow([\str_replace($dirs->get('root'), '', $directory), '']); + } + foreach ($config->getScopes() as $scope => $data) { + foreach ($data['exclude'] ?? [] as $directory) { + $grid->addRow([\str_replace($dirs->get('root'), '', $directory), $scope]); + } + } + + $grid->render(); + + $this->newLine(); + $this->info( + \sprintf('Tokenizer cache: %s', $config->isCacheEnabled() ? 'enabled' : 'disabled'), + ); + if (!$config->isCacheEnabled()) { + $this->comment('To enable cache, add "TOKENIZER_CACHE_TARGETS=true" to your .env file.'); + $this->comment('Read more at https://spiral.dev/docs/advanced-tokenizer/#class-listeners'); + } + + return self::SUCCESS; + } +} From 8bf0f83fdda0d5716e3a957fb178f039d490969e Mon Sep 17 00:00:00 2001 From: butschster Date: Thu, 10 Aug 2023 21:12:28 +0400 Subject: [PATCH 3/7] Fixes line endings --- .../Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php b/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php index 21ea17a23..33d4514f2 100644 --- a/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php +++ b/tests/Framework/Bootloader/Tokenizer/TokenizerBootloaderTest.php @@ -65,7 +65,7 @@ public function testConfig(): void ], 'cache' => [ 'directory' => $this->getDirectoryByAlias('runtime') .'cache/listeners', - 'enabled' => true + 'enabled' => false ] ] ); From 4c6ba20c7a31bef81a6779db6de0946b6e9c33af Mon Sep 17 00:00:00 2001 From: butschster Date: Mon, 14 Aug 2023 15:17:50 +0400 Subject: [PATCH 4/7] Fixes psalm issues --- .../src/Bootloader/TokenizerListenerBootloader.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php b/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php index 29593117a..e64e4d7c5 100644 --- a/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php +++ b/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php @@ -70,7 +70,7 @@ public function initCachedClassesLoader( EnvironmentInterface $env, TokenizerConfig $config, ): ClassesLoaderInterface { - return $this->makeCachedLoader($factory, $dirs, $env, $config, CachedClassesLoader::class); + return $this->makeCachedLoader($factory, $config, CachedClassesLoader::class); } public function initCachedEnumsLoader( @@ -79,7 +79,7 @@ public function initCachedEnumsLoader( EnvironmentInterface $env, TokenizerConfig $config, ): EnumsLoaderInterface { - return $this->makeCachedLoader($factory, $dirs, $env, $config, CachedEnumsLoader::class); + return $this->makeCachedLoader($factory, $config, CachedEnumsLoader::class); } public function initCachedInterfacesLoader( @@ -88,7 +88,7 @@ public function initCachedInterfacesLoader( EnvironmentInterface $env, TokenizerConfig $config, ): InterfacesLoaderInterface { - return $this->makeCachedLoader($factory, $dirs, $env, $config, CachedInterfacesLoader::class); + return $this->makeCachedLoader($factory, $config, CachedInterfacesLoader::class); } /** @@ -100,8 +100,6 @@ public function initCachedInterfacesLoader( */ private function makeCachedLoader( FactoryInterface $factory, - DirectoriesInterface $dirs, - EnvironmentInterface $env, TokenizerConfig $config, string $classLoader, ): mixed { From 8e0ccb2525c3f2d2957070cc328a9212656cfd69 Mon Sep 17 00:00:00 2001 From: butschster Date: Mon, 14 Aug 2023 15:44:08 +0400 Subject: [PATCH 5/7] Fixes tokenizer bootloaders tests --- .../TokenizerListenerBootloader.php | 6 - .../Bootloader/TokenizerBootloaderTest.php | 54 ++++ .../TokenizerListenerBootloaderTest.php | 246 +----------------- 3 files changed, 64 insertions(+), 242 deletions(-) create mode 100644 src/Tokenizer/tests/Bootloader/TokenizerBootloaderTest.php diff --git a/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php b/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php index e64e4d7c5..f97b7b0b4 100644 --- a/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php +++ b/src/Tokenizer/src/Bootloader/TokenizerListenerBootloader.php @@ -66,8 +66,6 @@ public function boot(AbstractKernel $kernel): void public function initCachedClassesLoader( FactoryInterface $factory, - DirectoriesInterface $dirs, - EnvironmentInterface $env, TokenizerConfig $config, ): ClassesLoaderInterface { return $this->makeCachedLoader($factory, $config, CachedClassesLoader::class); @@ -75,8 +73,6 @@ public function initCachedClassesLoader( public function initCachedEnumsLoader( FactoryInterface $factory, - DirectoriesInterface $dirs, - EnvironmentInterface $env, TokenizerConfig $config, ): EnumsLoaderInterface { return $this->makeCachedLoader($factory, $config, CachedEnumsLoader::class); @@ -84,8 +80,6 @@ public function initCachedEnumsLoader( public function initCachedInterfacesLoader( FactoryInterface $factory, - DirectoriesInterface $dirs, - EnvironmentInterface $env, TokenizerConfig $config, ): InterfacesLoaderInterface { return $this->makeCachedLoader($factory, $config, CachedInterfacesLoader::class); diff --git a/src/Tokenizer/tests/Bootloader/TokenizerBootloaderTest.php b/src/Tokenizer/tests/Bootloader/TokenizerBootloaderTest.php new file mode 100644 index 000000000..d7deca84e --- /dev/null +++ b/src/Tokenizer/tests/Bootloader/TokenizerBootloaderTest.php @@ -0,0 +1,54 @@ +shouldReceive('get')->once()->with('TOKENIZER_CACHE_TARGETS', false)->andReturn($readCache); + + $dirs->shouldReceive('get')->once()->with('app')->andReturn('app/'); + $dirs->shouldReceive('get')->once()->with('resources')->andReturn('resources/'); + $dirs->shouldReceive('get')->once()->with('config')->andReturn('config/'); + $dirs->shouldReceive('get')->once()->with('runtime')->andReturn('runtime/'); + + $bootloader = new TokenizerBootloader($config = new ConfigManager(new DirectoryLoader('config'))); + $bootloader->init($binder, $dirs, $env); + + $initConfig = $config->getConfig(TokenizerConfig::CONFIG); + + $this->assertSame('runtime/cache/listeners', $initConfig['cache']['directory']); + $this->assertSame($expected, $initConfig['cache']['enabled']); + } + + public static function readCacheDataProvider(): \Traversable + { + yield [true, true]; + yield [false, false]; + yield [1, true]; + yield [0, false]; + yield ['1', true]; + yield ['0', false]; + yield ['true', true]; + yield ['false', false]; + } +} diff --git a/src/Tokenizer/tests/Bootloader/TokenizerListenerBootloaderTest.php b/src/Tokenizer/tests/Bootloader/TokenizerListenerBootloaderTest.php index 933ada4fa..494d5612a 100644 --- a/src/Tokenizer/tests/Bootloader/TokenizerListenerBootloaderTest.php +++ b/src/Tokenizer/tests/Bootloader/TokenizerListenerBootloaderTest.php @@ -7,267 +7,52 @@ use Mockery as m; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Spiral\Attributes\ReaderInterface; -use Spiral\Boot\DirectoriesInterface; -use Spiral\Boot\EnvironmentInterface; use Spiral\Boot\Memory; use Spiral\Boot\MemoryInterface; use Spiral\Core\Container; use Spiral\Core\FactoryInterface; use Spiral\Tests\Tokenizer\Fixtures\TestCoreWithTokenizer; use Spiral\Tokenizer\Bootloader\TokenizerListenerBootloader; -use Spiral\Tokenizer\ClassesInterface; use Spiral\Tokenizer\Config\TokenizerConfig; -use Spiral\Tokenizer\EnumsInterface; -use Spiral\Tokenizer\InterfacesInterface; use Spiral\Tokenizer\Listener\CachedClassesLoader; use Spiral\Tokenizer\Listener\CachedEnumsLoader; use Spiral\Tokenizer\Listener\CachedInterfacesLoader; use Spiral\Tokenizer\Listener\ClassesLoaderInterface; use Spiral\Tokenizer\Listener\EnumsLoaderInterface; use Spiral\Tokenizer\Listener\InterfacesLoaderInterface; -use Spiral\Tokenizer\ScopedClassesInterface; -use Spiral\Tokenizer\ScopedEnumsInterface; -use Spiral\Tokenizer\ScopedInterfacesInterface; final class TokenizerListenerBootloaderTest extends TestCase { - public function testDisableCacheListenersThroughEnv(): void + public static function loadersProvider(): iterable { - $bootloader = new TokenizerListenerBootloader(); - - $factory = m::mock(FactoryInterface::class); - $factory->shouldReceive('make')->times(3)->withSomeOfArgs(Memory::class) - ->andReturn($memory = m::mock(MemoryInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedClassesLoader::class, [ - 'memory' => $memory, - 'readCache' => false - ])->andReturn($loader = m::mock(ClassesLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedEnumsLoader::class, [ - 'memory' => $memory, - 'readCache' => false - ])->andReturn($enumLoader = m::mock(EnumsLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedInterfacesLoader::class, [ - 'memory' => $memory, - 'readCache' => false - ])->andReturn($interfaceLoader = m::mock(InterfacesLoaderInterface::class)); - - $dirs = m::mock(DirectoriesInterface::class); - - $env = m::mock(EnvironmentInterface::class); - $env->shouldReceive('get')->with('TOKENIZER_CACHE_TARGETS', false)->andReturnFalse(); - $config = new TokenizerConfig([ - 'cache' => ['directory' => 'cache',], - ]); - - $this->assertSame( - $loader, - $bootloader->initCachedClassesLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $enumLoader, - $bootloader->initCachedEnumsLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $interfaceLoader, - $bootloader->initCachedInterfacesLoader($factory, $dirs, $env, $config), - ); - } - - public function testEnableCacheListenersThroughEnv(): void - { - $bootloader = new TokenizerListenerBootloader(); - - $factory = m::mock(FactoryInterface::class); - $factory->shouldReceive('make')->times(3)->with(Memory::class, [ - 'directory' => 'cache', - ])->andReturn($memory = m::mock(MemoryInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedClassesLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($loader = m::mock(ClassesLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedEnumsLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($enumLoader = m::mock(EnumsLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedInterfacesLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($interfaceLoader = m::mock(InterfacesLoaderInterface::class)); - - $dirs = m::mock(DirectoriesInterface::class); - - $env = m::mock(EnvironmentInterface::class); - $env->shouldReceive('get')->with('TOKENIZER_CACHE_TARGETS', false)->andReturnTrue(); - - $config = new TokenizerConfig(['cache' => ['directory' => 'cache',]]); - - $this->assertSame( - $loader, - $bootloader->initCachedClassesLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $enumLoader, - $bootloader->initCachedEnumsLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $interfaceLoader, - $bootloader->initCachedInterfacesLoader($factory, $dirs, $env, $config), - ); + yield 'Classes' => [CachedClassesLoader::class, ClassesLoaderInterface::class, 'initCachedClassesLoader']; + yield 'Enums' => [CachedEnumsLoader::class, EnumsLoaderInterface::class, 'initCachedEnumsLoader']; + yield 'Interfaces' => [CachedInterfacesLoader::class, InterfacesLoaderInterface::class, 'initCachedInterfacesLoader']; } - public function testEnableCacheListenersThroughConfig(): void + #[DataProvider(methodName: 'loadersProvider')] + public function testEnableCacheListenersThroughConfig(string $class, string $interface, string $method): void { $bootloader = new TokenizerListenerBootloader(); $factory = m::mock(FactoryInterface::class); - $factory->shouldReceive('make')->times(3)->with(Memory::class, [ + $factory->shouldReceive('make')->times(1)->with(Memory::class, [ 'directory' => 'cache', ])->andReturn($memory = m::mock(MemoryInterface::class)); - $factory->shouldReceive('make')->once()->with(CachedClassesLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($loader = m::mock(ClassesLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedEnumsLoader::class, [ + $factory->shouldReceive('make')->once()->with($class, [ 'memory' => $memory, 'readCache' => true - ])->andReturn($enumLoader = m::mock(EnumsLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedInterfacesLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($interfaceLoader = m::mock(InterfacesLoaderInterface::class)); - - $dirs = m::mock(DirectoriesInterface::class); - - $env = m::mock(EnvironmentInterface::class); - $env->shouldReceive('get')->with('TOKENIZER_CACHE_TARGETS', true)->andReturnTrue(); + ])->andReturn($loader = m::mock($interface)); $config = new TokenizerConfig(['cache' => ['directory' => 'cache', 'enabled' => true]]); $this->assertSame( $loader, - $bootloader->initCachedClassesLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $enumLoader, - $bootloader->initCachedEnumsLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $interfaceLoader, - $bootloader->initCachedInterfacesLoader($factory, $dirs, $env, $config), - ); - } - - public function testCacheListenersWithDefaultCacheDir(): void - { - $bootloader = new TokenizerListenerBootloader(); - - $factory = m::mock(FactoryInterface::class); - $factory->shouldReceive('make')->times(3)->with(Memory::class, [ - 'directory' => 'runtime/cache/listeners', - ])->andReturn($memory = m::mock(MemoryInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedClassesLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($loader = m::mock(ClassesLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedEnumsLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($enumLoader = m::mock(EnumsLoaderInterface::class)); - - $factory->shouldReceive('make')->once()->with(CachedInterfacesLoader::class, [ - 'memory' => $memory, - 'readCache' => true - ])->andReturn($interfaceLoader = m::mock(InterfacesLoaderInterface::class)); - - $dirs = m::mock(DirectoriesInterface::class); - $dirs->shouldReceive('get')->with('runtime')->andReturn('runtime/'); - - $env = m::mock(EnvironmentInterface::class); - $env->shouldReceive('get')->with('TOKENIZER_CACHE_TARGETS', false)->andReturnTrue(); - - $config = new TokenizerConfig(); - - $this->assertSame( - $loader, - $bootloader->initCachedClassesLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $enumLoader, - $bootloader->initCachedEnumsLoader($factory, $dirs, $env, $config), - ); - - $this->assertSame( - $interfaceLoader, - $bootloader->initCachedInterfacesLoader($factory, $dirs, $env, $config), + $bootloader->{$method}($factory, $config), ); } - #[DataProvider('readCacheDataProvider')] - public function testCastingReadCacheEnvVariable(mixed $readCache, bool $expected): void - { - $factory = new Container(); - $factory->bind(Memory::class, $this->createMock(MemoryInterface::class)); - $factory->bind(ReaderInterface::class, $this->createMock(ReaderInterface::class)); - $factory->bind(ClassesInterface::class, $this->createMock(ClassesInterface::class)); - $factory->bind(ScopedClassesInterface::class, $this->createMock(ScopedClassesInterface::class)); - $factory->bind(EnumsInterface::class, $this->createMock(EnumsInterface::class)); - $factory->bind(ScopedEnumsInterface::class, $this->createMock(ScopedEnumsInterface::class)); - $factory->bind(InterfacesInterface::class, $this->createMock(InterfacesInterface::class)); - $factory->bind(ScopedInterfacesInterface::class, $this->createMock(ScopedInterfacesInterface::class)); - - $env = $this->createMock(EnvironmentInterface::class); - $env - ->expects($this->exactly(3)) - ->method('get') - ->with('TOKENIZER_CACHE_TARGETS') - ->willReturn($readCache); - - $bootloader = new TokenizerListenerBootloader(); - $loader = $bootloader->initCachedClassesLoader( - $factory, - $this->createMock(DirectoriesInterface::class), - $env, - new TokenizerConfig() - ); - - $enumLoader = $bootloader->initCachedEnumsLoader( - $factory, - $this->createMock(DirectoriesInterface::class), - $env, - new TokenizerConfig() - ); - - $interfaceLoader = $bootloader->initCachedInterfacesLoader( - $factory, - $this->createMock(DirectoriesInterface::class), - $env, - new TokenizerConfig() - ); - - $this->assertSame($expected, (new \ReflectionProperty($loader, 'readCache'))->getValue($loader)); - $this->assertSame($expected, (new \ReflectionProperty($enumLoader, 'readCache'))->getValue($enumLoader)); - $this->assertSame($expected, (new \ReflectionProperty($interfaceLoader, 'readCache'))->getValue($interfaceLoader)); - } - public function testAddDirectoryInBootloaderInit(): void { $container = new Container(); @@ -281,15 +66,4 @@ public function testAddDirectoryInBootloaderInit(): void )); } - public static function readCacheDataProvider(): \Traversable - { - yield [true, true]; - yield [false, false]; - yield [1, true]; - yield [0, false]; - yield ['1', true]; - yield ['0', false]; - yield ['true', true]; - yield ['false', false]; - } } From e1b84385922b9746e831079d6aec2b16c8ff3a21 Mon Sep 17 00:00:00 2001 From: butschster Date: Mon, 14 Aug 2023 16:47:58 +0400 Subject: [PATCH 6/7] - Fixes Console Dispatcher tests - Fixes queue sync driver tests --- src/Queue/tests/Driver/SyncDriverTest.php | 8 ++++++++ tests/Framework/Dispatcher/ConsoleDispatcherTest.php | 9 ++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Queue/tests/Driver/SyncDriverTest.php b/src/Queue/tests/Driver/SyncDriverTest.php index d3d1f3962..42e5932a6 100644 --- a/src/Queue/tests/Driver/SyncDriverTest.php +++ b/src/Queue/tests/Driver/SyncDriverTest.php @@ -23,6 +23,7 @@ final class SyncDriverTest extends TestCase private SyncDriver $queue; private m\LegacyMockInterface|m\MockInterface|CoreInterface $core; private m\LegacyMockInterface|m\MockInterface|UuidFactoryInterface $factory; + private UuidFactoryInterface $realfactory; protected function setUp(): void { @@ -31,6 +32,7 @@ protected function setUp(): void $container = new Container(); $container->bind(TracerInterface::class, new NullTracer($container)); + $this->realfactory = Uuid::getFactory(); Uuid::setFactory($this->factory = m::mock(UuidFactoryInterface::class)); $this->queue = new SyncDriver( @@ -70,4 +72,10 @@ public static function payloadDataProvider(): \Traversable yield [123]; yield [null]; } + + protected function tearDown(): void + { + Uuid::setFactory($this->realfactory); + parent::tearDown(); + } } diff --git a/tests/Framework/Dispatcher/ConsoleDispatcherTest.php b/tests/Framework/Dispatcher/ConsoleDispatcherTest.php index d83c51f3c..3b587088c 100644 --- a/tests/Framework/Dispatcher/ConsoleDispatcherTest.php +++ b/tests/Framework/Dispatcher/ConsoleDispatcherTest.php @@ -59,13 +59,13 @@ public function testException(): void public function testExceptionVerbose(): void { - $output = new BufferedOutput(); $this->initApp(); - $output->setVerbosity(BufferedOutput::VERBOSITY_VERBOSE); + $output = new BufferedOutput(); $serveResult = $this->getContainer()->get(ConsoleDispatcher::class)->serve( new ArrayInput([ 'command' => 'dead', + '-vv' ]), $output ); @@ -79,14 +79,13 @@ public function testExceptionVerbose(): void public function testExceptionDebug(): void { - $output = new BufferedOutput(); $this->initApp(); - $output->setVerbosity(BufferedOutput::VERBOSITY_DEBUG); - + $output = new BufferedOutput(); $serveResult = $this->getContainer()->get(ConsoleDispatcher::class)->serve( new ArrayInput([ 'command' => 'dead', + '-vvv' ]), $output ); From 5d56380bd514d5a95a472747325001848a4e2480 Mon Sep 17 00:00:00 2001 From: butschster Date: Mon, 14 Aug 2023 16:54:12 +0400 Subject: [PATCH 7/7] Minimal version of spiral/testing increased up to 2.4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ee98ee429..8f33ce408 100644 --- a/composer.json +++ b/composer.json @@ -139,7 +139,7 @@ "rector/rector": "0.15.23", "spiral/code-style": "^1.1", "spiral/nyholm-bridge": "^1.2", - "spiral/testing": "^2.3", + "spiral/testing": "^2.4", "spiral/validator": "^1.3", "symplify/monorepo-builder": "^10.2.7", "vimeo/psalm": "^5.9"