Skip to content

Use scenarios in exercises #291

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
3 changes: 0 additions & 3 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
parameters:
treatPhpDocTypesAsCertain: false
ignoreErrors:
-
message: '#PhpSchool\\PhpWorkshop\\ExerciseRunner\\CliRunner\:\:preserveOldArgFormat\(\) should return#'
path: src/ExerciseRunner/CliRunner.php
-
message: '#Call to an undefined method PhpParser\\Node\\Expr\|PhpParser\\Node\\Name\:\:__toString\(\)#'
path: src/Check/FunctionRequirementsCheck.php
Expand Down
14 changes: 10 additions & 4 deletions src/Exercise/CgiExercise.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PhpSchool\PhpWorkshop\Exercise;

use PhpSchool\PhpWorkshop\Exercise\Scenario\CgiScenario;
use Psr\Http\Message\RequestInterface;

/**
Expand All @@ -12,10 +13,15 @@
interface CgiExercise extends ProvidesSolution
{
/**
* This method should return an array of PSR-7 requests, which will be forwarded to the student's
* solution.
* This method should return an instance of CgiScenario which contains PSR-7 requests,
* which will be forwarded to the student's solution.
*
* @return array<RequestInterface> An array of PSR-7 requests.
* Use like so:
*
* ```
* return (new CgiScenario())
* ->withExecution($request1)
* ```
*/
public function getRequests(): array;
public function defineTestScenario(): CgiScenario;
}
16 changes: 12 additions & 4 deletions src/Exercise/CliExercise.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@

namespace PhpSchool\PhpWorkshop\Exercise;

use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;

/**
* This interface describes the additional methods a CLI type exercise should implement.
*/
interface CliExercise extends ProvidesSolution
{
/**
* This method should return an array of an array of strings.
* Each set of arguments will be passed to the students solution as command line arguments.
* This method should return an instance of CliScenario which contains sets of arguments,
* which will be passed to the students solution as command line arguments.
*
* Use like so:
*
* @return array<array<string>> An array of string arguments.
* ```
* return (new CliScenario())
* ->withExecution(['arg1', 'arg2'])
* ->withExecution(['round2-arg1', 'round2-arg2'])
* ```
*/
public function getArgs(): array;
public function defineTestScenario(): CliScenario;
}
10 changes: 8 additions & 2 deletions src/ExerciseRunner/CgiRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,16 @@ public function getRequiredChecks(): array
*/
public function verify(Input $input): ResultInterface
{
$scenario = $this->exercise->defineTestScenario();

$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.start', $this->exercise, $input));

$result = new CgiResult(
array_map(
function (RequestInterface $request) use ($input) {
return $this->doVerify($request, $input);
},
$this->exercise->getRequests()
$scenario->getExecutions()
)
);
$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.verify.finish', $this->exercise, $input));
Expand Down Expand Up @@ -283,9 +286,12 @@ private function getPhpProcess(string $workingDirectory, string $fileName, Reque
*/
public function run(Input $input, OutputInterface $output): bool
{
$scenario = $this->exercise->defineTestScenario();

$this->eventDispatcher->dispatch(new CgiExerciseRunnerEvent('cgi.run.start', $this->exercise, $input));

$success = true;
foreach ($this->exercise->getRequests() as $i => $request) {
foreach ($scenario->getExecutions() as $i => $request) {
/** @var CgiExecuteEvent $event */
$event = $this->eventDispatcher->dispatch(
new CgiExecuteEvent('cgi.run.student-execute.pre', $this->exercise, $input, $request)
Expand Down
39 changes: 12 additions & 27 deletions src/ExerciseRunner/CliRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,46 +101,28 @@ public function getRequiredChecks(): array
*/
public function verify(Input $input): ResultInterface
{
$scenario = $this->exercise->defineTestScenario();

$this->eventDispatcher->dispatch(new CliExerciseRunnerEvent('cli.verify.start', $this->exercise, $input));
$result = new CliResult(
array_map(
function (array $args) use ($input) {
return $this->doVerify($args, $input);
function (Collection $args) use ($input) {
return $this->doVerify($input, $args);
},
$this->preserveOldArgFormat($this->exercise->getArgs())
$scenario->getExecutions()
)
);
$this->eventDispatcher->dispatch(new CliExerciseRunnerEvent('cli.verify.finish', $this->exercise, $input));
return $result;
}

/**
* BC - getArgs only returned 1 set of args in v1 instead of multiple sets of args in v2
*
* @param array<int, array<string>>|array<int, string> $args
* @return array<int, array<string>>
*/
private function preserveOldArgFormat(array $args): array
{
if (isset($args[0]) && !is_array($args[0])) {
$args = [$args];
} elseif (count($args) === 0) {
$args = [[]];
}

return $args;
}

/**
* @param array<string> $args
* @param Input $input
* @param Collection<int, string> $args
* @return CliResultInterface
*/
private function doVerify(array $args, Input $input): CliResultInterface
private function doVerify(Input $input, Collection $args): CliResultInterface
{
//arrays are not pass-by-ref
$args = new ArrayObject($args);

try {
/** @var CliExecuteEvent $event */
$event = $this->eventDispatcher->dispatch(
Expand Down Expand Up @@ -213,12 +195,15 @@ private function doVerify(array $args, Input $input): CliResultInterface
*/
public function run(Input $input, OutputInterface $output): bool
{
$scenario = $this->exercise->defineTestScenario();

$this->eventDispatcher->dispatch(new CliExerciseRunnerEvent('cli.run.start', $this->exercise, $input));

$success = true;
foreach ($this->preserveOldArgFormat($this->exercise->getArgs()) as $i => $args) {
foreach ($scenario->getExecutions() as $i => $args) {
/** @var CliExecuteEvent $event */
$event = $this->eventDispatcher->dispatch(
new CliExecuteEvent('cli.run.student-execute.pre', $this->exercise, $input, new ArrayObject($args))
new CliExecuteEvent('cli.run.student-execute.pre', $this->exercise, $input, $args)
);

$args = $event->getArgs();
Expand Down
38 changes: 21 additions & 17 deletions test/Asset/CgiExerciseImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

namespace PhpSchool\PhpWorkshopTest\Asset;

use PhpSchool\PhpWorkshop\Check\FileComparisonCheck;
use PhpSchool\PhpWorkshop\Event\EventDispatcher;
use PhpSchool\PhpWorkshop\Exercise\CgiExercise;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\Exercise\Scenario\CgiScenario;
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;
use Psr\Http\Message\RequestInterface;

class CgiExerciseImpl implements ExerciseInterface, CgiExercise
{
/**
* @var string
*/
private $name;
private string $name;
private SolutionInterface $solution;
private CgiScenario $scenario;

public function __construct(string $name = 'my-exercise')
{
$this->name = $name;
$this->scenario = new CgiScenario();
}

public function getName(): string
Expand All @@ -33,9 +33,14 @@ public function getDescription(): string
return $this->name;
}

public function setSolution(SolutionInterface $solution): void
{
$this->solution = $solution;
}

public function getSolution(): SolutionInterface
{
// TODO: Implement getSolution() method.
return $this->solution;
}

public function getProblem(): string
Expand All @@ -48,17 +53,6 @@ public function tearDown(): void
// TODO: Implement tearDown() method.
}

/**
* This method should return an array of PSR-7 requests, which will be forwarded to the student's
* solution.
*
* @return RequestInterface[] An array of PSR-7 requests.
*/
public function getRequests(): array
{
return []; // TODO: Implement getRequests() method.
}

public function getType(): ExerciseType
{
return ExerciseType::CGI();
Expand All @@ -72,4 +66,14 @@ public function getRequiredChecks(): array
public function defineListeners(EventDispatcher $dispatcher): void
{
}

public function setScenario(CgiScenario $scenario): void
{
$this->scenario = $scenario;
}

public function defineTestScenario(): CgiScenario
{
return $this->scenario;
}
}
24 changes: 12 additions & 12 deletions test/Asset/CliExerciseImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,25 @@

namespace PhpSchool\PhpWorkshopTest\Asset;

use PhpSchool\PhpWorkshop\Check\FileComparisonCheck;
use PhpSchool\PhpWorkshop\Event\EventDispatcher;
use PhpSchool\PhpWorkshop\Exercise\CliExercise;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\Exercise\ProvidesSolution;
use PhpSchool\PhpWorkshop\Exercise\Scenario\CliScenario;
use PhpSchool\PhpWorkshop\ExerciseDispatcher;
use PhpSchool\PhpWorkshop\Solution\SolutionInterface;

class CliExerciseImpl implements ExerciseInterface, CliExercise
{
/**
* @var string
*/
private $name;

/**
* @var SolutionInterface
*/
private $solution;
private string $name;
private SolutionInterface $solution;
private CliScenario $scenario;

public function __construct(string $name = 'my-exercise')
{
$this->name = $name;
$this->scenario = new CliScenario();
}

public function getName(): string
Expand Down Expand Up @@ -58,9 +53,14 @@ public function tearDown(): void
// TODO: Implement tearDown() method.
}

public function getArgs(): array
public function setScenario(CliScenario $scenario): void
{
$this->scenario = $scenario;
}

public function defineTestScenario(): CliScenario
{
return []; // TODO: Implement getArgs() method.
return $this->scenario;
}

public function getType(): ExerciseType
Expand Down
Loading
Loading