From adaa0d207ce7679a66a0f68d7eb56dfbbeb2a2eb Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Thu, 9 May 2024 19:47:51 +0200 Subject: [PATCH] Introduce new class to prepare environment files --- app/config.php | 4 + src/ExerciseRunner/EnvironmentManager.php | 68 +++++++++++++ src/Listener/CodePatchListener.php | 1 + .../ExerciseRunner/EnvironmentManagerTest.php | 99 +++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 src/ExerciseRunner/EnvironmentManager.php create mode 100644 test/ExerciseRunner/EnvironmentManagerTest.php diff --git a/app/config.php b/app/config.php index 39cea60d..1394abf4 100644 --- a/app/config.php +++ b/app/config.php @@ -186,6 +186,10 @@ EventDispatcher::class => factory(EventDispatcherFactory::class), EventDispatcherFactory::class => create(), + EnvironmentManager::class => function (ContainerInterface $c) { + return new EnvironmentManager($c->get(Filesystem::class), $c->get(EventDispatcher::class)); + }, + //Exercise Runners RunnerManager::class => function (ContainerInterface $c) { $manager = new RunnerManager(); diff --git a/src/ExerciseRunner/EnvironmentManager.php b/src/ExerciseRunner/EnvironmentManager.php new file mode 100644 index 00000000..c18312e2 --- /dev/null +++ b/src/ExerciseRunner/EnvironmentManager.php @@ -0,0 +1,68 @@ +copyExerciseFiles($scenario, $context->getStudentExecutionDirectory()); + + //cleanup the files when the run or verification process is finished + //we do this at late as possible in case any checks or other event listeners need to access the files + $this->eventDispatcher->listen(['run.finish', 'verify.finish'], function () use ($context, $scenario) { + foreach ($scenario->getFiles() as $fileName => $content) { + $this->filesystem->remove(Path::join($context->getStudentExecutionDirectory(), $fileName)); + } + }); + } + + public function prepareReference(ExecutionContext $context, ExerciseScenario $scenario): void + { + $exercise = $context->getExercise(); + + if (!$exercise instanceof ProvidesSolution) { + return; + } + + $this->filesystem->mkdir($context->getReferenceExecutionDirectory()); + + $solution = $exercise->getSolution(); + + foreach ($solution->getFiles() as $file) { + $this->filesystem->copy( + $file->getAbsolutePath(), + Path::join($context->getReferenceExecutionDirectory(), $file->getRelativePath()) + ); + } + + $this->copyExerciseFiles($scenario, $context->getReferenceExecutionDirectory()); + + //cleanup the files when the run or verification process is finished + //we do this at late as possible in case any checks or other event listeners need to access the files + $this->eventDispatcher->listen(['run.finish', 'verify.finish'], function () use ($context) { + $this->filesystem->remove($context->getReferenceExecutionDirectory()); + }); + } + + private function copyExerciseFiles(ExerciseScenario $scenario, string $dir): void + { + foreach ($scenario->getFiles() as $fileName => $content) { + $this->filesystem->dumpFile( + Path::join($dir, $fileName), + $content + ); + } + } +} diff --git a/src/Listener/CodePatchListener.php b/src/Listener/CodePatchListener.php index eda7abe2..3cbcf787 100644 --- a/src/Listener/CodePatchListener.php +++ b/src/Listener/CodePatchListener.php @@ -88,6 +88,7 @@ public function revert(EventInterface $event): void foreach ($this->originalCode as $fileName => $contents) { file_put_contents($fileName, $contents); + unset($this->originalCode[$fileName]); } } } diff --git a/test/ExerciseRunner/EnvironmentManagerTest.php b/test/ExerciseRunner/EnvironmentManagerTest.php new file mode 100644 index 00000000..8ab31e09 --- /dev/null +++ b/test/ExerciseRunner/EnvironmentManagerTest.php @@ -0,0 +1,99 @@ +createStudentSolutionDirectory(); + + $scenario = (new CliScenario()) + ->withFile('file.txt', 'content') + ->withFile('file2.txt', 'content2'); + + $manager = new EnvironmentManager(new Filesystem(), new EventDispatcher(new ResultAggregator())); + $manager->prepareStudent($context, $scenario); + + static::assertStringEqualsFile($context->getStudentExecutionDirectory() . '/file.txt', 'content'); + static::assertStringEqualsFile($context->getStudentExecutionDirectory() . '/file2.txt', 'content2'); + } + + public function testPrepareReferenceCopiesAllScenarioFilesAndSolutionFilesToExecutionDirectory(): void + { + $exercise = new CliExerciseImpl(); + $solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/cli/solution.php')); + $exercise->setSolution($solution); + + $context = new TestContext($exercise); + + $scenario = (new CliScenario()) + ->withFile('file.txt', 'content') + ->withFile('file2.txt', 'content2'); + + $manager = new EnvironmentManager(new Filesystem(), new EventDispatcher(new ResultAggregator())); + $manager->prepareReference($context, $scenario); + + static::assertFileEquals($context->getReferenceExecutionDirectory() . '/solution.php', __DIR__ . '/../res/cli/solution.php'); + static::assertStringEqualsFile($context->getReferenceExecutionDirectory() . '/file.txt', 'content'); + static::assertStringEqualsFile($context->getReferenceExecutionDirectory() . '/file2.txt', 'content2'); + } + + /** + * @dataProvider finishEvents + */ + public function testFileAreCleanedUpOnlyWhenFinishEventIsDispatched(string $eventName): void + { + $exercise = new CliExerciseImpl(); + $solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/cli/solution.php')); + $exercise->setSolution($solution); + + $context = new TestContext($exercise); + $context->createStudentSolutionDirectory(); + + $scenario = (new CliScenario()) + ->withFile('file.txt', 'content') + ->withFile('file2.txt', 'content2'); + + $eventDispatcher = new EventDispatcher(new ResultAggregator()); + $manager = new EnvironmentManager(new Filesystem(), $eventDispatcher); + $manager->prepareStudent($context, $scenario); + $manager->prepareReference($context, $scenario); + + static::assertFileExists($context->getStudentExecutionDirectory()); + static::assertFileExists($context->getReferenceExecutionDirectory()); + + $eventDispatcher->dispatch(new ExerciseRunnerEvent($eventName, $exercise, new Input('app', ['program' => '']))); + + static::assertFileExists($context->getStudentExecutionDirectory()); + static::assertFileNotExists($context->getReferenceExecutionDirectory() . '/file.txt'); + static::assertFileNotExists($context->getReferenceExecutionDirectory() . '/file2.txt'); + static::assertFileNotExists($context->getReferenceExecutionDirectory()); + } + + /** + * @return array + */ + public function finishEvents(): array + { + return [ + ['run.finish'], + ['verify.finish'], + ]; + } +}