From fea3bb73010ef8b056b49a4141a4789568b0c9ae Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:21:31 +0400 Subject: [PATCH 1/3] Lazy tasks. --- .../documentator/src/Commands/Preprocess.php | 5 +- .../src/Processor/Contracts/Task.php | 2 +- .../documentator/src/Processor/Executor.php | 15 +-- .../src/Processor/InstanceList.php | 101 ++++++++++++++++++ .../documentator/src/Processor/Processor.php | 38 +++---- .../src/Processor/ProcessorTest.php | 29 ++--- .../src/Processor/Tasks/Preprocess/Task.php | 2 +- 7 files changed, 146 insertions(+), 46 deletions(-) create mode 100644 packages/documentator/src/Processor/InstanceList.php diff --git a/packages/documentator/src/Commands/Preprocess.php b/packages/documentator/src/Commands/Preprocess.php index 03e6cb95..2d9f95fd 100644 --- a/packages/documentator/src/Commands/Preprocess.php +++ b/packages/documentator/src/Commands/Preprocess.php @@ -3,6 +3,7 @@ namespace LastDragon_ru\LaraASP\Documentator\Commands; use Illuminate\Console\Command; +use LastDragon_ru\LaraASP\Core\Application\ContainerResolver; use LastDragon_ru\LaraASP\Core\Utils\Cast; use LastDragon_ru\LaraASP\Core\Utils\Path; use LastDragon_ru\LaraASP\Documentator\Package; @@ -75,7 +76,7 @@ public function __construct( parent::__construct(); } - public function __invoke(Formatter $formatter): void { + public function __invoke(ContainerResolver $container, Formatter $formatter): void { $cwd = getcwd(); $path = Cast::toString($this->argument('path') ?? $cwd); $path = Path::normalize($path); @@ -99,7 +100,7 @@ public function __invoke(Formatter $formatter): void { $this->output->writeln($line, OutputInterface::OUTPUT_NORMAL | $resultVerbosity); }; - $duration = (new Processor()) + $duration = (new Processor($container)) ->task($this->preprocess) ->run($path, $exclude, $listener); diff --git a/packages/documentator/src/Processor/Contracts/Task.php b/packages/documentator/src/Processor/Contracts/Task.php index a2faf8c9..f1f407ed 100644 --- a/packages/documentator/src/Processor/Contracts/Task.php +++ b/packages/documentator/src/Processor/Contracts/Task.php @@ -13,7 +13,7 @@ interface Task { * * @return non-empty-list */ - public function getExtensions(): array; + public static function getExtensions(): array; /** * Performs action on the `$file`. diff --git a/packages/documentator/src/Processor/Executor.php b/packages/documentator/src/Processor/Executor.php index cf368908..305609d4 100644 --- a/packages/documentator/src/Processor/Executor.php +++ b/packages/documentator/src/Processor/Executor.php @@ -21,7 +21,6 @@ use Throwable; use Traversable; -use function array_merge; use function array_values; use function microtime; use function preg_match; @@ -48,9 +47,9 @@ public function __construct( */ private readonly array $exclude, /** - * @var array> + * @var InstanceList */ - private readonly array $tasks, + private readonly InstanceList $tasks, /** * @var Iterator */ @@ -100,7 +99,7 @@ private function runFile(File $file): float { } // Process - $tasks = array_merge($this->tasks[$file->getExtension()] ?? [], $this->tasks['*'] ?? []); + $tasks = $this->tasks->get($file->getExtension(), '*'); $start = microtime(true); $paused = 0; $this->stack[$path] = $file; @@ -219,12 +218,8 @@ private function dispatch(Dependency|File $file, Result $result, float $duration private function isSkipped(File $file): bool { // Tasks? - if (!isset($this->tasks['*'])) { - $tasks = $this->tasks[$file->getExtension()] ?? []; - - if (!$tasks) { - return true; - } + if (!$this->tasks->has($file->getExtension(), '*')) { + return true; } // Outside? diff --git a/packages/documentator/src/Processor/InstanceList.php b/packages/documentator/src/Processor/InstanceList.php new file mode 100644 index 00000000..ee753faf --- /dev/null +++ b/packages/documentator/src/Processor/InstanceList.php @@ -0,0 +1,101 @@ +, TInstance> + */ + private array $instances = []; + + /** + * @var array, class-string>> + */ + private array $map = []; + + public function __construct( + protected readonly ContainerResolver $container, + /** + * @var Closure(TInstance|class-string):(array|string|null) + */ + protected readonly Closure $keyResolver, + ) { + // empty + } + + /** + * @return list + */ + public function keys(): array { + return array_keys($this->map); + } + + public function has(string ...$key): bool { + $exists = false; + + foreach ($key as $k) { + if (isset($this->map[$k])) { + $exists = true; + break; + } + } + + return $exists; + } + + /** + * @return list + */ + public function get(string ...$key): array { + $instances = []; + + foreach ($key as $k) { + foreach ($this->map[$k] ?? [] as $class) { + $instances[$class] ??= $this->resolve($class); + } + } + + return array_values($instances); + } + + /** + * @param TInstance|class-string $instance + */ + public function add(object|string $instance): static { + $keys = (array) ($this->keyResolver)($instance); + + foreach ($keys as $key) { + $class = is_string($instance) ? $instance : $instance::class; + $resolved = is_object($instance) ? $instance : null; + $this->map[$key][$class] = $class; + + if ($resolved) { + $this->instances[$class] = $resolved; + } + } + + return $this; + } + + /** + * @param class-string $class + * + * @return TInstance + */ + protected function resolve(string $class): object { + return $this->instances[$class] ??= $this->container->getInstance()->make($class); + } +} diff --git a/packages/documentator/src/Processor/Processor.php b/packages/documentator/src/Processor/Processor.php index 5fe5b524..7c9caa4e 100644 --- a/packages/documentator/src/Processor/Processor.php +++ b/packages/documentator/src/Processor/Processor.php @@ -4,6 +4,7 @@ use Closure; use Exception; +use LastDragon_ru\LaraASP\Core\Application\ContainerResolver; use LastDragon_ru\LaraASP\Documentator\Processor\Contracts\Task; use LastDragon_ru\LaraASP\Documentator\Processor\Exceptions\ProcessingFailed; use LastDragon_ru\LaraASP\Documentator\Processor\Exceptions\ProcessorError; @@ -11,32 +12,33 @@ use LastDragon_ru\LaraASP\Documentator\Processor\FileSystem\FileSystem; use Symfony\Component\Finder\Glob; -use function array_keys; use function array_map; -use function array_unique; -use function in_array; use function microtime; class Processor { /** - * @var array> + * @var InstanceList */ - private array $tasks = []; + private InstanceList $tasks; - public function __construct() { - // empty + public function __construct(ContainerResolver $container) { + $this->tasks = new InstanceList($container, $this->key(...)); } - public function task(Task $task): static { - foreach (array_unique($task->getExtensions()) as $ext) { - if (!isset($this->tasks[$ext])) { - $this->tasks[$ext] = []; - } + /** + * @param Task|class-string $task + * + * @return list + */ + private function key(Task|string $task): array { + return $task::getExtensions(); + } - if (!in_array($task, $this->tasks[$ext], true)) { - $this->tasks[$ext][] = $task; - } - } + /** + * @param Task|class-string $task + */ + public function task(Task|string $task): static { + $this->tasks->add($task); return $this; } @@ -47,8 +49,8 @@ public function task(Task $task): static { */ public function run(string $path, array|string|null $exclude = null, ?Closure $listener = null): float { $start = microtime(true); - $extensions = !isset($this->tasks['*']) - ? array_map(static fn ($e) => "*.{$e}", array_keys($this->tasks)) + $extensions = !$this->tasks->has('*') + ? array_map(static fn ($e) => "*.{$e}", $this->tasks->keys()) : null; $exclude = array_map(Glob::toRegex(...), (array) $exclude); $root = new Directory($path, true); diff --git a/packages/documentator/src/Processor/ProcessorTest.php b/packages/documentator/src/Processor/ProcessorTest.php index 0a99befe..2c112d2b 100644 --- a/packages/documentator/src/Processor/ProcessorTest.php +++ b/packages/documentator/src/Processor/ProcessorTest.php @@ -3,6 +3,7 @@ namespace LastDragon_ru\LaraASP\Documentator\Processor; use Generator; +use LastDragon_ru\LaraASP\Core\Application\ContainerResolver; use LastDragon_ru\LaraASP\Core\Utils\Path; use LastDragon_ru\LaraASP\Documentator\Processor\Contracts\Dependency; use LastDragon_ru\LaraASP\Documentator\Processor\Contracts\Task; @@ -41,7 +42,7 @@ public function testRun(): void { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['htm']; } @@ -65,7 +66,7 @@ public function __invoke(Directory $root, File $file): bool { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['txt', 'md']; } @@ -117,7 +118,7 @@ static function (mixed $file) use ($root): mixed { $count = 0; $events = []; - (new Processor()) + (new Processor($this->app()->make(ContainerResolver::class))) ->task($mock) ->task($taskA) ->task($taskB) @@ -212,7 +213,7 @@ public function testRunPostpone(): void { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['txt', 'htm', 'html']; } @@ -241,7 +242,7 @@ public function __invoke(Directory $root, File $file): ?bool { $count = 0; $events = []; - (new Processor()) + (new Processor($this->app()->make(ContainerResolver::class))) ->task($task) ->run( $root, @@ -306,7 +307,7 @@ public function testRunWildcard(): void { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['html']; } @@ -344,7 +345,7 @@ public function __invoke(Directory $root, File $file): Generator { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['*']; } @@ -360,7 +361,7 @@ public function __invoke(Directory $root, File $file): bool { $count = 0; $events = []; - (new Processor()) + (new Processor($this->app()->make(ContainerResolver::class))) ->task($taskA) ->task($taskB) ->run( @@ -423,7 +424,7 @@ public function testRunFileNotFound(): void { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['txt']; } @@ -443,7 +444,7 @@ public function __invoke(Directory $root, File $file): Generator { self::expectException(DependencyNotFound::class); self::expectExceptionMessage("Dependency `404.html` of `a/a.txt` not found (root: `{$root}`)."); - (new Processor()) + (new Processor($this->app()->make(ContainerResolver::class))) ->task($task) ->run($root); } @@ -454,7 +455,7 @@ public function testRunCircularDependency(): void { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['txt']; } @@ -492,7 +493,7 @@ public function __invoke(Directory $root, File $file): Generator { MESSAGE, ); - (new Processor()) + (new Processor($this->app()->make(ContainerResolver::class))) ->task($task) ->run($root); } @@ -503,7 +504,7 @@ public function testRunCircularDependencySelf(): void { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['txt']; } @@ -535,7 +536,7 @@ public function __invoke(Directory $root, File $file): Generator { MESSAGE, ); - (new Processor()) + (new Processor($this->app()->make(ContainerResolver::class))) ->task($task) ->run($root); } diff --git a/packages/documentator/src/Processor/Tasks/Preprocess/Task.php b/packages/documentator/src/Processor/Tasks/Preprocess/Task.php index 3913f896..7d277c9b 100644 --- a/packages/documentator/src/Processor/Tasks/Preprocess/Task.php +++ b/packages/documentator/src/Processor/Tasks/Preprocess/Task.php @@ -132,7 +132,7 @@ public function addInstruction(Instruction|string $instruction): static { * @inheritDoc */ #[Override] - public function getExtensions(): array { + public static function getExtensions(): array { return ['md']; } From da939d19f6bca7194e881c53153556a49ee20030 Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sat, 17 Aug 2024 09:30:45 +0400 Subject: [PATCH 2/3] `ResolvedInstruction` replaced by `InstanceList`. --- .../src/Processor/InstanceList.php | 19 +++++++ .../Tasks/Preprocess/ResolvedInstruction.php | 56 ------------------- .../src/Processor/Tasks/Preprocess/Task.php | 33 ++++++----- .../Processor/Tasks/Preprocess/TaskTest.php | 23 +++----- 4 files changed, 48 insertions(+), 83 deletions(-) delete mode 100644 packages/documentator/src/Processor/Tasks/Preprocess/ResolvedInstruction.php diff --git a/packages/documentator/src/Processor/InstanceList.php b/packages/documentator/src/Processor/InstanceList.php index ee753faf..0e0b89fe 100644 --- a/packages/documentator/src/Processor/InstanceList.php +++ b/packages/documentator/src/Processor/InstanceList.php @@ -6,6 +6,8 @@ use LastDragon_ru\LaraASP\Core\Application\ContainerResolver; use function array_keys; +use function array_merge; +use function array_unique; use function array_values; use function is_object; use function is_string; @@ -36,6 +38,10 @@ public function __construct( // empty } + public function isEmpty(): bool { + return !$this->map; + } + /** * @return list */ @@ -43,6 +49,19 @@ public function keys(): array { return array_keys($this->map); } + /** + * @return list> + */ + public function classes(): array { + $classes = []; + + foreach ($this->map as $list) { + $classes = array_merge($classes, $list); + } + + return array_values(array_unique($classes)); + } + public function has(string ...$key): bool { $exists = false; diff --git a/packages/documentator/src/Processor/Tasks/Preprocess/ResolvedInstruction.php b/packages/documentator/src/Processor/Tasks/Preprocess/ResolvedInstruction.php deleted file mode 100644 index 5bdce5cb..00000000 --- a/packages/documentator/src/Processor/Tasks/Preprocess/ResolvedInstruction.php +++ /dev/null @@ -1,56 +0,0 @@ -> - */ - private readonly string $class; - /** - * @var Instruction|null - */ - private ?Instruction $instance = null; - - /** - * @param Instruction|class-string> $instruction - */ - public function __construct( - protected readonly ContainerResolver $container, - Instruction|string $instruction, - ) { - if (is_object($instruction)) { - $this->class = $instruction::class; - $this->instance = $instruction; - } else { - $this->class = $instruction; - } - } - - /** - * @return Instruction - */ - public function getInstance(): Instruction { - $this->instance ??= $this->container->getInstance()->make($this->class); - - return $this->instance; - } - - /** - * @return class-string> - */ - public function getClass(): string { - return $this->class; - } -} diff --git a/packages/documentator/src/Processor/Tasks/Preprocess/Task.php b/packages/documentator/src/Processor/Tasks/Preprocess/Task.php index 7d277c9b..4a21103a 100644 --- a/packages/documentator/src/Processor/Tasks/Preprocess/Task.php +++ b/packages/documentator/src/Processor/Tasks/Preprocess/Task.php @@ -10,6 +10,7 @@ use LastDragon_ru\LaraASP\Documentator\Processor\Exceptions\ProcessorError; use LastDragon_ru\LaraASP\Documentator\Processor\FileSystem\Directory; use LastDragon_ru\LaraASP\Documentator\Processor\FileSystem\File; +use LastDragon_ru\LaraASP\Documentator\Processor\InstanceList; use LastDragon_ru\LaraASP\Documentator\Processor\Tasks\Preprocess\Contracts\Instruction; use LastDragon_ru\LaraASP\Documentator\Processor\Tasks\Preprocess\Contracts\Parameters; use LastDragon_ru\LaraASP\Documentator\Processor\Tasks\Preprocess\Exceptions\PreprocessFailed; @@ -25,8 +26,6 @@ use LastDragon_ru\LaraASP\Serializer\Contracts\Serializer; use Override; -use function array_map; -use function array_values; use function hash; use function is_array; use function json_decode; @@ -90,14 +89,16 @@ class Task implements TaskContract { REGEXP; /** - * @var array> + * @var InstanceList> */ - private array $instructions = []; + private InstanceList $instructions; public function __construct( - protected readonly ContainerResolver $container, + ContainerResolver $container, protected readonly Serializer $serializer, ) { + $this->instructions = new InstanceList($container, $this->key(...)); + $this->addInstruction(IncludeFile::class); $this->addInstruction(IncludeExec::class); $this->addInstruction(IncludeExample::class); @@ -109,21 +110,27 @@ public function __construct( $this->addInstruction(IncludeGraphqlDirective::class); } + /** + * @param Instruction|class-string> $task + */ + private function key(Instruction|string $task): string { + return $task::getName(); + } + /** * @return list>> */ public function getInstructions(): array { - return array_values(array_map(static fn ($i) => $i->getClass(), $this->instructions)); + return $this->instructions->classes(); } /** - * @template I of Instruction + * @template I of Instruction<*> * * @param I|class-string $instruction */ public function addInstruction(Instruction|string $instruction): static { - // @phpstan-ignore-next-line argument.type (Assigment is fine...) - $this->instructions[$instruction::getName()] = new ResolvedInstruction($this->container, $instruction); + $this->instructions->add($instruction); // @phpstan-ignore argument.type return $this; } @@ -206,7 +213,7 @@ protected function parse(Directory $root, File $file): TokenList { $tokens = []; $matches = []; - if (!$this->instructions) { + if ($this->instructions->isEmpty()) { return new TokenList($tokens); } @@ -218,7 +225,7 @@ protected function parse(Directory $root, File $file): TokenList { foreach ($matches as $match) { // Instruction? $name = (string) $match['instruction']; - $instruction = $this->instructions[$name] ?? null; + $instruction = $this->instructions->get($name)[0] ?? null; if (!$instruction) { continue; @@ -241,11 +248,11 @@ protected function parse(Directory $root, File $file): TokenList { // Parse $context = new Context($root, $file, (string) $match['target'], $match['parameters']); - $parameters = $instruction->getClass()::getParameters(); + $parameters = $instruction::getParameters(); $parameters = $this->serializer->deserialize($parameters, $params, 'json'); $tokens[$hash] = new Token( - $instruction->getInstance(), + $instruction, $context, $target, $parameters, diff --git a/packages/documentator/src/Processor/Tasks/Preprocess/TaskTest.php b/packages/documentator/src/Processor/Tasks/Preprocess/TaskTest.php index 0a666579..2444f9fe 100644 --- a/packages/documentator/src/Processor/Tasks/Preprocess/TaskTest.php +++ b/packages/documentator/src/Processor/Tasks/Preprocess/TaskTest.php @@ -11,7 +11,6 @@ use LastDragon_ru\LaraASP\Documentator\Testing\Package\TestCase; use LastDragon_ru\LaraASP\Serializer\Contracts\Serializable; use LastDragon_ru\LaraASP\Serializer\Contracts\Serializer; -use LastDragon_ru\LaraASP\Testing\Mockery\MockProperties; use Mockery; use Override; use PHPUnit\Framework\Attributes\CoversClass; @@ -58,19 +57,15 @@ final class TaskTest extends TestCase { public function testParse(): void { $a = new TaskTest__EmptyInstruction(); $b = new TaskTest__TestInstruction(); - $task = Mockery::mock(Task::class, MockProperties::class); - $task->shouldAllowMockingProtectedMethods(); - $task->makePartial(); - $task - ->shouldUseProperty('container') - ->value( - $this->app()->make(ContainerResolver::class), - ); - $task - ->shouldUseProperty('serializer') - ->value( - $this->app()->make(Serializer::class), - ); + $task = new class( + $this->app()->make(ContainerResolver::class), + $this->app()->make(Serializer::class), + ) extends Task { + #[Override] + public function parse(Directory $root, File $file): TokenList { + return parent::parse($root, $file); + } + }; $task->addInstruction($a::class); $task->addInstruction($b); From 50f8cfebe839bbdf00ab78b172212835a2bf1e8b Mon Sep 17 00:00:00 2001 From: Aleksei Lebedev <1329824+LastDragon-ru@users.noreply.github.com> Date: Sat, 17 Aug 2024 11:02:18 +0400 Subject: [PATCH 3/3] `FileSystem` will use weak cache for `File`/`Directory`. --- .../src/Processor/FileSystem/FileSystem.php | 30 ++++++++--- .../Processor/FileSystem/FileSystemTest.php | 50 ++++++++++++++----- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/packages/documentator/src/Processor/FileSystem/FileSystem.php b/packages/documentator/src/Processor/FileSystem/FileSystem.php index 385d7b88..94cb5197 100644 --- a/packages/documentator/src/Processor/FileSystem/FileSystem.php +++ b/packages/documentator/src/Processor/FileSystem/FileSystem.php @@ -6,6 +6,7 @@ use Iterator; use SplFileInfo; use Symfony\Component\Finder\Finder; +use WeakReference; use function dirname; use function file_put_contents; @@ -13,6 +14,11 @@ use function is_file; class FileSystem { + /** + * @var array> + */ + private array $cache = []; + public function __construct() { // empty } @@ -35,8 +41,13 @@ public function getFile(Directory $root, SplFileInfo|File|string $path): ?File { } // Create - $writable = $root->isWritable() && $root->isInside($path); - $file = new File($path, $writable); + $file = ($this->cache[$path] ?? null)?->get(); + + if (!($file instanceof File)) { + $writable = $root->isWritable() && $root->isInside($path); + $file = new File($path, $writable); + $this->cache[$path] = WeakReference::create($file); + } return $file; } @@ -66,12 +77,17 @@ public function getDirectory(Directory $root, SplFileInfo|Directory|File|string } // Create - $writable = $root->isWritable() && $root->isInside($path); - $dir = $root->getPath() !== $path - ? new Directory($path, $writable) - : $root; + $directory = ($this->cache[$path] ?? null)?->get(); + + if (!($directory instanceof Directory)) { + $writable = $root->isWritable() && $root->isInside($path); + $directory = $root->getPath() !== $path + ? new Directory($path, $writable) + : $root; + $this->cache[$path] = WeakReference::create($directory); + } - return $dir; + return $directory; } /** diff --git a/packages/documentator/src/Processor/FileSystem/FileSystemTest.php b/packages/documentator/src/Processor/FileSystem/FileSystemTest.php index 3403c014..e940b1e9 100644 --- a/packages/documentator/src/Processor/FileSystem/FileSystemTest.php +++ b/packages/documentator/src/Processor/FileSystem/FileSystemTest.php @@ -24,9 +24,9 @@ public function testGetFile(): void { $relative = $fs->getFile($directory, basename(__FILE__)); $notfound = $fs->getFile($directory, 'not found'); $writable = new Directory(Path::normalize(__DIR__), true); - $internal = $fs->getFile($writable, basename(__FILE__)); + $internal = $fs->getFile($writable, self::getTestData()->path('c.html')); $external = $fs->getFile($writable, '../Processor.php'); - $file = new File(Path::normalize(__FILE__), false); + $file = new File(Path::normalize(self::getTestData()->path('c.txt')), false); $fromFile = $fs->getFile($writable, $file); $splFile = new SplFileInfo($file->getPath()); $fromSplFile = $fs->getFile($writable, $splFile); @@ -43,7 +43,10 @@ public function testGetFile(): void { self::assertNotNull($internal); self::assertTrue($internal->isWritable()); - self::assertEquals(Path::normalize(__FILE__), $internal->getPath()); + self::assertEquals( + Path::normalize(self::getTestData()->path('c.html')), + $internal->getPath(), + ); self::assertNotNull($external); self::assertFalse($external->isWritable()); @@ -53,13 +56,19 @@ public function testGetFile(): void { self::assertFalse($file->isWritable()); self::assertTrue($fromFile->isWritable()); self::assertEquals($file->getPath(), $fromFile->getPath()); - self::assertEquals(Path::normalize(__FILE__), $fromFile->getPath()); + self::assertEquals( + Path::normalize(self::getTestData()->path('c.txt')), + $fromFile->getPath(), + ); self::assertNotNull($fromSplFile); self::assertFalse($file->isWritable()); self::assertTrue($fromSplFile->isWritable()); self::assertEquals($file->getPath(), $fromSplFile->getPath()); - self::assertEquals(Path::normalize(__FILE__), $fromSplFile->getPath()); + self::assertEquals( + Path::normalize(self::getTestData()->path('c.txt')), + $fromSplFile->getPath(), + ); } public function testGetDirectory(): void { @@ -93,11 +102,12 @@ public function testGetDirectory(): void { self::assertNull($notDirectory); // Internal - $internal = $fs->getDirectory($writable, basename(__DIR__)); + $internalPath = self::getTestData()->path('a'); + $internal = $fs->getDirectory($writable, $internalPath); self::assertNotNull($internal); self::assertTrue($internal->isWritable()); - self::assertEquals(Path::normalize(__DIR__), $internal->getPath()); + self::assertEquals($internalPath, $internal->getPath()); // External $external = $fs->getDirectory($writable, '../Testing'); @@ -107,14 +117,18 @@ public function testGetDirectory(): void { self::assertEquals(Path::getPath(__DIR__, '../../Testing'), $external->getPath()); // From file - $fromFile = $fs->getDirectory($writable, new File(Path::normalize(__FILE__), true)); + $fromFilePath = Path::normalize(self::getTestData()->path('c.html')); + $fromFile = $fs->getDirectory($writable, new File($fromFilePath, true)); self::assertNotNull($fromFile); self::assertTrue($fromFile->isWritable()); - self::assertEquals(Path::normalize(__DIR__), $fromFile->getPath()); + self::assertEquals( + Path::normalize(self::getTestData()->path('')), + $fromFile->getPath(), + ); // From SplFileInfo - $spl = new SplFileInfo(__DIR__); + $spl = new SplFileInfo(self::getTestData()->path('b')); $fromSpl = $fs->getDirectory($writable, $spl); self::assertNotNull($fromSpl); @@ -122,7 +136,7 @@ public function testGetDirectory(): void { self::assertEquals(Path::normalize($spl->getPathname()), $fromSpl->getPath()); // From Directory - $directory = new Directory(Path::normalize(__DIR__), false); + $directory = new Directory(Path::normalize(self::getTestData()->path('a/a')), false); $fromDirectory = $fs->getDirectory($writable, $directory); self::assertNotNull($fromDirectory); @@ -238,7 +252,6 @@ public function testGetDirectoriesIterator(): void { ); } - public function testSave(): void { $fs = new FileSystem(); $temp = Path::normalize(self::getTempFile(__FILE__)->getPathname()); @@ -266,4 +279,17 @@ public function testSaveReadonly(): void { self::assertEquals(__FILE__, file_get_contents($temp)); } + + public function testCache(): void { + $fs = new FileSystem(); + $dir = new Directory(Path::normalize(__DIR__), false); + $file = $fs->getFile($dir, __FILE__); + $directory = $fs->getDirectory($dir, __DIR__); + + self::assertNotNull($file); + self::assertSame($file, $fs->getFile($dir, __FILE__)); + + self::assertNotNull($directory); + self::assertSame($directory, $fs->getDirectory($dir, __DIR__)); + } }