Skip to content

Commit

Permalink
Introduce new class to prepare environment files
Browse files Browse the repository at this point in the history
  • Loading branch information
AydinHassan committed May 11, 2024
1 parent 0e24fd9 commit adaa0d2
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 0 deletions.
4 changes: 4 additions & 0 deletions app/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
68 changes: 68 additions & 0 deletions src/ExerciseRunner/EnvironmentManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace PhpSchool\PhpWorkshop\ExerciseRunner;

use PhpSchool\PhpWorkshop\Event\EventDispatcher;
use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution;
use PhpSchool\PhpWorkshop\Exercise\Scenario\ExerciseScenario;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Utils\Path;
use Symfony\Component\Filesystem\Filesystem;

class EnvironmentManager
{
public function __construct(private Filesystem $filesystem, private EventDispatcher $eventDispatcher)
{
}

public function prepareStudent(ExecutionContext $context, ExerciseScenario $scenario): void
{
$this->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
);
}
}
}
1 change: 1 addition & 0 deletions src/Listener/CodePatchListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
}
}
99 changes: 99 additions & 0 deletions test/ExerciseRunner/EnvironmentManagerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace PhpSchool\PhpWorkshopTest\ExerciseRunner;

use PhpSchool\PhpWorkshop\Event\EventDispatcher;
use PhpSchool\PhpWorkshop\Event\ExerciseRunnerEvent;
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\TestContext;
use PhpSchool\PhpWorkshop\ExerciseRunner\EnvironmentManager;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\ResultAggregator;
use PhpSchool\PhpWorkshop\Solution\DirectorySolution;
use PhpSchool\PhpWorkshop\Solution\SingleFileSolution;
use PhpSchool\PhpWorkshopTest\Asset\CliExerciseImpl;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Filesystem\Filesystem;

class EnvironmentManagerTest extends TestCase
{
public function testPrepareStudentCopiesAllScenarioFilesToExecutionDirectory(): void
{
$context = new TestContext();
$context->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<array{0: string}>
*/
public function finishEvents(): array
{
return [
['run.finish'],
['verify.finish'],
];
}
}

0 comments on commit adaa0d2

Please sign in to comment.