Skip to content

Commit b080d91

Browse files
committed
Use scenarios in exercises
1 parent 1b6de18 commit b080d91

11 files changed

+104
-93
lines changed

Diff for: phpstan.neon

-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
parameters:
22
treatPhpDocTypesAsCertain: false
33
ignoreErrors:
4-
-
5-
message: '#PhpSchool\\PhpWorkshop\\ExerciseRunner\\CliRunner\:\:preserveOldArgFormat\(\) should return#'
6-
path: src/ExerciseRunner/CliRunner.php
74
-
85
message: '#Call to an undefined method PhpParser\\Node\\Expr\|PhpParser\\Node\\Name\:\:__toString\(\)#'
96
path: src/Check/FunctionRequirementsCheck.php

Diff for: src/Exercise/CgiExercise.php

+10-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpSchool\PhpWorkshop\Exercise;
66

7+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CgiScenario;
78
use Psr\Http\Message\RequestInterface;
89

910
/**
@@ -12,10 +13,15 @@
1213
interface CgiExercise extends ProvidesSolution
1314
{
1415
/**
15-
* This method should return an array of PSR-7 requests, which will be forwarded to the student's
16-
* solution.
16+
* This method should return an instance of CgiScenario which contains PSR-7 requests,
17+
* which will be forwarded to the student's solution.
1718
*
18-
* @return array<RequestInterface> An array of PSR-7 requests.
19+
* Use like so:
20+
*
21+
* ```
22+
* return (new CgiScenario())
23+
* ->withExecution($request1)
24+
* ```
1925
*/
20-
public function getRequests(): array;
26+
public function defineTestScenario(): CgiScenario;
2127
}

Diff for: src/Exercise/CliExercise.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,24 @@
44

55
namespace PhpSchool\PhpWorkshop\Exercise;
66

7+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
8+
79
/**
810
* This interface describes the additional methods a CLI type exercise should implement.
911
*/
1012
interface CliExercise extends ProvidesSolution
1113
{
1214
/**
13-
* This method should return an array of an array of strings.
14-
* Each set of arguments will be passed to the students solution as command line arguments.
15+
* This method should return an instance of CliScenario which contains sets of arguments,
16+
* which will be passed to the students solution as command line arguments.
17+
*
18+
* Use like so:
1519
*
16-
* @return array<array<string>> An array of string arguments.
20+
* ```
21+
* return (new CliScenario())
22+
* ->withExecution(['arg1', 'arg2'])
23+
* ->withExecution(['round2-arg1', 'round2-arg2'])
24+
* ```
1725
*/
18-
public function getArgs(): array;
26+
public function defineTestScenario(): CliScenario;
1927
}

Diff for: src/ExerciseRunner/CgiRunner.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,15 @@ public function getRequiredChecks(): array
105105
*/
106106
public function verify(ExecutionContext $context): ResultInterface
107107
{
108+
$scenario = $this->exercise->defineTestScenario();
109+
108110
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $context->getInput()));
109111
$result = new CgiResult(
110112
array_map(
111113
function (RequestInterface $request) use ($context) {
112114
return $this->doVerify($request, $context);
113115
},
114-
$this->exercise->getRequests()
116+
$scenario->getExecutions()
115117
)
116118
);
117119
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $context->getInput()));
@@ -287,9 +289,11 @@ private function getPhpProcess(string $workingDirectory, string $fileName, Reque
287289
*/
288290
public function run(ExecutionContext $context, OutputInterface $output): bool
289291
{
292+
$scenario = $this->exercise->defineTestScenario();
293+
290294
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $context->getInput()));
291295
$success = true;
292-
foreach ($this->exercise->getRequests() as $i => $request) {
296+
foreach ($scenario->getExecutions() as $i => $request) {
293297
/** @var CgiExecuteEvent $event */
294298
$event = $this->eventDispatcher->dispatch(
295299
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $context->getInput(), $request)

Diff for: src/ExerciseRunner/CliRunner.php

+10-25
Original file line numberDiff line numberDiff line change
@@ -102,43 +102,26 @@ public function getRequiredChecks(): array
102102
*/
103103
public function verify(ExecutionContext $context): ResultInterface
104104
{
105+
$scenario = $this->exercise->defineTestScenario();
106+
105107
$this->eventDispatcher->dispatch(new CliExerciseRunnerEvent('cli.verify.start', $this->exercise, $context->getInput()));
106108
$result = new CliResult(
107109
array_map(
108-
function (array $args) use ($context) {
110+
function (Collection $args) use ($context) {
109111
return $this->doVerify($context, $args);
110112
},
111-
$this->preserveOldArgFormat($this->exercise->getArgs())
113+
$scenario->getExecutions()
112114
)
113115
);
114116
$this->eventDispatcher->dispatch(new CliExerciseRunnerEvent('cli.verify.finish', $this->exercise, $context->getInput()));
115117
return $result;
116118
}
117119

118120
/**
119-
* BC - getArgs only returned 1 set of args in v1 instead of multiple sets of args in v2
120-
*
121-
* @param array<int, array<string>>|array<int, string> $args
122-
* @return array<int, array<string>>
123-
*/
124-
private function preserveOldArgFormat(array $args): array
125-
{
126-
if (isset($args[0]) && !is_array($args[0])) {
127-
$args = [$args];
128-
} elseif (count($args) === 0) {
129-
$args = [[]];
130-
}
131-
132-
return $args;
133-
}
134-
135-
/**
136-
* @param array<string> $args
121+
* @param Collection<int, string> $args
137122
*/
138-
private function doVerify(ExecutionContext $context, array $args): CliResultInterface
123+
private function doVerify(ExecutionContext $context, Collection $args): CliResultInterface
139124
{
140-
//arrays are not pass-by-ref
141-
$args = new ArrayObject($args);
142125

143126
try {
144127
/** @var CliExecuteEvent $event */
@@ -214,12 +197,14 @@ private function doVerify(ExecutionContext $context, array $args): CliResultInte
214197
*/
215198
public function run(ExecutionContext $context, OutputInterface $output): bool
216199
{
200+
$scenario = $this->exercise->defineTestScenario();
201+
217202
$this->eventDispatcher->dispatch(new CliExerciseRunnerEvent('cli.run.start', $this->exercise, $context->getInput()));
218203
$success = true;
219-
foreach ($this->preserveOldArgFormat($this->exercise->getArgs()) as $i => $args) {
204+
foreach ($scenario->getExecutions() as $i => $args) {
220205
/** @var CliExecuteEvent $event */
221206
$event = $this->eventDispatcher->dispatch(
222-
new CliExecuteEvent('cli.run.student-execute.pre', $this->exercise, $context->getInput(), new ArrayObject($args))
207+
new CliExecuteEvent('cli.run.student-execute.pre', $this->exercise, $context->getInput(), $args)
223208
);
224209

225210
$args = $event->getArgs();

Diff for: test/Asset/CgiExerciseImpl.php

+13-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
88
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
99
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
10+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CgiScenario;
11+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
1012
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
1113
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
1214
use Psr\Http\Message\RequestInterface;
@@ -15,12 +17,12 @@ class CgiExerciseImpl implements ExerciseInterface, CgiExercise
1517
{
1618
private string $name;
1719
private SolutionInterface $solution;
18-
/** @var array<RequestInterface> */
19-
private array $requests = [];
20+
private CgiScenario $scenario;
2021

2122
public function __construct(string $name = 'my-exercise')
2223
{
2324
$this->name = $name;
25+
$this->scenario = new CgiScenario();
2426
}
2527

2628
public function getName(): string
@@ -53,36 +55,27 @@ public function tearDown(): void
5355
// TODO: Implement tearDown() method.
5456
}
5557

56-
/**
57-
* @param array<RequestInterface> $requests
58-
*/
59-
public function setRequests(array $requests): void
58+
public function getType(): ExerciseType
6059
{
61-
$this->requests = $requests;
60+
return ExerciseType::CGI();
6261
}
6362

64-
/**
65-
* This method should return an array of PSR-7 requests, which will be forwarded to the student's
66-
* solution.
67-
*
68-
* @return array<RequestInterface> An array of PSR-7 requests.
69-
*/
70-
public function getRequests(): array
63+
public function getRequiredChecks(): array
7164
{
72-
return $this->requests;
65+
return [];
7366
}
7467

75-
public function getType(): ExerciseType
68+
public function defineListeners(EventDispatcher $dispatcher): void
7669
{
77-
return ExerciseType::CGI();
7870
}
7971

80-
public function getRequiredChecks(): array
72+
public function setScenario(CgiScenario $scenario): void
8173
{
82-
return [];
74+
$this->scenario = $scenario;
8375
}
8476

85-
public function defineListeners(EventDispatcher $dispatcher): void
77+
public function defineTestScenario(): CgiScenario
8678
{
79+
return $this->scenario;
8780
}
8881
}

Diff for: test/Asset/CliExerciseImpl.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
99
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
1010
use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution;
11+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
1112
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
1213
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
1314

1415
class CliExerciseImpl implements ExerciseInterface, CliExercise
1516
{
1617
private string $name;
1718
private SolutionInterface $solution;
18-
private array $args = [[]];
19+
private CliScenario $scenario;
1920

2021
public function __construct(string $name = 'my-exercise')
2122
{
2223
$this->name = $name;
24+
$this->scenario = new CliScenario();
2325
}
2426

2527
public function getName(): string
@@ -52,14 +54,14 @@ public function tearDown(): void
5254
// TODO: Implement tearDown() method.
5355
}
5456

55-
public function setArgs(array $args): void
57+
public function setScenario(CliScenario $scenario): void
5658
{
57-
$this->args = $args;
59+
$this->scenario = $scenario;
5860
}
5961

60-
public function getArgs(): array
62+
public function defineTestScenario(): CliScenario
6163
{
62-
return $this->args;
64+
return $this->scenario;
6365
}
6466

6567
public function getType(): ExerciseType

Diff for: test/Asset/DatabaseExercise.php

+11-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PhpSchool\PhpWorkshop\Exercise\CliExercise;
99
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
1010
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
11+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
1112
use PhpSchool\PhpWorkshop\ExerciseCheck\DatabaseExerciseCheck;
1213
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
1314

@@ -16,7 +17,12 @@ class DatabaseExercise implements ExerciseInterface, DatabaseExerciseCheck, CliE
1617
private SolutionInterface $solution;
1718
private ?\Closure $verifier = null;
1819
private ?\Closure $seeder = null;
19-
private array $args = [[]];
20+
private CliScenario $scenario;
21+
22+
public function __construct()
23+
{
24+
$this->scenario = new CliScenario();
25+
}
2026

2127
public function setSeeder(\Closure $seeder): void
2228
{
@@ -92,13 +98,13 @@ public function getSolution(): SolutionInterface
9298
return $this->solution;
9399
}
94100

95-
public function setArgs(array $args): void
101+
public function setScenario(CliScenario $scenario): void
96102
{
97-
$this->args = $args;
103+
$this->scenario = $scenario;
98104
}
99105

100-
public function getArgs(): array
106+
public function defineTestScenario(): CliScenario
101107
{
102-
return $this->args;
108+
return $this->scenario;
103109
}
104110
}

Diff for: test/Check/DatabaseCheckTest.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpSchool\PhpWorkshop\Event\EventDispatcher;
1010
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
1111
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
12+
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
1213
use PhpSchool\PhpWorkshop\ExerciseCheck\DatabaseExerciseCheck;
1314
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
1415
use PhpSchool\PhpWorkshop\ExerciseRunner\CliRunner;
@@ -111,8 +112,7 @@ public function testIfPDOThrowsExceptionItCleansUp(): void
111112
$this->check = new DatabaseCheck();
112113
$solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/database/solution.php'));
113114
$this->exercise->setSolution($solution);
114-
115-
$this->exercise->setArgs([[1, 2, 3]]);
115+
$this->exercise->setScenario((new CliScenario())->withExecution([1, 2, 3]));
116116
$this->exercise->setVerifier(fn () => true);
117117

118118
$this->checkRepository->registerCheck($this->check);
@@ -139,7 +139,7 @@ public function testSuccessIsReturnedIfDatabaseVerificationPassed(): void
139139
{
140140
$solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/database/solution.php'));
141141
$this->exercise->setSolution($solution);
142-
$this->exercise->setArgs([[1, 2, 3]]);
142+
$this->exercise->setScenario((new CliScenario())->withExecution([1, 2, 3]));
143143

144144
$this->exercise->setVerifier(fn () => true);
145145
$this->checkRepository->registerCheck($this->check);
@@ -191,7 +191,7 @@ public function testFailureIsReturnedIfDatabaseVerificationFails(): void
191191
{
192192
$solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/database/solution.php'));
193193
$this->exercise->setSolution($solution);
194-
$this->exercise->setArgs([[1, 2, 3]]);
194+
$this->exercise->setScenario((new CliScenario())->withExecution([1, 2, 3]));
195195
$this->exercise->setVerifier(fn () => false);
196196

197197
$this->checkRepository->registerCheck($this->check);
@@ -221,6 +221,7 @@ public function testAlteringDatabaseInSolutionDoesNotEffectDatabaseInUserSolutio
221221
{
222222
$solution = SingleFileSolution::fromFile(realpath(__DIR__ . '/../res/database/solution-alter-db.php'));
223223
$this->exercise->setSolution($solution);
224+
$this->exercise->setScenario((new CliScenario())->withExecution());
224225

225226
$this->exercise->setVerifier(function (PDO $db) {
226227
$users = $db->query('SELECT * FROM users');

0 commit comments

Comments
 (0)