Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5c95d08

Browse files
committedMay 9, 2024
Context
1 parent be53db0 commit 5c95d08

File tree

9 files changed

+423
-0
lines changed

9 files changed

+423
-0
lines changed
 

‎src/Exercise/MockExercise.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshop\Exercise;
4+
5+
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
6+
7+
class MockExercise extends AbstractExercise implements ExerciseInterface
8+
{
9+
public function getName(): string
10+
{
11+
return 'Mock Exercise';
12+
}
13+
14+
public function getDescription(): string
15+
{
16+
return 'Mock Exercise';
17+
}
18+
19+
public function getType(): ExerciseType
20+
{
21+
return ExerciseType::CUSTOM();
22+
}
23+
24+
public function getProblem(): string
25+
{
26+
return 'problem-file.md';
27+
}
28+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshop\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
6+
use PhpSchool\PhpWorkshop\Input\Input;
7+
use PhpSchool\PhpWorkshop\Utils\Path;
8+
use PhpSchool\PhpWorkshop\Utils\System;
9+
10+
class ExecutionContext
11+
{
12+
public function __construct(
13+
private string $studentExecutionDirectory,
14+
private string $referenceExecutionDirectory,
15+
private ExerciseInterface $exercise,
16+
private Input $input,
17+
) {
18+
}
19+
20+
public function getExercise(): ExerciseInterface
21+
{
22+
return $this->exercise;
23+
}
24+
25+
public function getInput(): Input
26+
{
27+
return $this->input;
28+
}
29+
30+
public function hasStudentSolution(): bool
31+
{
32+
return $this->input->hasArgument('program');
33+
}
34+
35+
public function getEntryPoint(): string
36+
{
37+
if (!$this->hasStudentSolution()) {
38+
throw new NoEntryPoint();
39+
}
40+
41+
return Path::join(
42+
$this->studentExecutionDirectory,
43+
basename($this->input->getRequiredArgument('program'))
44+
);
45+
}
46+
47+
public function getStudentExecutionDirectory(): string
48+
{
49+
return $this->studentExecutionDirectory;
50+
}
51+
52+
public function getReferenceExecutionDirectory(): string
53+
{
54+
return $this->referenceExecutionDirectory;
55+
}
56+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshop\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exception\RuntimeException;
6+
7+
class NoEntryPoint extends RuntimeException
8+
{
9+
public function __construct()
10+
{
11+
parent::__construct('No entry point provided');
12+
}
13+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshop\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exception\RuntimeException;
6+
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
7+
use PhpSchool\PhpWorkshop\Exercise\CliExercise;
8+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
9+
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
10+
use PhpSchool\PhpWorkshop\Input\Input;
11+
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
12+
use PhpSchool\PhpWorkshop\Utils\System;
13+
use Symfony\Component\Filesystem\Filesystem;
14+
use PhpSchool\PhpWorkshop\Utils\Path;
15+
16+
class TestContext extends ExecutionContext
17+
{
18+
private Filesystem $filesystem;
19+
private ExerciseInterface $exercise;
20+
private bool $studentSolutionDirWasCreated = false;
21+
private bool $referenceSolutionDirWasCreated = false;
22+
23+
public function __construct(
24+
ExerciseInterface $exercise = null,
25+
Input $input = null,
26+
string $studentDirectory = null,
27+
) {
28+
$this->exercise = $exercise ?? new MockExercise();
29+
30+
$this->filesystem = new Filesystem();
31+
32+
if ($studentDirectory === null) {
33+
$studentDirectory = System::randomTempDir();
34+
}
35+
36+
parent::__construct(
37+
$studentDirectory,
38+
System::randomTempDir(),
39+
$this->exercise,
40+
$input ? $input : new Input('test', ['program' => 'solution.php']),
41+
);
42+
}
43+
44+
public function createStudentSolutionDirectory(): void
45+
{
46+
$this->filesystem->mkdir($this->getStudentExecutionDirectory());
47+
$this->studentSolutionDirWasCreated = true;
48+
}
49+
50+
public function createReferenceSolutionDirectory(): void
51+
{
52+
$this->filesystem->mkdir($this->getReferenceExecutionDirectory());
53+
$this->referenceSolutionDirWasCreated = true;
54+
}
55+
56+
public function importStudentFileFromString(string $content, string $filename = 'solution.php'): void
57+
{
58+
if (!$this->studentSolutionDirWasCreated) {
59+
throw new RuntimeException(
60+
sprintf('Student execution directory not created. Call %s::createStudentSolutionDirectory() first.', self::class)
61+
);
62+
}
63+
64+
file_put_contents(Path::join($this->getStudentExecutionDirectory(), $filename), $content);
65+
}
66+
67+
public function importReferenceFileFromString(string $content, string $filename = 'solution.php'): void
68+
{
69+
if (!$this->referenceSolutionDirWasCreated) {
70+
throw new RuntimeException(
71+
sprintf('Reference execution directory not created. Call %s::createReferenceSolutionDirectory() first.', self::class)
72+
);
73+
}
74+
75+
file_put_contents(Path::join($this->getReferenceExecutionDirectory(), $filename), $content);
76+
}
77+
78+
public static function fromExerciseAndStudentSolution(ExerciseInterface $exercise, string $file): self
79+
{
80+
if (file_exists($file)) {
81+
$file = (string) realpath($file);
82+
}
83+
84+
$input = new Input('test', ['program' => $file]);
85+
return new self(
86+
exercise: $exercise,
87+
input: $input,
88+
studentDirectory: dirname($file)
89+
);
90+
}
91+
92+
public function __destruct()
93+
{
94+
if ($this->studentSolutionDirWasCreated) {
95+
$this->filesystem->remove($this->getStudentExecutionDirectory());
96+
}
97+
98+
if ($this->referenceSolutionDirWasCreated) {
99+
$this->filesystem->remove($this->getReferenceExecutionDirectory());
100+
}
101+
}
102+
}

‎src/Utils/System.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ public static function tempDir(string $path = ''): string
2323
{
2424
return Path::join(self::realpath(sys_get_temp_dir()), 'php-school', $path);
2525
}
26+
27+
public static function randomTempDir(): string
28+
{
29+
return Path::join(self::realpath(sys_get_temp_dir()), 'php-school', bin2hex(random_bytes(4)));
30+
}
2631
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
6+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
7+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\NoEntryPoint;
8+
use PhpSchool\PhpWorkshop\Input\Input;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class ExecutionContextTest extends TestCase
12+
{
13+
public function testGetters(): void
14+
{
15+
$exercise = new MockExercise();
16+
$input = new Input('test', ['program' => 'solution.php']);
17+
$context = new ExecutionContext(
18+
'/student-dir',
19+
'/reference-dir',
20+
$exercise,
21+
$input
22+
);
23+
24+
static::assertSame($exercise, $context->getExercise());
25+
static::assertSame($input, $context->getInput());
26+
static::assertSame('/student-dir', $context->getStudentExecutionDirectory());
27+
static::assertSame('/reference-dir', $context->getReferenceExecutionDirectory());
28+
}
29+
30+
public function testHasStudentSolution(): void
31+
{
32+
$exercise = new MockExercise();
33+
$input = new Input('test', ['program' => 'solution.php']);
34+
$context = new ExecutionContext(
35+
'/student-dir',
36+
'/reference-dir',
37+
$exercise,
38+
$input
39+
);
40+
41+
static::assertTrue($context->hasStudentSolution());
42+
43+
$exercise = new MockExercise();
44+
$input = new Input('test');
45+
$context = new ExecutionContext(
46+
'/student-dir',
47+
'/reference-dir',
48+
$exercise,
49+
$input
50+
);
51+
52+
static::assertFalse($context->hasStudentSolution());
53+
}
54+
55+
public function testGetEntryPoint(): void
56+
{
57+
$exercise = new MockExercise();
58+
$input = new Input('test', ['program' => 'solution.php']);
59+
$context = new ExecutionContext(
60+
'/student-dir',
61+
'/reference-dir',
62+
$exercise,
63+
$input
64+
);
65+
66+
static::assertSame('/student-dir/solution.php', $context->getEntryPoint());
67+
}
68+
69+
public function testGetEntryPointThrowsExceptionWhenNoStudentSolution(): void
70+
{
71+
static::expectException(NoEntryPoint::class);
72+
73+
$exercise = new MockExercise();
74+
$input = new Input('test');
75+
$context = new ExecutionContext(
76+
'/student-dir',
77+
'/reference-dir',
78+
$exercise,
79+
$input
80+
);
81+
82+
$context->getEntryPoint();
83+
}
84+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\NoEntryPoint;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class NoEntryPointTest extends TestCase
9+
{
10+
public function testException(): void
11+
{
12+
$e = new NoEntryPoint();
13+
static::assertSame('No entry point provided', $e->getMessage());
14+
}
15+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
6+
use PhpSchool\PhpWorkshop\Exercise\TemporaryDirectoryTrait;
7+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\TestContext;
8+
use PhpSchool\PhpWorkshop\Solution\DirectorySolution;
9+
use PhpSchool\PhpWorkshop\Utils\System;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class TestContextTest extends TestCase
13+
{
14+
public function testThatDirectoriesAreNotCreatedInitially(): void
15+
{
16+
$context = new TestContext();
17+
18+
static::assertFileNotExists($context->getStudentExecutionDirectory());
19+
static::assertFileNotExists($context->getReferenceExecutionDirectory());
20+
}
21+
22+
public function testCreateDirectories(): void
23+
{
24+
$context = new TestContext();
25+
$context->createStudentSolutionDirectory();
26+
$context->createReferenceSolutionDirectory();
27+
28+
static::assertFileExists($context->getStudentExecutionDirectory());
29+
static::assertFileExists($context->getReferenceExecutionDirectory());
30+
}
31+
32+
public function testFromExerciseAndSolutionFactory(): void
33+
{
34+
$exercise = new MockExercise();
35+
$context = TestContext::fromExerciseAndStudentSolution($exercise, __FILE__);
36+
37+
static::assertSame(__DIR__, $context->getStudentExecutionDirectory());
38+
static::assertSame(__FILE__, $context->getInput()->getRequiredArgument('program'));
39+
static::assertSame($exercise, $context->getExercise());
40+
}
41+
42+
public function testImportStudentSolutionFileFromStringThrowsExceptionIfExecutionDirectoryDoesNotExist(): void
43+
{
44+
$this->expectException(\RuntimeException::class);
45+
46+
$context = new TestContext();
47+
$context->importStudentFileFromString('<?php echo "yo";');
48+
}
49+
50+
public function testImportStudentSolutionFileFromStringCreatesFileInExecutionDirectory(): void
51+
{
52+
$context = new TestContext();
53+
$context->createStudentSolutionDirectory();
54+
$context->importStudentFileFromString('<?php echo "yo";');
55+
56+
static::assertFileExists($context->getStudentExecutionDirectory() . '/solution.php');
57+
static::assertEquals('<?php echo "yo";', file_get_contents($context->getStudentExecutionDirectory() . '/solution.php'));
58+
}
59+
60+
public function testImportStudentSolutionFileFromStringWithCustomPathCreatesFileInExecutionDirectory(): void
61+
{
62+
$context = new TestContext();
63+
$context->createStudentSolutionDirectory();
64+
$context->importStudentFileFromString('<?php echo "yo";', 'some-file.php');
65+
66+
static::assertFileExists($context->getStudentExecutionDirectory() . '/some-file.php');
67+
static::assertEquals('<?php echo "yo";', file_get_contents($context->getStudentExecutionDirectory() . '/some-file.php'));
68+
}
69+
70+
public function testImportReferenceSolutionFileFromStringThrowsExceptionIfExecutionDirectoryDoesNotExist(): void
71+
{
72+
$this->expectException(\RuntimeException::class);
73+
74+
$context = new TestContext();
75+
$context->importReferenceFileFromString('<?php echo "yo";');
76+
}
77+
78+
public function testImportReferenceSolutionFileFromStringCreatesFileInExecutionDirectory(): void
79+
{
80+
$context = new TestContext();
81+
$context->createReferenceSolutionDirectory();
82+
$context->importReferenceFileFromString('<?php echo "yo";');
83+
84+
static::assertFileExists($context->getReferenceExecutionDirectory() . '/solution.php');
85+
static::assertEquals('<?php echo "yo";', file_get_contents($context->getReferenceExecutionDirectory() . '/solution.php'));
86+
}
87+
88+
public function testImportReferenceSolutionFileFromStringWithCustomPathCreatesFileInExecutionDirectory(): void
89+
{
90+
$context = new TestContext();
91+
$context->createReferenceSolutionDirectory();
92+
$context->importReferenceFileFromString('<?php echo "yo";', 'some-file.php');
93+
94+
static::assertFileExists($context->getReferenceExecutionDirectory() . '/some-file.php');
95+
static::assertEquals('<?php echo "yo";', file_get_contents($context->getReferenceExecutionDirectory() . '/some-file.php'));
96+
}
97+
98+
public function testDestructCleansUpExecutionDirectories(): void
99+
{
100+
$context = new TestContext();
101+
$context->createStudentSolutionDirectory();
102+
$context->createReferenceSolutionDirectory();
103+
104+
$studentExecutionDirectory = $context->getStudentExecutionDirectory();
105+
$referenceExecutionDirectory = $context->getReferenceExecutionDirectory();
106+
107+
static::assertFileExists($studentExecutionDirectory);
108+
static::assertFileExists($referenceExecutionDirectory);
109+
110+
unset($context);
111+
112+
static::assertFileNotExists($studentExecutionDirectory);
113+
static::assertFileNotExists($referenceExecutionDirectory);
114+
}
115+
}

‎test/Utils/SystemTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,9 @@ public function testTempDirWithPath(): void
3333
$expect = sprintf('%s/php-school/%s', realpath(sys_get_temp_dir()), 'test');
3434
self::assertSame($expect, System::tempDir('test'));
3535
}
36+
37+
public function testRandomTempDir(): void
38+
{
39+
self::assertTrue(str_starts_with(System::randomTempDir(), realpath(sys_get_temp_dir()) . '/php-school'));
40+
}
3641
}

0 commit comments

Comments
 (0)
Please sign in to comment.