Skip to content

Commit e97dcb3

Browse files
committed
Use context objects in dispatcher and runners
1 parent 0964eb3 commit e97dcb3

17 files changed

+277
-278
lines changed

Diff for: app/config.php

+18-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
3939
use PhpSchool\PhpWorkshop\ExerciseRenderer;
4040
use PhpSchool\PhpWorkshop\ExerciseRepository;
41+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContextFactory;
42+
use PhpSchool\PhpWorkshop\ExerciseRunner\EnvironmentManager;
4143
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CgiRunnerFactory;
4244
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CliRunnerFactory;
4345
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CustomVerifyingRunnerFactory;
@@ -134,7 +136,8 @@
134136
$c->get(RunnerManager::class),
135137
$c->get(ResultAggregator::class),
136138
$c->get(EventDispatcher::class),
137-
$c->get(CheckRepository::class)
139+
$c->get(CheckRepository::class),
140+
new ExecutionContextFactory()
138141
);
139142
},
140143
ResultAggregator::class => create(ResultAggregator::class),
@@ -186,11 +189,23 @@
186189
EventDispatcher::class => factory(EventDispatcherFactory::class),
187190
EventDispatcherFactory::class => create(),
188191

192+
EnvironmentManager::class => function (ContainerInterface $c) {
193+
return new EnvironmentManager($c->get(Filesystem::class));
194+
},
195+
189196
//Exercise Runners
190197
RunnerManager::class => function (ContainerInterface $c) {
191198
$manager = new RunnerManager();
192-
$manager->addFactory(new CliRunnerFactory($c->get(EventDispatcher::class), $c->get(ProcessFactory::class)));
193-
$manager->addFactory(new CgiRunnerFactory($c->get(EventDispatcher::class), $c->get(ProcessFactory::class)));
199+
$manager->addFactory(new CliRunnerFactory(
200+
$c->get(EventDispatcher::class),
201+
$c->get(ProcessFactory::class),
202+
$c->get(EnvironmentManager::class)
203+
));
204+
$manager->addFactory(new CgiRunnerFactory(
205+
$c->get(EventDispatcher::class),
206+
$c->get(ProcessFactory::class),
207+
$c->get(EnvironmentManager::class)
208+
));
194209
$manager->addFactory(new CustomVerifyingRunnerFactory());
195210
return $manager;
196211
},

Diff for: src/ExerciseDispatcher.php

+17-35
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PhpSchool\PhpWorkshop\Exception\ExerciseNotConfiguredException;
1515
use PhpSchool\PhpWorkshop\Exception\InvalidArgumentException;
1616
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
17+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
1718
use PhpSchool\PhpWorkshop\ExerciseRunner\RunnerManager;
1819
use PhpSchool\PhpWorkshop\Input\Input;
1920
use PhpSchool\PhpWorkshop\Output\OutputInterface;
@@ -29,49 +30,26 @@ class ExerciseDispatcher
2930
/**
3031
* @var array<SimpleCheckInterface>
3132
*/
32-
private $checksToRunBefore = [];
33+
private array $checksToRunBefore = [];
3334

3435
/**
3536
* @var array<SimpleCheckInterface>
3637
*/
37-
private $checksToRunAfter = [];
38+
private array $checksToRunAfter = [];
3839

39-
/**
40-
* @var RunnerManager
41-
*/
42-
private $runnerManager;
43-
44-
/**
45-
* @var ResultAggregator
46-
*/
47-
private $results;
48-
49-
/**
50-
* @var EventDispatcher
51-
*/
52-
private $eventDispatcher;
53-
54-
/**
55-
* @var CheckRepository
56-
*/
57-
private $checkRepository;
5840

5941
/**
6042
* @param RunnerManager $runnerManager Factory capable of building an exercise runner based on the exercise type.
61-
* @param ResultAggregator $resultAggregator
43+
* @param ResultAggregator $results
6244
* @param EventDispatcher $eventDispatcher
6345
* @param CheckRepository $checkRepository
6446
*/
6547
public function __construct(
66-
RunnerManager $runnerManager,
67-
ResultAggregator $resultAggregator,
68-
EventDispatcher $eventDispatcher,
69-
CheckRepository $checkRepository
48+
private RunnerManager $runnerManager,
49+
private ResultAggregator $results,
50+
private EventDispatcher $eventDispatcher,
51+
private CheckRepository $checkRepository,
7052
) {
71-
$this->runnerManager = $runnerManager;
72-
$this->results = $resultAggregator;
73-
$this->eventDispatcher = $eventDispatcher;
74-
$this->checkRepository = $checkRepository;
7553
}
7654

7755
/**
@@ -129,6 +107,8 @@ public function requireCheck(string $requiredCheck): void
129107
*/
130108
public function verify(ExerciseInterface $exercise, Input $input): ResultAggregator
131109
{
110+
$context = ExecutionContext::fromInputAndExercise($input, $exercise);
111+
132112
$runner = $this->runnerManager->getRunner($exercise);
133113

134114
$exercise->defineListeners($this->eventDispatcher);
@@ -143,7 +123,7 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
143123
$this->validateChecks($this->checksToRunAfter, $exercise);
144124

145125
foreach ($this->checksToRunBefore as $check) {
146-
$this->results->add($check->check($exercise, $input));
126+
$this->results->add($check->check($context->getExercise(), $context->getInput()));
147127

148128
if (!$this->results->isSuccessful()) {
149129
return $this->results;
@@ -153,13 +133,13 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
153133
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.pre.execute', $exercise, $input));
154134

155135
try {
156-
$this->results->add($runner->verify($input));
136+
$this->results->add($runner->verify($context));
157137
} finally {
158138
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.execute', $exercise, $input));
159139
}
160140

161141
foreach ($this->checksToRunAfter as $check) {
162-
$this->results->add($check->check($exercise, $input));
142+
$this->results->add($check->check($context->getExercise(), $context->getInput()));
163143
}
164144

165145
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.check', $exercise, $input));
@@ -181,11 +161,13 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
181161
*/
182162
public function run(ExerciseInterface $exercise, Input $input, OutputInterface $output): bool
183163
{
164+
$context = ExecutionContext::fromInputAndExercise($input, $exercise);
165+
184166
$exercise->defineListeners($this->eventDispatcher);
185167

186168
/** @var PhpLintCheck $lint */
187169
$lint = $this->checkRepository->getByClass(PhpLintCheck::class);
188-
$result = $lint->check($exercise, $input);
170+
$result = $lint->check($context->getExercise(), $context->getInput());
189171

190172
if ($result instanceof FailureInterface) {
191173
throw CouldNotRunException::fromFailure($result);
@@ -196,7 +178,7 @@ public function run(ExerciseInterface $exercise, Input $input, OutputInterface $
196178
try {
197179
$exitStatus = $this->runnerManager
198180
->getRunner($exercise)
199-
->run($input, $output);
181+
->run($context, $output);
200182
} finally {
201183
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('run.finish', $exercise, $input));
202184
}

Diff for: src/ExerciseRunner/CgiRunner.php

+43-27
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PhpSchool\PhpWorkshop\Exception\SolutionExecutionException;
1919
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
2020
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
21+
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
2122
use PhpSchool\PhpWorkshop\Input\Input;
2223
use PhpSchool\PhpWorkshop\Output\OutputInterface;
2324
use PhpSchool\PhpWorkshop\Process\ProcessFactory;
@@ -63,7 +64,8 @@ class CgiRunner implements ExerciseRunnerInterface
6364
public function __construct(
6465
private CgiExercise $exercise,
6566
private EventDispatcher $eventDispatcher,
66-
private ProcessFactory $processFactory
67+
private ProcessFactory $processFactory,
68+
private EnvironmentManager $environmentManager
6769
) {
6870
}
6971

@@ -99,36 +101,43 @@ public function getRequiredChecks(): array
99101
* * cgi.verify.student.executing
100102
* * cgi.verify.student-execute.fail (if the student's solution fails to execute)
101103
*
102-
* @param Input $input The command line arguments passed to the command.
104+
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
103105
* @return CgiResult The result of the check.
104106
*/
105-
public function verify(Input $input): ResultInterface
107+
public function verify(ExecutionContext $context): ResultInterface
106108
{
107109
$scenario = $this->exercise->defineTestScenario();
108110

109-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $input));
111+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $context->getInput()));
112+
113+
$this->environmentManager->prepareStudent($context, $scenario);
114+
$this->environmentManager->prepareReference($context, $scenario);
110115

111116
$result = new CgiResult(
112117
array_map(
113-
function (RequestInterface $request) use ($input) {
114-
return $this->doVerify($request, $input);
118+
function (RequestInterface $request) use ($context) {
119+
return $this->doVerify($request, $context);
115120
},
116121
$scenario->getExecutions()
117122
)
118123
);
119-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $input));
124+
125+
$this->environmentManager->cleanup($context, $scenario);
126+
127+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $context->getInput()));
120128
return $result;
121129
}
122130

123-
private function doVerify(RequestInterface $request, Input $input): CgiResultInterface
131+
private function doVerify(RequestInterface $request, ExecutionContext $context): CgiResultInterface
124132
{
125133
try {
126134
/** @var CgiExecuteEvent $event */
127135
$event = $this->eventDispatcher->dispatch(
128-
new CgiExecuteEvent('cgi.verify.reference-execute.pre', $this->exercise, $input, $request)
136+
new CgiExecuteEvent('cgi.verify.reference-execute.pre', $this->exercise, $context->getInput(), $request)
129137
);
130138
$solutionResponse = $this->executePhpFile(
131-
$input,
139+
$context,
140+
$context->getReferenceExecutionDirectory(),
132141
$this->exercise->getSolution()->getEntryPoint()->getAbsolutePath(),
133142
$event->getRequest(),
134143
'reference'
@@ -138,7 +147,7 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
138147
new CgiExecuteEvent(
139148
'cgi.verify.reference-execute.fail',
140149
$this->exercise,
141-
$input,
150+
$context->getInput(),
142151
$request,
143152
['exception' => $e]
144153
)
@@ -149,11 +158,12 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
149158
try {
150159
/** @var CgiExecuteEvent $event */
151160
$event = $this->eventDispatcher->dispatch(
152-
new CgiExecuteEvent('cgi.verify.student-execute.pre', $this->exercise, $input, $request)
161+
new CgiExecuteEvent('cgi.verify.student-execute.pre', $this->exercise, $context->getInput(), $request)
153162
);
154163
$userResponse = $this->executePhpFile(
155-
$input,
156-
$input->getRequiredArgument('program'),
164+
$context,
165+
$context->getStudentExecutionDirectory(),
166+
$context->getEntryPoint(),
157167
$event->getRequest(),
158168
'student'
159169
);
@@ -162,7 +172,7 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
162172
new CgiExecuteEvent(
163173
'cgi.verify.student-execute.fail',
164174
$this->exercise,
165-
$input,
175+
$context->getInput(),
166176
$request,
167177
['exception' => $e]
168178
)
@@ -202,16 +212,17 @@ private function getHeaders(ResponseInterface $response): array
202212
* @return ResponseInterface
203213
*/
204214
private function executePhpFile(
205-
Input $input,
215+
ExecutionContext $context,
216+
string $workingDirectory,
206217
string $fileName,
207218
RequestInterface $request,
208219
string $type
209220
): ResponseInterface {
210-
$process = $this->getPhpProcess(dirname($fileName), basename($fileName), $request);
221+
$process = $this->getPhpProcess($workingDirectory, $fileName, $request);
211222

212223
$process->start();
213224
$this->eventDispatcher->dispatch(
214-
new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $this->exercise, $input, $request)
225+
new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $this->exercise, $context->getInput(), $request)
215226
);
216227
$process->wait();
217228

@@ -280,25 +291,27 @@ private function getPhpProcess(string $workingDirectory, string $fileName, Reque
280291
* * cgi.run.student-execute.pre
281292
* * cgi.run.student.executing
282293
*
283-
* @param Input $input The command line arguments passed to the command.
294+
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
284295
* @param OutputInterface $output A wrapper around STDOUT.
285296
* @return bool If the solution was successfully executed, eg. exit code was 0.
286297
*/
287-
public function run(Input $input, OutputInterface $output): bool
298+
public function run(ExecutionContext $context, OutputInterface $output): bool
288299
{
289300
$scenario = $this->exercise->defineTestScenario();
290301

291-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $input));
302+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $context->getInput()));
303+
304+
$this->environmentManager->prepareStudent($context, $scenario);
292305

293306
$success = true;
294307
foreach ($scenario->getExecutions() as $i => $request) {
295308
/** @var CgiExecuteEvent $event */
296309
$event = $this->eventDispatcher->dispatch(
297-
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $input, $request)
310+
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $context->getInput(), $request)
298311
);
299312
$process = $this->getPhpProcess(
300-
dirname($input->getRequiredArgument('program')),
301-
$input->getRequiredArgument('program'),
313+
$context->getStudentExecutionDirectory(),
314+
$context->getEntryPoint(),
302315
$event->getRequest()
303316
);
304317

@@ -307,7 +320,7 @@ public function run(Input $input, OutputInterface $output): bool
307320
new CgiExecuteEvent(
308321
'cgi.run.student.executing',
309322
$this->exercise,
310-
$input,
323+
$context->getInput(),
311324
$request,
312325
['output' => $output]
313326
)
@@ -324,10 +337,13 @@ public function run(Input $input, OutputInterface $output): bool
324337
$output->lineBreak();
325338

326339
$this->eventDispatcher->dispatch(
327-
new CgiExecuteEvent('cgi.run.student-execute.post', $this->exercise, $input, $request)
340+
new CgiExecuteEvent('cgi.run.student-execute.post', $this->exercise, $context->getInput(), $request)
328341
);
329342
}
330-
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.finish', $this->exercise, $input));
343+
344+
$this->environmentManager->cleanup($context, $scenario);
345+
346+
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.finish', $this->exercise, $context->getInput()));
331347
return $success;
332348
}
333349
}

0 commit comments

Comments
 (0)