Skip to content

Commit

Permalink
Use context objects in dispatcher and runners
Browse files Browse the repository at this point in the history
  • Loading branch information
AydinHassan committed May 9, 2024
1 parent 662e454 commit d2ddc76
Show file tree
Hide file tree
Showing 14 changed files with 499 additions and 534 deletions.
4 changes: 3 additions & 1 deletion app/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
use PhpSchool\PhpWorkshop\ExerciseRenderer;
use PhpSchool\PhpWorkshop\ExerciseRepository;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContextFactory;
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CgiRunnerFactory;
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CliRunnerFactory;
use PhpSchool\PhpWorkshop\ExerciseRunner\Factory\CustomVerifyingRunnerFactory;
Expand Down Expand Up @@ -134,7 +135,8 @@
$c->get(RunnerManager::class),
$c->get(ResultAggregator::class),
$c->get(EventDispatcher::class),
$c->get(CheckRepository::class)
$c->get(CheckRepository::class),
new ExecutionContextFactory()
);
},
ResultAggregator::class => create(ResultAggregator::class),
Expand Down
53 changes: 18 additions & 35 deletions src/ExerciseDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PhpSchool\PhpWorkshop\Exception\ExerciseNotConfiguredException;
use PhpSchool\PhpWorkshop\Exception\InvalidArgumentException;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContextFactory;
use PhpSchool\PhpWorkshop\ExerciseRunner\RunnerManager;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Output\OutputInterface;
Expand All @@ -29,49 +30,27 @@ class ExerciseDispatcher
/**
* @var array<SimpleCheckInterface>
*/
private $checksToRunBefore = [];
private array $checksToRunBefore = [];

/**
* @var array<SimpleCheckInterface>
*/
private $checksToRunAfter = [];
private array $checksToRunAfter = [];

/**
* @var RunnerManager
*/
private $runnerManager;

/**
* @var ResultAggregator
*/
private $results;

/**
* @var EventDispatcher
*/
private $eventDispatcher;

/**
* @var CheckRepository
*/
private $checkRepository;

/**
* @param RunnerManager $runnerManager Factory capable of building an exercise runner based on the exercise type.
* @param ResultAggregator $resultAggregator
* @param ResultAggregator $results
* @param EventDispatcher $eventDispatcher
* @param CheckRepository $checkRepository
*/
public function __construct(
RunnerManager $runnerManager,
ResultAggregator $resultAggregator,
EventDispatcher $eventDispatcher,
CheckRepository $checkRepository
private RunnerManager $runnerManager,
private ResultAggregator $results,
private EventDispatcher $eventDispatcher,
private CheckRepository $checkRepository,
private ExecutionContextFactory $executionContextFactory,
) {
$this->runnerManager = $runnerManager;
$this->results = $resultAggregator;
$this->eventDispatcher = $eventDispatcher;
$this->checkRepository = $checkRepository;
}

/**
Expand Down Expand Up @@ -129,6 +108,7 @@ public function requireCheck(string $requiredCheck): void
*/
public function verify(ExerciseInterface $exercise, Input $input): ResultAggregator
{
$context = $this->executionContextFactory->fromInputAndExercise($input, $exercise);
$runner = $this->runnerManager->getRunner($exercise);

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

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

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

try {
$this->results->add($runner->verify($input));
$this->results->add($runner->verify($context));
} finally {
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.execute', $exercise, $input));
}

foreach ($this->checksToRunAfter as $check) {
$this->results->add($check->check($exercise, $input));
$this->results->add($check->check($context->getExercise(), $context->getInput()));
}

$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.check', $exercise, $input));
Expand All @@ -181,13 +161,16 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
*/
public function run(ExerciseInterface $exercise, Input $input, OutputInterface $output): bool
{
$context = $this->executionContextFactory->fromInputAndExercise($input, $exercise);

$exercise->defineListeners($this->eventDispatcher);

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

if ($result instanceof FailureInterface) {
var_dump($result);
throw CouldNotRunException::fromFailure($result);
}

Expand All @@ -196,7 +179,7 @@ public function run(ExerciseInterface $exercise, Input $input, OutputInterface $
try {
$exitStatus = $this->runnerManager
->getRunner($exercise)
->run($input, $output);
->run($context, $output);
} finally {
$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('run.finish', $exercise, $input));
}
Expand Down
56 changes: 30 additions & 26 deletions src/ExerciseRunner/CgiRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use PhpSchool\PhpWorkshop\Exception\SolutionExecutionException;
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Output\OutputInterface;
use PhpSchool\PhpWorkshop\Process\ProcessFactory;
Expand Down Expand Up @@ -99,33 +100,34 @@ public function getRequiredChecks(): array
* * cgi.verify.student.executing
* * cgi.verify.student-execute.fail (if the student's solution fails to execute)
*
* @param Input $input The command line arguments passed to the command.
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
* @return CgiResult The result of the check.
*/
public function verify(Input $input): ResultInterface
public function verify(ExecutionContext $context): ResultInterface
{
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $input));
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $context->getInput()));
$result = new CgiResult(
array_map(
function (RequestInterface $request) use ($input) {
return $this->doVerify($request, $input);
function (RequestInterface $request) use ($context) {
return $this->doVerify($request, $context);
},
$this->exercise->getRequests()
)
);
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $input));
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $context->getInput()));
return $result;
}

private function doVerify(RequestInterface $request, Input $input): CgiResultInterface
private function doVerify(RequestInterface $request, ExecutionContext $context): CgiResultInterface
{
try {
/** @var CgiExecuteEvent $event */
$event = $this->eventDispatcher->dispatch(
new CgiExecuteEvent('cgi.verify.reference-execute.pre', $this->exercise, $input, $request)
new CgiExecuteEvent('cgi.verify.reference-execute.pre', $this->exercise, $context->getInput(), $request)
);
$solutionResponse = $this->executePhpFile(
$input,
$context,
$context->getReferenceExecutionDirectory(),
$this->exercise->getSolution()->getEntryPoint()->getAbsolutePath(),
$event->getRequest(),
'reference'
Expand All @@ -135,7 +137,7 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
new CgiExecuteEvent(
'cgi.verify.reference-execute.fail',
$this->exercise,
$input,
$context->getInput(),
$request,
['exception' => $e]
)
Expand All @@ -146,11 +148,12 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
try {
/** @var CgiExecuteEvent $event */
$event = $this->eventDispatcher->dispatch(
new CgiExecuteEvent('cgi.verify.student-execute.pre', $this->exercise, $input, $request)
new CgiExecuteEvent('cgi.verify.student-execute.pre', $this->exercise, $context->getInput(), $request)
);
$userResponse = $this->executePhpFile(
$input,
$input->getRequiredArgument('program'),
$context,
$context->getStudentExecutionDirectory(),
$context->getEntryPoint(),
$event->getRequest(),
'student'
);
Expand All @@ -159,7 +162,7 @@ private function doVerify(RequestInterface $request, Input $input): CgiResultInt
new CgiExecuteEvent(
'cgi.verify.student-execute.fail',
$this->exercise,
$input,
$context->getInput(),
$request,
['exception' => $e]
)
Expand Down Expand Up @@ -199,16 +202,17 @@ private function getHeaders(ResponseInterface $response): array
* @return ResponseInterface
*/
private function executePhpFile(
Input $input,
ExecutionContext $context,
string $workingDirectory,
string $fileName,
RequestInterface $request,
string $type
): ResponseInterface {
$process = $this->getPhpProcess(dirname($fileName), basename($fileName), $request);
$process = $this->getPhpProcess($workingDirectory, $fileName, $request);

$process->start();
$this->eventDispatcher->dispatch(
new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $this->exercise, $input, $request)
new CgiExecuteEvent(sprintf('cgi.verify.%s.executing', $type), $this->exercise, $context->getInput(), $request)
);
$process->wait();

Expand Down Expand Up @@ -277,22 +281,22 @@ private function getPhpProcess(string $workingDirectory, string $fileName, Reque
* * cgi.run.student-execute.pre
* * cgi.run.student.executing
*
* @param Input $input The command line arguments passed to the command.
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
* @param OutputInterface $output A wrapper around STDOUT.
* @return bool If the solution was successfully executed, eg. exit code was 0.
*/
public function run(Input $input, OutputInterface $output): bool
public function run(ExecutionContext $context, OutputInterface $output): bool
{
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $input));
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $context->getInput()));
$success = true;
foreach ($this->exercise->getRequests() as $i => $request) {
/** @var CgiExecuteEvent $event */
$event = $this->eventDispatcher->dispatch(
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $input, $request)
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $context->getInput(), $request)
);
$process = $this->getPhpProcess(
dirname($input->getRequiredArgument('program')),
$input->getRequiredArgument('program'),
$context->getStudentExecutionDirectory(),
$context->getEntryPoint(),
$event->getRequest()
);

Expand All @@ -301,7 +305,7 @@ public function run(Input $input, OutputInterface $output): bool
new CgiExecuteEvent(
'cgi.run.student.executing',
$this->exercise,
$input,
$context->getInput(),
$request,
['output' => $output]
)
Expand All @@ -318,10 +322,10 @@ public function run(Input $input, OutputInterface $output): bool
$output->lineBreak();

$this->eventDispatcher->dispatch(
new CgiExecuteEvent('cgi.run.student-execute.post', $this->exercise, $input, $request)
new CgiExecuteEvent('cgi.run.student-execute.post', $this->exercise, $context->getInput(), $request)
);
}
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.finish', $this->exercise, $input));
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.finish', $this->exercise, $context->getInput()));
return $success;
}
}
Loading

0 comments on commit d2ddc76

Please sign in to comment.