Skip to content

Use context in checks #290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading