From 9dc82146c91b78feccb86b07e265e6140ecd1a05 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Tue, 1 Dec 2020 19:21:22 +0100 Subject: [PATCH 1/2] Allow exercises to provide initial code --- app/config.php | 17 ++++-- src/Exercise/ProvidesInitialCode.php | 21 ++++++++ src/Factory/MenuFactory.php | 8 +-- src/Listener/InitialCodeListener.php | 46 ++++++++++++++++ src/Solution/SingleFileSolution.php | 4 ++ src/Solution/SolutionFile.php | 2 +- test/Asset/ExerciseWithInitialCode.php | 53 ++++++++++++++++++ test/Asset/initial-code/init-solution.php | 5 ++ test/Listener/InitialCodeListenerTest.php | 66 +++++++++++++++++++++++ 9 files changed, 211 insertions(+), 11 deletions(-) create mode 100644 src/Exercise/ProvidesInitialCode.php create mode 100644 src/Listener/InitialCodeListener.php create mode 100644 test/Asset/ExerciseWithInitialCode.php create mode 100644 test/Asset/initial-code/init-solution.php create mode 100644 test/Listener/InitialCodeListenerTest.php diff --git a/app/config.php b/app/config.php index 17d0ffbb..e59ea364 100644 --- a/app/config.php +++ b/app/config.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Colors\Color; +use PhpSchool\PhpWorkshop\Listener\InitialCodeListener; use function DI\create; use function DI\factory; use Kadet\Highlighter\KeyLighter; @@ -201,17 +202,20 @@ }, //Listeners - PrepareSolutionListener::class => create(), - CodePatchListener::class => function (ContainerInterface $c) { + InitialCodeListener::class => function (ContainerInterface $c) { + return new InitialCodeListener(getcwd()); + }, + PrepareSolutionListener::class => create(), + CodePatchListener::class => function (ContainerInterface $c) { return new CodePatchListener($c->get(CodePatcher::class)); }, - SelfCheckListener::class => function (ContainerInterface $c) { + SelfCheckListener::class => function (ContainerInterface $c) { return new SelfCheckListener($c->get(ResultAggregator::class)); }, CheckExerciseAssignedListener::class => function (ContainerInterface $c) { return new CheckExerciseAssignedListener($c->get(UserState::class)); }, - ConfigureCommandListener::class => function (ContainerInterface $c) { + ConfigureCommandListener::class => function (ContainerInterface $c) { return new ConfigureCommandListener( $c->get(UserState::class), $c->get(ExerciseRepository::class), @@ -393,5 +397,10 @@ function (CgiResult $result) use ($c) { containerListener(SelfCheckListener::class) ], ], + 'create-initial-code' => [ + 'exercise.selected' => [ + containerListener(InitialCodeListener::class) + ] + ] ], ]; diff --git a/src/Exercise/ProvidesInitialCode.php b/src/Exercise/ProvidesInitialCode.php new file mode 100644 index 00000000..708fb996 --- /dev/null +++ b/src/Exercise/ProvidesInitialCode.php @@ -0,0 +1,21 @@ +dispatch(new Event('exercise.selected', ['exercise' => $exercise])); $eventDispatcher->dispatch( - new Event( - sprintf( - 'exercise.selected.%s', - AbstractExercise::normaliseName($exercise->getName()) - ) - ) + new Event(sprintf('exercise.selected.%s', AbstractExercise::normaliseName($exercise->getName()))) ); } } diff --git a/src/Listener/InitialCodeListener.php b/src/Listener/InitialCodeListener.php new file mode 100644 index 00000000..a7948bac --- /dev/null +++ b/src/Listener/InitialCodeListener.php @@ -0,0 +1,46 @@ +workingDirectory = $workingDirectory; + } + + /** + * @param Event $event + */ + public function __invoke(Event $event): void + { + $exercise = $event->getParameter('exercise'); + + if ($exercise instanceof ProvidesInitialCode) { + foreach ($exercise->getInitialCode()->getFiles() as $file) { + /** @var SolutionFile $file */ + if (!file_exists($this->workingDirectory . '/' . $file->getRelativePath())) { + copy($file->getAbsolutePath(), $this->workingDirectory . '/' . $file->getRelativePath()); + } + } + } + } +} diff --git a/src/Solution/SingleFileSolution.php b/src/Solution/SingleFileSolution.php index 151b9b8a..b32aac67 100644 --- a/src/Solution/SingleFileSolution.php +++ b/src/Solution/SingleFileSolution.php @@ -22,6 +22,10 @@ class SingleFileSolution implements SolutionInterface */ public function __construct(string $file) { + if (!file_exists($file)) { + throw new InvalidArgumentException(sprintf('File: "%s" does not exist', $file)); + } + $this->file = SolutionFile::fromFile((string) realpath($file)); } diff --git a/src/Solution/SolutionFile.php b/src/Solution/SolutionFile.php index fe4af028..b9a89d44 100644 --- a/src/Solution/SolutionFile.php +++ b/src/Solution/SolutionFile.php @@ -53,7 +53,7 @@ public static function fromFile(string $file): self * * @return string */ - private function getAbsolutePath(): string + public function getAbsolutePath(): string { return sprintf('%s/%s', $this->baseDirectory, $this->relativePath); } diff --git a/test/Asset/ExerciseWithInitialCode.php b/test/Asset/ExerciseWithInitialCode.php new file mode 100644 index 00000000..e32a9467 --- /dev/null +++ b/test/Asset/ExerciseWithInitialCode.php @@ -0,0 +1,53 @@ +filesystem = new Filesystem(); + + $this->cwd = sprintf('%s/%s', str_replace('\\', '/', sys_get_temp_dir()), $this->getName()); + mkdir($this->cwd, 0775, true); + } + + public function testExerciseCodeIsCopiedIfExerciseProvidesInitialCode(): void + { + $exercise = new ExerciseWithInitialCode(); + + $event = new Event('exercise.selected', ['exercise' => $exercise]); + + $listener = new InitialCodeListener($this->cwd); + $listener->__invoke($event); + + $this->assertFileExists($this->cwd . '/init-solution.php'); + $this->assertFileEquals( + $exercise->getInitialCode()->getFiles()[0]->getAbsolutePath(), + $this->cwd . '/init-solution.php' + ); + } + + public function testExerciseCodeIsNotCopiedIfExerciseDoesNotProvideInitialCode(): void + { + $exercise = new CliExerciseImpl(); + + $event = new Event('exercise.selected', ['exercise' => $exercise]); + + $listener = new InitialCodeListener($this->cwd); + $listener->__invoke($event); + + $this->assertEmpty(array_diff(scandir($this->cwd), ['.', '..'])); + } + + public function tearDown(): void + { + $this->filesystem->remove($this->cwd); + } +} From d50b6425f6f5961076d32a5ef6c3449eb595bb99 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Tue, 1 Dec 2020 19:56:17 +0100 Subject: [PATCH 2/2] Return early --- src/Listener/InitialCodeListener.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Listener/InitialCodeListener.php b/src/Listener/InitialCodeListener.php index a7948bac..93e1cadd 100644 --- a/src/Listener/InitialCodeListener.php +++ b/src/Listener/InitialCodeListener.php @@ -34,12 +34,14 @@ public function __invoke(Event $event): void { $exercise = $event->getParameter('exercise'); - if ($exercise instanceof ProvidesInitialCode) { - foreach ($exercise->getInitialCode()->getFiles() as $file) { - /** @var SolutionFile $file */ - if (!file_exists($this->workingDirectory . '/' . $file->getRelativePath())) { - copy($file->getAbsolutePath(), $this->workingDirectory . '/' . $file->getRelativePath()); - } + if (!$exercise instanceof ProvidesInitialCode) { + return; + } + + foreach ($exercise->getInitialCode()->getFiles() as $file) { + /** @var SolutionFile $file */ + if (!file_exists($this->workingDirectory . '/' . $file->getRelativePath())) { + copy($file->getAbsolutePath(), $this->workingDirectory . '/' . $file->getRelativePath()); } } }