Skip to content

Commit 7b6688a

Browse files
committed
Introduce new class to prepare environment files
1 parent b3736bb commit 7b6688a

File tree

4 files changed

+172
-0
lines changed

4 files changed

+172
-0
lines changed

Diff for: app/config.php

+4
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@
186186
EventDispatcher::class => factory(EventDispatcherFactory::class),
187187
EventDispatcherFactory::class => create(),
188188

189+
EnvironmentManager::class => function (ContainerInterface $c) {
190+
return new EnvironmentManager($c->get(Filesystem::class), $c->get(EventDispatcher::class));
191+
},
192+
189193
//Exercise Runners
190194
RunnerManager::class => function (ContainerInterface $c) {
191195
$manager = new RunnerManager();

Diff for: src/ExerciseRunner/EnvironmentManager.php

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshop\ExerciseRunner;
4+
5+
use PhpSchool\PhpWorkshop\Event\EventDispatcher;
6+
use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution;
7+
use PhpSchool\PhpWorkshop\Exercise\Scenario\ExerciseScenario;
8+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
9+
use PhpSchool\PhpWorkshop\Utils\Path;
10+
use Symfony\Component\Filesystem\Filesystem;
11+
12+
class EnvironmentManager
13+
{
14+
public function __construct(private Filesystem $filesystem, private EventDispatcher $eventDispatcher)
15+
{
16+
}
17+
18+
public function prepareStudent(ExecutionContext $context, ExerciseScenario $scenario): void
19+
{
20+
$this->copyExerciseFiles($scenario, $context->getStudentExecutionDirectory());
21+
22+
//cleanup the files when the run or verification process is finished
23+
//we do this at late as possible in case any checks or other event listeners need to access the files
24+
$this->eventDispatcher->listen(['run.finish', 'verify.finish'], function () use ($context, $scenario) {
25+
foreach ($scenario->getFiles() as $fileName => $content) {
26+
$this->filesystem->remove(Path::join($context->getStudentExecutionDirectory(), $fileName));
27+
}
28+
});
29+
}
30+
31+
public function prepareReference(ExecutionContext $context, ExerciseScenario $scenario): void
32+
{
33+
$exercise = $context->getExercise();
34+
35+
if (!$exercise instanceof ProvidesSolution) {
36+
return;
37+
}
38+
39+
$this->filesystem->mkdir($context->getReferenceExecutionDirectory());
40+
41+
$solution = $exercise->getSolution();
42+
43+
foreach ($solution->getFiles() as $file) {
44+
$this->filesystem->copy(
45+
$file->getAbsolutePath(),
46+
Path::join($context->getReferenceExecutionDirectory(), $file->getRelativePath())
47+
);
48+
}
49+
50+
$this->copyExerciseFiles($scenario, $context->getReferenceExecutionDirectory());
51+
52+
//cleanup the files when the run or verification process is finished
53+
//we do this at late as possible in case any checks or other event listeners need to access the files
54+
$this->eventDispatcher->listen(['run.finish', 'verify.finish'], function () use ($context) {
55+
$this->filesystem->remove($context->getReferenceExecutionDirectory());
56+
});
57+
}
58+
59+
private function copyExerciseFiles(ExerciseScenario $scenario, string $dir): void
60+
{
61+
foreach ($scenario->getFiles() as $fileName => $content) {
62+
$this->filesystem->dumpFile(
63+
Path::join($dir, $fileName),
64+
$content
65+
);
66+
}
67+
}
68+
}

Diff for: src/Listener/CodePatchListener.php

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public function revert(EventInterface $event): void
8888

8989
foreach ($this->originalCode as $fileName => $contents) {
9090
file_put_contents($fileName, $contents);
91+
unset($this->originalCode[$fileName]);
9192
}
9293
}
9394
}

Diff for: test/ExerciseRunner/EnvironmentManagerTest.php

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\ExerciseRunner;
4+
5+
use PhpSchool\PhpWorkshop\Event\EventDispatcher;
6+
use PhpSchool\PhpWorkshop\Event\ExerciseRunnerEvent;
7+
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
8+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
9+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\TestContext;
10+
use PhpSchool\PhpWorkshop\ExerciseRunner\EnvironmentManager;
11+
use PhpSchool\PhpWorkshop\Input\Input;
12+
use PhpSchool\PhpWorkshop\ResultAggregator;
13+
use PhpSchool\PhpWorkshop\Solution\DirectorySolution;
14+
use PhpSchool\PhpWorkshop\Solution\SingleFileSolution;
15+
use PhpSchool\PhpWorkshopTest\Asset\CliExerciseImpl;
16+
use PHPUnit\Framework\TestCase;
17+
use Symfony\Component\Filesystem\Filesystem;
18+
19+
class EnvironmentManagerTest extends TestCase
20+
{
21+
public function testPrepareStudentCopiesAllScenarioFilesToExecutionDirectory(): void
22+
{
23+
$context = new TestContext();
24+
$context->createStudentSolutionDirectory();
25+
26+
$scenario = (new CliScenario())
27+
->withFile('file.txt', 'content')
28+
->withFile('file2.txt', 'content2');
29+
30+
$manager = new EnvironmentManager(new Filesystem(), new EventDispatcher(new ResultAggregator()));
31+
$manager->prepareStudent($context, $scenario);
32+
33+
static::assertStringEqualsFile($context->getStudentExecutionDirectory() . '/file.txt', 'content');
34+
static::assertStringEqualsFile($context->getStudentExecutionDirectory() . '/file2.txt', 'content2');
35+
}
36+
37+
public function testPrepareReferenceCopiesAllScenarioFilesAndSolutionFilesToExecutionDirectory(): void
38+
{
39+
$exercise = new CliExerciseImpl();
40+
$solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/cli/solution.php'));
41+
$exercise->setSolution($solution);
42+
43+
$context = new TestContext($exercise);
44+
45+
$scenario = (new CliScenario())
46+
->withFile('file.txt', 'content')
47+
->withFile('file2.txt', 'content2');
48+
49+
$manager = new EnvironmentManager(new Filesystem(), new EventDispatcher(new ResultAggregator()));
50+
$manager->prepareReference($context, $scenario);
51+
52+
static::assertFileEquals($context->getReferenceExecutionDirectory() . '/solution.php', __DIR__ . '/../res/cli/solution.php');
53+
static::assertStringEqualsFile($context->getReferenceExecutionDirectory() . '/file.txt', 'content');
54+
static::assertStringEqualsFile($context->getReferenceExecutionDirectory() . '/file2.txt', 'content2');
55+
}
56+
57+
/**
58+
* @dataProvider finishEvents
59+
*/
60+
public function testFileAreCleanedUpOnlyWhenFinishEventIsDispatched(string $eventName): void
61+
{
62+
$exercise = new CliExerciseImpl();
63+
$solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/cli/solution.php'));
64+
$exercise->setSolution($solution);
65+
66+
$context = new TestContext($exercise);
67+
$context->createStudentSolutionDirectory();
68+
69+
$scenario = (new CliScenario())
70+
->withFile('file.txt', 'content')
71+
->withFile('file2.txt', 'content2');
72+
73+
$eventDispatcher = new EventDispatcher(new ResultAggregator());
74+
$manager = new EnvironmentManager(new Filesystem(), $eventDispatcher);
75+
$manager->prepareStudent($context, $scenario);
76+
$manager->prepareReference($context, $scenario);
77+
78+
static::assertFileExists($context->getStudentExecutionDirectory());
79+
static::assertFileExists($context->getReferenceExecutionDirectory());
80+
81+
$eventDispatcher->dispatch(new ExerciseRunnerEvent($eventName, $exercise, new Input('app', ['program' => ''])));
82+
83+
static::assertFileExists($context->getStudentExecutionDirectory());
84+
static::assertFileNotExists($context->getReferenceExecutionDirectory() . '/file.txt');
85+
static::assertFileNotExists($context->getReferenceExecutionDirectory() . '/file2.txt');
86+
static::assertFileNotExists($context->getReferenceExecutionDirectory());
87+
}
88+
89+
/**
90+
* @return array<array{0: string}>
91+
*/
92+
public function finishEvents(): array
93+
{
94+
return [
95+
['run.finish'],
96+
['verify.finish'],
97+
];
98+
}
99+
}

0 commit comments

Comments
 (0)