diff --git a/.github/workflows/php-workshop.yml b/.github/workflows/php-workshop.yml index 37040d83..7568a445 100644 --- a/.github/workflows/php-workshop.yml +++ b/.github/workflows/php-workshop.yml @@ -24,6 +24,7 @@ jobs: with: php-version: ${{ matrix.php }} tools: composer:v2 + ini-values: opcache.enable=0 - name: Install Dependencies run: composer update --prefer-dist diff --git a/app/config.php b/app/config.php index 7b64b923..d67ab76a 100644 --- a/app/config.php +++ b/app/config.php @@ -384,28 +384,19 @@ function (CgiResult $result) use ($c) { ], ], 'code-patcher' => [ - 'cli.verify.start' => [ - containerListener(CodePatchListener::class, 'patch'), - ], - 'cli.verify.finish' => [ + 'application.tear-down' => [ containerListener(CodePatchListener::class, 'revert'), ], - 'cli.run.start' => [ + 'verify.pre.execute' => [ containerListener(CodePatchListener::class, 'patch'), ], - 'cli.run.finish' => [ + 'verify.post.execute' => [ containerListener(CodePatchListener::class, 'revert'), ], - 'cgi.verify.start' => [ - containerListener(CodePatchListener::class, 'patch'), - ], - 'cgi.verify.finish' => [ - containerListener(CodePatchListener::class, 'revert'), - ], - 'cgi.run.start' => [ + 'run.start' => [ containerListener(CodePatchListener::class, 'patch'), ], - 'cgi.run.finish' => [ + 'run.finish' => [ containerListener(CodePatchListener::class, 'revert'), ], ], diff --git a/src/Application.php b/src/Application.php index 3aec2936..2a4cff7c 100644 --- a/src/Application.php +++ b/src/Application.php @@ -7,11 +7,14 @@ use DI\Container; use DI\ContainerBuilder; use PhpSchool\PhpWorkshop\Check\CheckRepository; +use PhpSchool\PhpWorkshop\Event\Event; +use PhpSchool\PhpWorkshop\Event\EventDispatcher; use PhpSchool\PhpWorkshop\Exception\InvalidArgumentException; use PhpSchool\PhpWorkshop\Exception\MissingArgumentException; use PhpSchool\PhpWorkshop\Factory\ResultRendererFactory; use PhpSchool\PhpWorkshop\Output\OutputInterface; use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; use RuntimeException; use function class_exists; @@ -192,6 +195,12 @@ public function configure(): ContainerInterface } } + set_error_handler(function () use ($container): bool { + $this->tearDown($container); + + return false; // Use default error handler + }); + return $container; } @@ -227,6 +236,8 @@ public function run(): int $message = str_replace($basePath, '', $message); } + $this->tearDown($container); + $container ->get(OutputInterface::class) ->printError( @@ -268,4 +279,15 @@ private function getContainer(): Container return $containerBuilder->build(); } + + private function tearDown(ContainerInterface $container): void + { + try { + $container + ->get(EventDispatcher::class) + ->dispatch(new Event('application.tear-down')); + } catch (\Throwable $t) { + $container->get(LoggerInterface::class)->error($t->getMessage(), ['exception' => $t]); + } + } } diff --git a/src/Exercise/AbstractExercise.php b/src/Exercise/AbstractExercise.php index b94d204d..1a5355e6 100644 --- a/src/Exercise/AbstractExercise.php +++ b/src/Exercise/AbstractExercise.php @@ -15,6 +15,10 @@ */ abstract class AbstractExercise { + /** + * @var SolutionInterface|null + */ + protected $solution; /** * Get the name of the exercise, like `Hello World!`. @@ -35,15 +39,19 @@ abstract public function getName(): string; */ public function getSolution(): SolutionInterface { - return SingleFileSolution::fromFile( - (string) realpath( - sprintf( - '%s/../../exercises/%s/solution/solution.php', - dirname((string) (new ReflectionClass(static::class))->getFileName()), - self::normaliseName($this->getName()) + if (null === $this->solution) { + $this->solution = SingleFileSolution::fromFile( + (string)realpath( + sprintf( + '%s/../../exercises/%s/solution/solution.php', + dirname((string)(new ReflectionClass(static::class))->getFileName()), + self::normaliseName($this->getName()) + ) ) - ) - ); + ); + } + + return $this->solution; } /** diff --git a/src/Listener/CodePatchListener.php b/src/Listener/CodePatchListener.php index a1266167..53ed0845 100644 --- a/src/Listener/CodePatchListener.php +++ b/src/Listener/CodePatchListener.php @@ -6,10 +6,12 @@ use PhpSchool\PhpWorkshop\CodePatcher; use PhpSchool\PhpWorkshop\Event\ExerciseRunnerEvent; +use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution; +use PhpSchool\PhpWorkshop\Solution\SolutionFile; use RuntimeException; /** - * Listener which patches student's solutions + * Listener which patches internal and student's solutions */ class CodePatchListener { @@ -19,9 +21,9 @@ class CodePatchListener private $codePatcher; /** - * @var string + * @var array */ - private $originalCode; + private $originalCode = []; /** * @param CodePatcher $codePatcher @@ -36,34 +38,36 @@ public function __construct(CodePatcher $codePatcher) */ public function patch(ExerciseRunnerEvent $event): void { - $fileName = $event->getInput()->getArgument('program'); + $files = [ + $event->getInput()->getArgument('program'), + ]; - if (null === $fileName) { - return; + $exercise = $event->getExercise(); + if ($exercise instanceof ProvidesSolution) { + $files[] = $exercise->getSolution()->getEntryPoint(); } - $this->originalCode = (string) file_get_contents($fileName); - file_put_contents( - $fileName, - $this->codePatcher->patch($event->getExercise(), $this->originalCode) - ); + foreach (array_filter($files) as $fileName) { + $this->originalCode[$fileName] = (string) file_get_contents($fileName); + + file_put_contents( + $fileName, + $this->codePatcher->patch($event->getExercise(), $this->originalCode[$fileName]) + ); + } } /** - * @param ExerciseRunnerEvent $event + * @param \PhpSchool\PhpWorkshop\Event\EventInterface $event */ - public function revert(ExerciseRunnerEvent $event): void + public function revert(\PhpSchool\PhpWorkshop\Event\EventInterface $event): void { - if (null === $this->originalCode) { - throw new RuntimeException('Can only revert previously patched code'); - } - - $fileName = $event->getInput()->getArgument('program'); - - if (null === $fileName) { + if (null === $this->originalCode || empty($this->originalCode)) { return; } - file_put_contents($fileName, $this->originalCode); + foreach ($this->originalCode as $fileName => $contents) { + file_put_contents($fileName, $contents); + } } } diff --git a/src/Solution/DirectorySolution.php b/src/Solution/DirectorySolution.php index b67b55fe..0c2242f2 100644 --- a/src/Solution/DirectorySolution.php +++ b/src/Solution/DirectorySolution.php @@ -40,7 +40,7 @@ class DirectorySolution implements SolutionInterface * @param array $exclusions An array of file names to exclude from the folder. * @throws InvalidArgumentException If the entry point does not exist in the folder. */ - public function __construct(string $directory, string $entryPoint, array $exclusions = []) + private function __construct(string $directory, string $entryPoint, array $exclusions = []) { $directory = (string) realpath(rtrim($directory, '/')); $entryPoint = ltrim($entryPoint, '/'); @@ -81,15 +81,20 @@ public function __construct(string $directory, string $entryPoint, array $exclus * @param string $directory The directory to search for files. * @param array $exclusions An array of file names to exclude from the folder. * @param string $entryPoint The relative path from the directory of the entry point file. - * @return self + * @return SolutionInterface */ - public static function fromDirectory(string $directory, array $exclusions = [], $entryPoint = 'solution.php'): self - { - return new self($directory, $entryPoint, array_merge($exclusions, ['composer.lock', 'vendor'])); + public static function fromDirectory( + string $directory, + array $exclusions = [], + $entryPoint = 'solution.php' + ): SolutionInterface { + return InTempSolution::fromSolution( + new self($directory, $entryPoint, array_merge($exclusions, ['composer.lock', 'vendor'])) + ); } /** - * Get the entry point. This is the PHP file that PHO would execute in order to run the + * Get the entry point. This is the PHP file that PHP would execute in order to run the * program. This should be the absolute path. * * @return string diff --git a/src/Solution/InTempSolution.php b/src/Solution/InTempSolution.php new file mode 100644 index 00000000..1bbc4667 --- /dev/null +++ b/src/Solution/InTempSolution.php @@ -0,0 +1,119 @@ +getBaseDirectory())); + $entryPointPath = explode('/', System::realpath($solution->getEntryPoint())); + + $intersection = array_intersect($currentPath, $solutionPath); + + if (count($intersection) <= 1) { + $intersection = explode('/', $tempDir); + } + + $basename = implode('/', array_diff($solutionPath, $intersection)); + $entrypoint = implode('/', array_diff($entryPointPath, $intersection)); + + $this->baseDirectory = sprintf('%s/php-school/%s', $tempDir, $basename); + $this->entryPoint = sprintf('%s/php-school/%s', $tempDir, $entrypoint); + + if ($fileSystem->exists($this->baseDirectory)) { + $fileSystem->remove($this->baseDirectory); + } + + $fileSystem->mkdir($this->baseDirectory); + + $dirIterator = new \RecursiveDirectoryIterator( + $solution->getBaseDirectory(), + \RecursiveDirectoryIterator::SKIP_DOTS + ); + $iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::SELF_FIRST); + + foreach ($iterator as $file) { + $target = sprintf('%s/%s', $this->baseDirectory, $iterator->getSubPathName()); + $file->isDir() + ? $fileSystem->mkdir($target) + : $fileSystem->copy($file->getPathname(), $target); + } + + $this->files = array_map(function (SolutionFile $solutionFile) use ($intersection, $tempDir) { + $filePath = explode('/', System::realpath($solutionFile->__toString())); + $file = implode('/', array_diff($filePath, $intersection)); + return SolutionFile::fromFile(sprintf('%s/php-school/%s', $tempDir, $file)); + }, $solution->getFiles()); + } + + public static function fromSolution(SolutionInterface $solution): SolutionInterface + { + return new self($solution); + } + + /** + * Get the entry point. This is the PHP file that PHP would execute in order to run the + * program. This should be the absolute path. + * + * @return string + */ + public function getEntryPoint(): string + { + return $this->entryPoint; + } + + /** + * Get all the files which are contained with the solution. + * + * @return array + */ + public function getFiles(): array + { + return $this->files; + } + + /** + * Get the absolute path to the directory containing the solution. + * + * @return string + */ + public function getBaseDirectory(): string + { + return $this->baseDirectory; + } + + /** + * Check whether there is a `composer.lock` file in the base directory. + * + * @return bool + */ + public function hasComposerFile(): bool + { + return file_exists(sprintf('%s/composer.lock', $this->baseDirectory)); + } +} diff --git a/src/Solution/SingleFileSolution.php b/src/Solution/SingleFileSolution.php index b32aac67..1804d89a 100644 --- a/src/Solution/SingleFileSolution.php +++ b/src/Solution/SingleFileSolution.php @@ -20,7 +20,7 @@ class SingleFileSolution implements SolutionInterface * @param string $file The absolute path of the reference solution. * @throws InvalidArgumentException If the file does not exist. */ - public function __construct(string $file) + private function __construct(string $file) { if (!file_exists($file)) { throw new InvalidArgumentException(sprintf('File: "%s" does not exist', $file)); @@ -33,12 +33,12 @@ public function __construct(string $file) * Static constructor to build an instance from an absolute file path. * * @param string $file The absolute path of the reference solution. - * @return self + * @return SolutionInterface * @throws InvalidArgumentException If the file does not exist. */ - public static function fromFile(string $file): self + public static function fromFile(string $file): SolutionInterface { - return new self($file); + return InTempSolution::fromSolution(new self($file)); } /** diff --git a/src/TestUtils/SolutionPathTransformer.php b/src/TestUtils/SolutionPathTransformer.php new file mode 100644 index 00000000..5000f582 --- /dev/null +++ b/src/TestUtils/SolutionPathTransformer.php @@ -0,0 +1,21 @@ +getSolution(); $this->assertInstanceOf(SolutionInterface::class, $solution); $files = $solution->getFiles(); $this->assertCount(1, $files); $this->assertInstanceOf(SolutionFile::class, $files[0]); - $this->assertSame(realpath($path), $files[0]->__toString()); + $this->assertSame('FILE CONTENTS', file_get_contents($files[0]->__toString())); unlink($path); rmdir(__DIR__ . '/../../exercises/array-we-go/solution'); rmdir(__DIR__ . '/../../exercises/array-we-go'); diff --git a/test/Listener/CodePatchListenerTest.php b/test/Listener/CodePatchListenerTest.php index d686f41c..6474df1e 100644 --- a/test/Listener/CodePatchListenerTest.php +++ b/test/Listener/CodePatchListenerTest.php @@ -38,19 +38,6 @@ public function setUp(): void touch($this->file); } - public function testRevertThrowsExceptionIfPatchNotPreviouslyCalled(): void - { - $input = new Input('app', ['program' => $this->file]); - $exercise = $this->createMock(ExerciseInterface::class); - - $listener = new CodePatchListener($this->codePatcher); - $event = new ExerciseRunnerEvent('event', $exercise, $input); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Can only revert previously patched code'); - $listener->revert($event); - } - public function testPatchUpdatesCode(): void { file_put_contents($this->file, 'ORIGINAL CONTENT'); diff --git a/test/ResultRenderer/ResultsRendererTest.php b/test/ResultRenderer/ResultsRendererTest.php index f853fbaf..9bded6dc 100644 --- a/test/ResultRenderer/ResultsRendererTest.php +++ b/test/ResultRenderer/ResultsRendererTest.php @@ -119,11 +119,11 @@ public function testRenderSuccessWithSolution(): void $exerciseRepo->method('count')->willReturn(2); $tmpFile = sprintf('%s/%s/some-file', sys_get_temp_dir(), $this->getName()); - mkdir(dirname($tmpFile)); + @mkdir(dirname($tmpFile)); file_put_contents($tmpFile, 'FILE CONTENTS'); $exercise = new CliExerciseImpl(); - $exercise->setSolution(new SingleFileSolution($tmpFile)); + $exercise->setSolution(SingleFileSolution::fromFile($tmpFile)); $renderer = new ResultsRenderer( 'app', @@ -165,11 +165,11 @@ public function testRenderSuccessWithPhpSolutionFileIsSyntaxHighlighted(): void $exerciseRepo->method('count')->willReturn(2); $tmpFile = sprintf('%s/%s/some-file.php', sys_get_temp_dir(), $this->getName()); - mkdir(dirname($tmpFile)); + @mkdir(dirname($tmpFile)); file_put_contents($tmpFile, 'FILE CONTENTS'); $exercise = new CliExerciseImpl(); - $exercise->setSolution(new SingleFileSolution($tmpFile)); + $exercise->setSolution(SingleFileSolution::fromFile($tmpFile)); $syntaxHighlighter = $this->createMock(KeyLighter::class); $php = new Php(); diff --git a/test/Solution/DirectorySolutionTest.php b/test/Solution/DirectorySolutionTest.php index 37b18356..1e5f4b9a 100644 --- a/test/Solution/DirectorySolutionTest.php +++ b/test/Solution/DirectorySolutionTest.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use PhpSchool\PhpWorkshop\Solution\DirectorySolution; +use PhpSchool\PhpWorkshop\TestUtils\SolutionPathTransformer; use PHPUnit\Framework\TestCase; class DirectorySolutionTest extends TestCase @@ -32,18 +33,23 @@ public function testWithDefaultEntryPoint(): void $solution = DirectorySolution::fromDirectory($tempPath); - $this->assertSame($tempPath, $solution->getBaseDirectory()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + + $this->assertSame($expectedBaseDir, $solution->getBaseDirectory()); $this->assertFalse($solution->hasComposerFile()); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $solution->getEntryPoint()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $solution->getEntryPoint()); $files = $solution->getFiles(); $this->assertCount(2, $files); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $files[0]->__toString()); - $this->assertSame(sprintf('%s/some-class.php', $tempPath), $files[1]->__toString()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $files[0]->__toString()); + $this->assertSame(sprintf('%s/some-class.php', $expectedBaseDir), $files[1]->__toString()); + unlink(sprintf('%s/solution.php', $expectedBaseDir)); + unlink(sprintf('%s/some-class.php', $expectedBaseDir)); unlink(sprintf('%s/solution.php', $tempPath)); unlink(sprintf('%s/some-class.php', $tempPath)); rmdir($tempPath); + rmdir($expectedBaseDir); } public function testWithManualEntryPoint(): void @@ -55,18 +61,23 @@ public function testWithManualEntryPoint(): void $solution = DirectorySolution::fromDirectory($tempPath, [], 'index.php'); - $this->assertSame($tempPath, $solution->getBaseDirectory()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + + $this->assertSame($expectedBaseDir, $solution->getBaseDirectory()); $this->assertFalse($solution->hasComposerFile()); - $this->assertSame(sprintf('%s/index.php', $tempPath), $solution->getEntryPoint()); + $this->assertSame(sprintf('%s/index.php', $expectedBaseDir), $solution->getEntryPoint()); $files = $solution->getFiles(); $this->assertCount(2, $files); - $this->assertSame(sprintf('%s/index.php', $tempPath), $files[0]->__toString()); - $this->assertSame(sprintf('%s/some-class.php', $tempPath), $files[1]->__toString()); + $this->assertSame(sprintf('%s/index.php', $expectedBaseDir), $files[0]->__toString()); + $this->assertSame(sprintf('%s/some-class.php', $expectedBaseDir), $files[1]->__toString()); + unlink(sprintf('%s/index.php', $expectedBaseDir)); + unlink(sprintf('%s/some-class.php', $expectedBaseDir)); unlink(sprintf('%s/index.php', $tempPath)); unlink(sprintf('%s/some-class.php', $tempPath)); rmdir($tempPath); + rmdir($expectedBaseDir); } public function testHasComposerFileReturnsTrueIfPresent(): void @@ -79,15 +90,20 @@ public function testHasComposerFileReturnsTrueIfPresent(): void $solution = DirectorySolution::fromDirectory($tempPath); - $this->assertSame($tempPath, $solution->getBaseDirectory()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + + $this->assertSame($expectedBaseDir, $solution->getBaseDirectory()); $this->assertTrue($solution->hasComposerFile()); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $solution->getEntryPoint()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $solution->getEntryPoint()); $files = $solution->getFiles(); $this->assertCount(2, $files); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $files[0]->__toString()); - $this->assertSame(sprintf('%s/some-class.php', $tempPath), $files[1]->__toString()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $files[0]->__toString()); + $this->assertSame(sprintf('%s/some-class.php', $expectedBaseDir), $files[1]->__toString()); + unlink(sprintf('%s/composer.lock', $expectedBaseDir)); + unlink(sprintf('%s/solution.php', $expectedBaseDir)); + unlink(sprintf('%s/some-class.php', $expectedBaseDir)); unlink(sprintf('%s/composer.lock', $tempPath)); unlink(sprintf('%s/solution.php', $tempPath)); unlink(sprintf('%s/some-class.php', $tempPath)); @@ -105,17 +121,23 @@ public function testWithExceptions(): void $solution = DirectorySolution::fromDirectory($tempPath, $exclusions); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $solution->getEntryPoint()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $solution->getEntryPoint()); $files = $solution->getFiles(); $this->assertCount(2, $files); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $files[0]->__toString()); - $this->assertSame(sprintf('%s/some-class.php', $tempPath), $files[1]->__toString()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $files[0]->__toString()); + $this->assertSame(sprintf('%s/some-class.php', $expectedBaseDir), $files[1]->__toString()); + unlink(sprintf('%s/solution.php', $expectedBaseDir)); + unlink(sprintf('%s/some-class.php', $expectedBaseDir)); + unlink(sprintf('%s/exclude.txt', $expectedBaseDir)); unlink(sprintf('%s/solution.php', $tempPath)); unlink(sprintf('%s/some-class.php', $tempPath)); unlink(sprintf('%s/exclude.txt', $tempPath)); rmdir($tempPath); + rmdir($expectedBaseDir); } public function testWithNestedDirectories(): void @@ -134,15 +156,17 @@ public function testWithNestedDirectories(): void $solution = DirectorySolution::fromDirectory($tempPath); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $solution->getEntryPoint()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $solution->getEntryPoint()); $files = $solution->getFiles(); $this->assertCount(5, $files); - $this->assertSame(sprintf('%s/composer.json', $tempPath), $files[0]->__toString()); - $this->assertSame(sprintf('%s/nested/another-class.php', $tempPath), $files[1]->__toString()); - $this->assertSame(sprintf('%s/nested/deep/even-more.php', $tempPath), $files[2]->__toString()); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $files[3]->__toString()); - $this->assertSame(sprintf('%s/some-class.php', $tempPath), $files[4]->__toString()); + $this->assertSame(sprintf('%s/composer.json', $expectedBaseDir), $files[0]->__toString()); + $this->assertSame(sprintf('%s/nested/another-class.php', $expectedBaseDir), $files[1]->__toString()); + $this->assertSame(sprintf('%s/nested/deep/even-more.php', $expectedBaseDir), $files[2]->__toString()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $files[3]->__toString()); + $this->assertSame(sprintf('%s/some-class.php', $expectedBaseDir), $files[4]->__toString()); unlink(sprintf('%s/solution.php', $tempPath)); unlink(sprintf('%s/some-class.php', $tempPath)); @@ -152,6 +176,14 @@ public function testWithNestedDirectories(): void rmdir(sprintf('%s/nested/deep', $tempPath)); rmdir(sprintf('%s/nested', $tempPath)); rmdir($tempPath); + unlink(sprintf('%s/solution.php', $expectedBaseDir)); + unlink(sprintf('%s/some-class.php', $expectedBaseDir)); + unlink(sprintf('%s/composer.json', $expectedBaseDir)); + unlink(sprintf('%s/nested/another-class.php', $expectedBaseDir)); + unlink(sprintf('%s/nested/deep/even-more.php', $expectedBaseDir)); + rmdir(sprintf('%s/nested/deep', $expectedBaseDir)); + rmdir(sprintf('%s/nested', $expectedBaseDir)); + rmdir($expectedBaseDir); } public function testExceptionsWithNestedDirectories(): void @@ -175,12 +207,14 @@ public function testExceptionsWithNestedDirectories(): void $solution = DirectorySolution::fromDirectory($tempPath, $exclusions); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $solution->getEntryPoint()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $solution->getEntryPoint()); $files = $solution->getFiles(); $this->assertCount(2, $files); - $this->assertSame(sprintf('%s/solution.php', $tempPath), $files[0]->__toString()); - $this->assertSame(sprintf('%s/some-class.php', $tempPath), $files[1]->__toString()); + $this->assertSame(sprintf('%s/solution.php', $expectedBaseDir), $files[0]->__toString()); + $this->assertSame(sprintf('%s/some-class.php', $expectedBaseDir), $files[1]->__toString()); unlink(sprintf('%s/solution.php', $tempPath)); unlink(sprintf('%s/some-class.php', $tempPath)); @@ -193,5 +227,16 @@ public function testExceptionsWithNestedDirectories(): void rmdir(sprintf('%s/vendor/somelib', $tempPath)); rmdir(sprintf('%s/vendor', $tempPath)); rmdir($tempPath); + unlink(sprintf('%s/solution.php', $expectedBaseDir)); + unlink(sprintf('%s/some-class.php', $expectedBaseDir)); + unlink(sprintf('%s/exclude.txt', $expectedBaseDir)); + unlink(sprintf('%s/nested/exclude.txt', $expectedBaseDir)); + unlink(sprintf('%s/nested/deep/exclude.txt', $expectedBaseDir)); + unlink(sprintf('%s/vendor/somelib/app.php', $expectedBaseDir)); + rmdir(sprintf('%s/nested/deep', $expectedBaseDir)); + rmdir(sprintf('%s/nested', $expectedBaseDir)); + rmdir(sprintf('%s/vendor/somelib', $expectedBaseDir)); + rmdir(sprintf('%s/vendor', $expectedBaseDir)); + rmdir($expectedBaseDir); } } diff --git a/test/Solution/SingleFileSolutionTest.php b/test/Solution/SingleFileSolutionTest.php index 6fa336d7..23ea19f9 100644 --- a/test/Solution/SingleFileSolutionTest.php +++ b/test/Solution/SingleFileSolutionTest.php @@ -3,13 +3,15 @@ namespace PhpSchool\PhpWorkshopTest\Solution; use PhpSchool\PhpWorkshop\Solution\SingleFileSolution; +use PhpSchool\PhpWorkshop\TestUtils\SolutionPathTransformer; use PHPUnit\Framework\TestCase; class SingleFileSolutionTest extends TestCase { public function testGetters(): void { - $tempPath = sprintf('%s/%s', realpath(sys_get_temp_dir()), $this->getName()); + $tmpDir = realpath(sys_get_temp_dir()); + $tempPath = sprintf('%s/%s', $tmpDir, $this->getName()); $filePath = sprintf('%s/test.file', $tempPath); @mkdir($tempPath, 0775, true); @@ -17,11 +19,14 @@ public function testGetters(): void $solution = SingleFileSolution::fromFile($filePath); - $this->assertSame($filePath, $solution->getEntryPoint()); - $this->assertSame($tempPath, $solution->getBaseDirectory()); + $expectedBaseDir = SolutionPathTransformer::tempPathToSolutionTempPath($tempPath); + $expectedFilePath = SolutionPathTransformer::tempPathToSolutionTempPath($filePath); + + $this->assertSame($expectedFilePath, $solution->getEntryPoint()); + $this->assertSame($expectedBaseDir, $solution->getBaseDirectory()); $this->assertFalse($solution->hasComposerFile()); $this->assertCount(1, $solution->getFiles()); - $this->assertSame($filePath, $solution->getFiles()[0]->__toString()); + $this->assertSame($expectedFilePath, $solution->getFiles()[0]->__toString()); unlink($filePath); rmdir($tempPath); } diff --git a/test/Util/SystemTest.php b/test/Util/SystemTest.php new file mode 100644 index 00000000..392fe111 --- /dev/null +++ b/test/Util/SystemTest.php @@ -0,0 +1,29 @@ +expectException(\RuntimeException::class); + $this->expectExceptionMessage('Failed to get realpath of "non_existing_file.txt"'); + + System::realpath('non_existing_file.txt'); + } + + public function testRealpathReturnsFullPath() + { + self::assertSame(realpath(__DIR__), System::realpath(__DIR__)); + } + + public function testTempDir() + { + self::assertSame(realpath(sys_get_temp_dir()), System::tempDir()); + } +}