Skip to content

Commit 5c95d08

Browse files
committed
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

+28
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+
}
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+
}
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+
}
+102
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

+5
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
}
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+
}
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+
}

0 commit comments

Comments
 (0)