Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 29eeaf7

Browse files
Context
1 parent be53db0 commit 29eeaf7

File tree

13 files changed

+576
-0
lines changed

13 files changed

+576
-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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\System;
8+
9+
class ExecutionContextFactory
10+
{
11+
public function fromInputAndExercise(Input $input, ExerciseInterface $exercise): ExecutionContext
12+
{
13+
return new ExecutionContext(
14+
dirname($input->getRequiredArgument('program')),
15+
System::randomTempDir(),
16+
$exercise,
17+
$input
18+
);
19+
}
20+
}
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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\System;
8+
use Symfony\Component\Filesystem\Filesystem;
9+
10+
class StaticExecutionContextFactory extends ExecutionContextFactory
11+
{
12+
public function __construct(private TestContext $context)
13+
{
14+
}
15+
16+
public function fromInputAndExercise(Input $input, ExerciseInterface $exercise): ExecutionContext
17+
{
18+
return $this->context;
19+
}
20+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshop\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exception\RuntimeException;
6+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
7+
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
8+
use PhpSchool\PhpWorkshop\Input\Input;
9+
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
10+
use PhpSchool\PhpWorkshop\Utils\System;
11+
use Symfony\Component\Filesystem\Filesystem;
12+
use PhpSchool\PhpWorkshop\Utils\Path;
13+
14+
class TestContext extends ExecutionContext
15+
{
16+
public Filesystem $filesystem;
17+
public ExerciseInterface $exercise;
18+
19+
private function __construct(
20+
ExerciseInterface $exercise = null,
21+
Input $input = null
22+
) {
23+
$this->exercise = $exercise ?? new MockExercise();
24+
25+
$this->filesystem = new Filesystem();
26+
27+
parent::__construct(
28+
System::randomTempDir(),
29+
System::randomTempDir(),
30+
$this->exercise,
31+
$input ? $input : new Input('test', ['program' => 'solution.php']),
32+
);
33+
}
34+
35+
public function importStudentFileFromString(string $content, string $filename = 'solution.php'): void
36+
{
37+
if (!$this->filesystem->exists($this->getStudentExecutionDirectory())) {
38+
throw new RuntimeException(
39+
sprintf('Execution directories not created. Use %s::withDirectories() method instead.', self::class)
40+
);
41+
}
42+
43+
file_put_contents(Path::join($this->getStudentExecutionDirectory(), $filename), $content);
44+
}
45+
46+
public function importStudentSolution(string $file): void
47+
{
48+
if (!$this->filesystem->exists($this->getStudentExecutionDirectory())) {
49+
throw new RuntimeException(
50+
sprintf('Execution directories not created. Use %s::withDirectories() method instead.', self::class)
51+
);
52+
}
53+
54+
copy($file, Path::join($this->getStudentExecutionDirectory(), 'solution.php'));
55+
}
56+
57+
public function importStudentSolutionFolder(string $folder): void
58+
{
59+
if (!$this->filesystem->exists($this->getStudentExecutionDirectory())) {
60+
throw new RuntimeException(
61+
sprintf('Execution directories not created. Use %s::withDirectories() method instead.', self::class)
62+
);
63+
}
64+
65+
$this->filesystem->mirror($folder, $this->getStudentExecutionDirectory());
66+
}
67+
68+
public function importReferenceFileFromString(string $content, string $filename = 'solution.php'): void
69+
{
70+
if (!$this->filesystem->exists($this->getReferenceExecutionDirectory())) {
71+
throw new RuntimeException(
72+
sprintf('Execution directories not created. Use %s::withDirectories() method instead.', self::class)
73+
);
74+
}
75+
76+
file_put_contents(Path::join($this->getReferenceExecutionDirectory(), $filename), $content);
77+
}
78+
79+
public function importReferenceSolution(SolutionInterface $solution): void
80+
{
81+
if (!$this->filesystem->exists($this->getReferenceExecutionDirectory())) {
82+
throw new RuntimeException(
83+
sprintf('Execution directories not created. Use %s::withDirectories() method instead.', self::class)
84+
);
85+
}
86+
87+
foreach ($solution->getFiles() as $file) {
88+
$this->filesystem->copy(
89+
$file->getAbsolutePath(),
90+
Path::join($this->getReferenceExecutionDirectory(), $file->getRelativePath())
91+
);
92+
}
93+
}
94+
95+
public static function withDirectories(Input $input = null, ExerciseInterface $exercise = null): self
96+
{
97+
$self = new self($exercise, $input);
98+
99+
$self->filesystem->mkdir($self->getStudentExecutionDirectory());
100+
$self->filesystem->mkdir($self->getReferenceExecutionDirectory());
101+
102+
return $self;
103+
}
104+
105+
public static function withoutDirectories(Input $input = null, ExerciseInterface $exercise = null): self
106+
{
107+
return new self($exercise, $input);
108+
}
109+
110+
public function __destruct()
111+
{
112+
$this->filesystem->remove($this->getStudentExecutionDirectory());
113+
$this->filesystem->remove($this->getReferenceExecutionDirectory());
114+
}
115+
}

‎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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\ExerciseRunner\Context;
4+
5+
use PhpSchool\PhpWorkshop\Exercise\MockExercise;
6+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContextFactory;
7+
use PhpSchool\PhpWorkshop\Input\Input;
8+
use PhpSchool\PhpWorkshop\Utils\System;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class ExecutionContextFactoryTest extends TestCase
12+
{
13+
public function testFactory(): void
14+
{
15+
$factory = new ExecutionContextFactory();
16+
17+
$temporaryDirectory = System::randomTempDir();
18+
19+
$input = new Input('test', ['program' => $temporaryDirectory . '/solution.php']);
20+
$exercise = new MockExercise();
21+
22+
$context = $factory->fromInputAndExercise($input, $exercise);
23+
24+
//check that student execution directory uses the parent directory of the program from the input
25+
static::assertSame($temporaryDirectory, $context->getStudentExecutionDirectory());
26+
static::assertSame($temporaryDirectory . '/solution.php', $context->getEntryPoint());
27+
28+
//check that reference execution directory is a random temporary directory
29+
static::assertTrue(str_starts_with($context->getReferenceExecutionDirectory(), System::tempDir()));
30+
}
31+
}
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+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /