diff --git a/app/config.php b/app/config.php index 10422ea2..9846ea0f 100644 --- a/app/config.php +++ b/app/config.php @@ -231,7 +231,11 @@ }, PrepareSolutionListener::class => create(), CodePatchListener::class => function (ContainerInterface $c) { - return new CodePatchListener($c->get(CodePatcher::class)); + return new CodePatchListener( + $c->get(CodePatcher::class), + $c->get(LoggerInterface::class), + $c->get('debugMode') + ); }, SelfCheckListener::class => function (ContainerInterface $c) { return new SelfCheckListener($c->get(ResultAggregator::class)); diff --git a/src/Listener/CodePatchListener.php b/src/Listener/CodePatchListener.php index 6fc12f0b..5cfc63d4 100644 --- a/src/Listener/CodePatchListener.php +++ b/src/Listener/CodePatchListener.php @@ -8,6 +8,7 @@ use PhpSchool\PhpWorkshop\Event\EventInterface; use PhpSchool\PhpWorkshop\Event\ExerciseRunnerEvent; use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution; +use Psr\Log\LoggerInterface; use RuntimeException; /** @@ -20,6 +21,16 @@ class CodePatchListener */ private $codePatcher; + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var bool + */ + private $debugMode; + /** * @var array */ @@ -27,10 +38,14 @@ class CodePatchListener /** * @param CodePatcher $codePatcher + * @param LoggerInterface $logger + * @param bool $debugMode */ - public function __construct(CodePatcher $codePatcher) + public function __construct(CodePatcher $codePatcher, LoggerInterface $logger, bool $debugMode) { $this->codePatcher = $codePatcher; + $this->logger = $logger; + $this->debugMode = $debugMode; } /** @@ -46,6 +61,8 @@ public function patch(ExerciseRunnerEvent $event): void } foreach (array_filter($files) as $fileName) { + $this->logger->debug("Patching file: $fileName"); + $this->originalCode[$fileName] = (string) file_get_contents($fileName); file_put_contents( @@ -64,6 +81,11 @@ public function revert(EventInterface $event): void return; } + //if we're in debug mode leave the students patch for debugging + if ($event instanceof ExerciseRunnerEvent && $this->debugMode) { + unset($this->originalCode[$event->getInput()->getArgument('program')]); + } + foreach ($this->originalCode as $fileName => $contents) { file_put_contents($fileName, $contents); } diff --git a/src/TestUtils/WorkshopExerciseTest.php b/src/TestUtils/WorkshopExerciseTest.php index 181c5c99..8b501ccc 100644 --- a/src/TestUtils/WorkshopExerciseTest.php +++ b/src/TestUtils/WorkshopExerciseTest.php @@ -23,6 +23,7 @@ use PhpSchool\PhpWorkshop\ResultAggregator; use PhpSchool\PhpWorkshop\Utils\ArrayObject; use PhpSchool\PhpWorkshop\Utils\Collection; +use PhpSchool\PhpWorkshop\Utils\System; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; @@ -53,6 +54,11 @@ public function setUp(): void $this->container = $this->app->configure(); } + public function tearDown(): void + { + (new Filesystem())->remove(System::tempDir()); + } + /** * @return class-string */ diff --git a/test/Listener/CodePatchListenerTest.php b/test/Listener/CodePatchListenerTest.php index 018055ad..227b76a0 100644 --- a/test/Listener/CodePatchListenerTest.php +++ b/test/Listener/CodePatchListenerTest.php @@ -10,6 +10,8 @@ use PhpSchool\PhpWorkshop\Utils\System; use PhpSchool\PhpWorkshopTest\Asset\ProvidesSolutionExercise; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use Symfony\Component\Filesystem\Filesystem; class CodePatchListenerTest extends TestCase @@ -47,6 +49,11 @@ public function setUp(): void touch($this->solution); } + public function tearDown(): void + { + $this->filesystem->remove(dirname($this->file)); + } + public function testPatchUpdatesCode(): void { file_put_contents($this->file, 'ORIGINAL CONTENT'); @@ -60,7 +67,7 @@ public function testPatchUpdatesCode(): void ->with($exercise, 'ORIGINAL CONTENT') ->willReturn('MODIFIED CONTENT'); - $listener = new CodePatchListener($this->codePatcher); + $listener = new CodePatchListener($this->codePatcher, new NullLogger(), false); $event = new ExerciseRunnerEvent('event', $exercise, $input); $listener->patch($event); @@ -80,7 +87,7 @@ public function testRevertAfterPatch(): void ->with($exercise, 'ORIGINAL CONTENT') ->willReturn('MODIFIED CONTENT'); - $listener = new CodePatchListener($this->codePatcher); + $listener = new CodePatchListener($this->codePatcher, new NullLogger(), false); $event = new ExerciseRunnerEvent('event', $exercise, $input); $listener->patch($event); $listener->revert($event); @@ -101,7 +108,7 @@ public function testPatchesProvidedSolution(): void ->withConsecutive([$exercise, 'ORIGINAL CONTENT'], [$exercise, "willReturn('MODIFIED CONTENT'); - $listener = new CodePatchListener($this->codePatcher); + $listener = new CodePatchListener($this->codePatcher, new NullLogger(), false); $event = new ExerciseRunnerEvent('event', $exercise, $input); $listener->patch($event); @@ -109,8 +116,47 @@ public function testPatchesProvidedSolution(): void self::assertStringEqualsFile($exercise->getSolution()->getEntryPoint(), 'MODIFIED CONTENT'); } - public function tearDown(): void + public function testFileIsLoggedWhenPatches(): void { - $this->filesystem->remove(dirname($this->file)); + file_put_contents($this->file, 'ORIGINAL CONTENT'); + + $input = new Input('app', ['program' => $this->file]); + $exercise = $this->createMock(ExerciseInterface::class); + + $this->codePatcher + ->expects($this->once()) + ->method('patch') + ->with($exercise, 'ORIGINAL CONTENT') + ->willReturn('MODIFIED CONTENT'); + + $logger = $this->createMock(LoggerInterface::class); + $logger->expects($this->once()) + ->method('debug') + ->with('Patching file: ' . $this->file); + + $listener = new CodePatchListener($this->codePatcher, $logger, false); + $event = new ExerciseRunnerEvent('event', $exercise, $input); + $listener->patch($event); + } + + public function testRevertDoesNotRevertStudentSubmissionPatchIfInDebugMode(): void + { + file_put_contents($this->file, 'ORIGINAL CONTENT'); + + $input = new Input('app', ['program' => $this->file]); + $exercise = $this->createMock(ExerciseInterface::class); + + $this->codePatcher + ->expects($this->once()) + ->method('patch') + ->with($exercise, 'ORIGINAL CONTENT') + ->willReturn('MODIFIED CONTENT'); + + $listener = new CodePatchListener($this->codePatcher, new NullLogger(), true); + $event = new ExerciseRunnerEvent('event', $exercise, $input); + $listener->patch($event); + $listener->revert($event); + + self::assertStringEqualsFile($this->file, 'MODIFIED CONTENT'); } }