Skip to content

Commit

Permalink
Use context in checks
Browse files Browse the repository at this point in the history
  • Loading branch information
AydinHassan committed May 17, 2024
1 parent bb84725 commit 4af4474
Show file tree
Hide file tree
Showing 18 changed files with 240 additions and 301 deletions.
15 changes: 6 additions & 9 deletions src/Check/CodeExistsCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,16 @@
use PhpParser\Parser;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\Failure;
use PhpSchool\PhpWorkshop\Result\ResultInterface;
use PhpSchool\PhpWorkshop\Result\Success;

class CodeExistsCheck implements SimpleCheckInterface
{
/**
* @var Parser
*/
private $parser;

public function __construct(Parser $parser)
public function __construct(private Parser $parser)
{
$this->parser = $parser;
}

public function getName(): string
Expand All @@ -34,18 +29,20 @@ public function getName(): string
}

/**
* @param ExecutionContext $context The current execution context, containing the exercise, input and working directories.
*
* Check solution provided contains code
* Note: We don't care if it's valid code at this point
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
$noopHandler = new class implements ErrorHandler {
public function handleError(Error $error): void
{
}
};

$code = (string) file_get_contents($input->getRequiredArgument('program'));
$code = (string) file_get_contents($context->getEntryPoint());
$statements = $this->parser->parse($code, $noopHandler);

$empty = null === $statements || empty($statements);
Expand Down
21 changes: 6 additions & 15 deletions src/Check/CodeParseCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PhpParser\Parser;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\Failure;
use PhpSchool\PhpWorkshop\Result\ResultInterface;
Expand All @@ -19,17 +20,8 @@
*/
class CodeParseCheck implements SimpleCheckInterface
{
/**
* @var Parser
*/
private $parser;

/**
* @param Parser $parser
*/
public function __construct(Parser $parser)
public function __construct(private Parser $parser)
{
$this->parser = $parser;
}

/**
Expand All @@ -45,18 +37,17 @@ public function getName(): string
* attempts to parse it with `nikic/php-parser`. If any exceptions are thrown
* by the parser, it is treated as a failure.
*
* @param ExerciseInterface $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
$code = (string) file_get_contents($input->getRequiredArgument('program'));
$code = (string) file_get_contents($context->getEntryPoint());

try {
$this->parser->parse($code);
} catch (Error $e) {
return Failure::fromCheckAndCodeParseFailure($this, $e, $input->getRequiredArgument('program'));
return Failure::fromCheckAndCodeParseFailure($this, $e, $context->getEntryPoint());
}

return Success::fromCheck($this);
Expand Down
16 changes: 8 additions & 8 deletions src/Check/ComposerCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseCheck\ComposerExerciseCheck;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\ComposerFailure;
use PhpSchool\PhpWorkshop\Result\Failure;
Expand All @@ -34,30 +35,29 @@ public function getName(): string
* installed a set of required packages. If they did not a failure is returned, otherwise,
* a success is returned.
*
* @param ExerciseInterface $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
* @noinspection SpellCheckingInspection
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
$exercise = $context->getExercise();
if (!$exercise instanceof ComposerExerciseCheck) {
throw new InvalidArgumentException();
}

if (!file_exists(sprintf('%s/composer.json', dirname($input->getRequiredArgument('program'))))) {
if (!file_exists(sprintf('%s/composer.json', $context->getStudentExecutionDirectory()))) {
return ComposerFailure::fromCheckAndMissingFileOrFolder($this, 'composer.json');
}

if (!file_exists(sprintf('%s/composer.lock', dirname($input->getRequiredArgument('program'))))) {
if (!file_exists(sprintf('%s/composer.lock', $context->getStudentExecutionDirectory()))) {
return ComposerFailure::fromCheckAndMissingFileOrFolder($this, 'composer.lock');
}

if (!file_exists(sprintf('%s/vendor', dirname($input->getRequiredArgument('program'))))) {
if (!file_exists(sprintf('%s/vendor', $context->getStudentExecutionDirectory()))) {
return ComposerFailure::fromCheckAndMissingFileOrFolder($this, 'vendor');
}

$lockFile = new LockFileParser(sprintf('%s/composer.lock', dirname($input->getRequiredArgument('program'))));
$lockFile = new LockFileParser(sprintf('%s/composer.lock', $context->getStudentExecutionDirectory()));
$missingPackages = array_filter($exercise->getRequiredPackages(), function ($package) use ($lockFile) {
return !$lockFile->hasInstalledPackage($package);
});
Expand Down
29 changes: 5 additions & 24 deletions src/Check/DatabaseCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,11 @@ class DatabaseCheck implements ListenableCheckInterface
{
use TemporaryDirectoryTrait;

/**
* @var string
*/
private $databaseDirectory;

/**
* @var string
*/
private $userDatabasePath;

/**
* @var string
*/
private $solutionDatabasePath;

/**
* @var string
*/
private $userDsn;

/**
* @var string
*/
private $solutionDsn;
private string $databaseDirectory;
private string $userDatabasePath;
private string $solutionDatabasePath;
private string $userDsn;
private string $solutionDsn;

/**
* Setup paths and DSN's.
Expand Down
11 changes: 6 additions & 5 deletions src/Check/FileComparisonCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution;
use PhpSchool\PhpWorkshop\ExerciseCheck\FileComparisonExerciseCheck;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\Failure;
use PhpSchool\PhpWorkshop\Result\FileComparisonFailure;
Expand All @@ -34,21 +35,21 @@ public function getName(): string
/**
* Simply check that the file exists.
*
* @param ExerciseInterface&ProvidesSolution $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
$exercise = $context->getExercise();
if (!$exercise instanceof FileComparisonExerciseCheck) {
throw new InvalidArgumentException();
}

foreach ($exercise->getFilesToCompare() as $key => $file) {
[$options, $file] = $this->getOptionsAndFile($key, $file);

$studentFile = Path::join(dirname($input->getRequiredArgument('program')), $file);
$referenceFile = Path::join($exercise->getSolution()->getBaseDirectory(), $file);
$studentFile = Path::join($context->getStudentExecutionDirectory(), $file);
$referenceFile = Path::join($context->getReferenceExecutionDirectory(), $file);

if (!file_exists($referenceFile)) {
throw SolutionFileDoesNotExistException::fromExpectedFile($file);
Expand Down
10 changes: 5 additions & 5 deletions src/Check/FileExistsCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\Failure;
use PhpSchool\PhpWorkshop\Result\ResultInterface;
Expand All @@ -27,19 +28,18 @@ public function getName(): string
/**
* Simply check that the file exists.
*
* @param ExerciseInterface $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
if (file_exists($input->getRequiredArgument('program'))) {
if (file_exists($context->getEntryPoint())) {
return Success::fromCheck($this);
}

return Failure::fromCheckAndReason(
$this,
sprintf('File: "%s" does not exist', $input->getRequiredArgument('program'))
sprintf('File: "%s" does not exist', $context->getEntryPoint())
);
}

Expand Down
22 changes: 7 additions & 15 deletions src/Check/FunctionRequirementsCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseCheck\FunctionRequirementsExerciseCheck;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\NodeVisitor\FunctionVisitor;
use PhpSchool\PhpWorkshop\Result\Failure;
Expand All @@ -25,17 +26,8 @@
*/
class FunctionRequirementsCheck implements SimpleCheckInterface
{
/**
* @var Parser
*/
private $parser;

/**
* @param Parser $parser
*/
public function __construct(Parser $parser)
public function __construct(private Parser $parser)
{
$this->parser = $parser;
}

/**
Expand All @@ -51,25 +43,25 @@ public function getName(): string
* required functions and that banned functions are not used. The requirements
* are pulled from the exercise.
*
* @param ExerciseInterface $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
$exercise = $context->getExercise();
if (!$exercise instanceof FunctionRequirementsExerciseCheck) {
throw new InvalidArgumentException();
}

$requiredFunctions = $exercise->getRequiredFunctions();
$bannedFunctions = $exercise->getBannedFunctions();

$code = (string) file_get_contents($input->getRequiredArgument('program'));
$code = (string) file_get_contents($context->getEntryPoint());

try {
$ast = $this->parser->parse($code) ?? [];
} catch (Error $e) {
return Failure::fromCheckAndCodeParseFailure($this, $e, $input->getRequiredArgument('program'));
return Failure::fromCheckAndCodeParseFailure($this, $e, $context->getEntryPoint());
}

$visitor = new FunctionVisitor($requiredFunctions, $bannedFunctions);
Expand Down
8 changes: 4 additions & 4 deletions src/Check/PhpLintCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\ResultInterface;
use PhpSchool\PhpWorkshop\Result\Success;
Expand All @@ -30,14 +31,13 @@ public function getName(): string
/**
* Simply check the student's solution can be linted with `php -l`.
*
* @param ExerciseInterface $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
public function check(ExecutionContext $context): ResultInterface
{
$finder = new ExecutableFinder();
$process = new Process([$finder->find('php'), '-l', $input->getArgument('program')]);
$process = new Process([$finder->find('php'), '-l', $context->getEntryPoint()]);
$process->run();

if ($process->isSuccessful()) {
Expand Down
6 changes: 3 additions & 3 deletions src/Check/SimpleCheckInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseRunner\Context\ExecutionContext;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\ResultInterface;

Expand Down Expand Up @@ -46,11 +47,10 @@ public function canRun(ExerciseType $exerciseType): bool;
* successful then an instance of `PhpSchool\PhpWorkshop\Result\FailureInterface`
* should be returned.
*
* @param ExerciseInterface $exercise The exercise to check against.
* @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 ResultInterface The result of the check.
*/
public function check(ExerciseInterface $exercise, Input $input): ResultInterface;
public function check(ExecutionContext $context): ResultInterface;

/**
* Either `static::CHECK_BEFORE` | `static::CHECK_AFTER`.
Expand Down
6 changes: 3 additions & 3 deletions src/ExerciseDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,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($context->getExercise(), $context->getInput()));
$this->results->add($check->check($context));

if (!$this->results->isSuccessful()) {
return $this->results;
Expand All @@ -139,7 +139,7 @@ public function verify(ExerciseInterface $exercise, Input $input): ResultAggrega
}

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

$this->eventDispatcher->dispatch(new ExerciseRunnerEvent('verify.post.check', $exercise, $input));
Expand Down Expand Up @@ -167,7 +167,7 @@ public function run(ExerciseInterface $exercise, Input $input, OutputInterface $

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

if ($result instanceof FailureInterface) {
throw CouldNotRunException::fromFailure($result);
Expand Down
Loading

0 comments on commit 4af4474

Please sign in to comment.