Skip to content

Commit e0fa255

Browse files
authored
Merge pull request #197 from php-school/code-exists-check
Add code exists check
2 parents 24309da + c37f8a6 commit e0fa255

File tree

7 files changed

+179
-0
lines changed

7 files changed

+179
-0
lines changed

app/config.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
use PhpSchool\PhpWorkshop\Utils\RequestRenderer;
6464
use PhpSchool\PhpWorkshop\WorkshopType;
6565
use PhpSchool\PhpWorkshop\Check\FileExistsCheck;
66+
use PhpSchool\PhpWorkshop\Check\CodeExistsCheck;
6667
use PhpSchool\PhpWorkshop\Check\FunctionRequirementsCheck;
6768
use PhpSchool\PhpWorkshop\Check\PhpLintCheck;
6869
use PhpSchool\PhpWorkshop\Command\CreditsCommand;
@@ -109,6 +110,7 @@
109110
CheckRepository::class => function (ContainerInterface $c) {
110111
return new CheckRepository([
111112
$c->get(FileExistsCheck::class),
113+
$c->get(CodeExistsCheck::class),
112114
$c->get(PhpLintCheck::class),
113115
$c->get(CodeParseCheck::class),
114116
$c->get(ComposerCheck::class),
@@ -239,6 +241,9 @@
239241
//checks
240242
FileExistsCheck::class => create(),
241243
PhpLintCheck::class => create(),
244+
CodeExistsCheck::class => function (ContainerInterface $c) {
245+
return new CodeExistsCheck($c->get(Parser::class));
246+
},
242247
CodeParseCheck::class => function (ContainerInterface $c) {
243248
return new CodeParseCheck($c->get(Parser::class));
244249
},

src/Check/CodeExistsCheck.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpSchool\PhpWorkshop\Check;
6+
7+
use PhpParser\Error;
8+
use PhpParser\ErrorHandler;
9+
use PhpParser\Node\Stmt\InlineHTML;
10+
use PhpParser\NodeFinder;
11+
use PhpParser\Parser;
12+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
13+
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
14+
use PhpSchool\PhpWorkshop\Input\Input;
15+
use PhpSchool\PhpWorkshop\Result\Failure;
16+
use PhpSchool\PhpWorkshop\Result\ResultInterface;
17+
use PhpSchool\PhpWorkshop\Result\Success;
18+
19+
class CodeExistsCheck implements SimpleCheckInterface
20+
{
21+
/**
22+
* @var Parser
23+
*/
24+
private $parser;
25+
26+
public function __construct(Parser $parser)
27+
{
28+
$this->parser = $parser;
29+
}
30+
31+
public function getName(): string
32+
{
33+
return 'Code Exists Check';
34+
}
35+
36+
/**
37+
* Check solution provided contains code
38+
* Note: We don't care if it's valid code at this point
39+
*/
40+
public function check(ExerciseInterface $exercise, Input $input): ResultInterface
41+
{
42+
$noopHandler = new class implements ErrorHandler {
43+
public function handleError(Error $error): void
44+
{
45+
}
46+
};
47+
48+
$code = (string) file_get_contents($input->getRequiredArgument('program'));
49+
$statements = $this->parser->parse($code, $noopHandler);
50+
51+
$empty = null === $statements || empty($statements);
52+
53+
if (!$empty) {
54+
$openingTag = is_array($statements) && count($statements) === 1 ? $statements[0] : null;
55+
$empty = $openingTag instanceof InlineHTML ? in_array($openingTag->value, ['<?php', '<?']) : false;
56+
}
57+
58+
if ($empty) {
59+
return Failure::fromCheckAndReason($this, 'No code was found');
60+
}
61+
62+
return Success::fromCheck($this);
63+
}
64+
65+
/**
66+
* This check can run on any exercise type.
67+
*/
68+
public function canRun(ExerciseType $exerciseType): bool
69+
{
70+
return in_array($exerciseType->getValue(), [ExerciseType::CGI, ExerciseType::CLI], true);
71+
}
72+
73+
public function getExerciseInterface(): string
74+
{
75+
return ExerciseInterface::class;
76+
}
77+
78+
/**
79+
* This check must run before executing the solution because all solutions require code
80+
*/
81+
public function getPosition(): string
82+
{
83+
return SimpleCheckInterface::CHECK_BEFORE;
84+
}
85+
}

src/ExerciseRunner/CgiRunner.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpSchool\PhpWorkshop\ExerciseRunner;
66

77
use GuzzleHttp\Psr7\Message;
8+
use PhpSchool\PhpWorkshop\Check\CodeExistsCheck;
89
use PhpSchool\PhpWorkshop\Check\CodeParseCheck;
910
use PhpSchool\PhpWorkshop\Check\FileExistsCheck;
1011
use PhpSchool\PhpWorkshop\Check\PhpLintCheck;
@@ -55,6 +56,7 @@ class CgiRunner implements ExerciseRunnerInterface
5556
*/
5657
private static $requiredChecks = [
5758
FileExistsCheck::class,
59+
CodeExistsCheck::class,
5860
PhpLintCheck::class,
5961
CodeParseCheck::class,
6062
];

src/ExerciseRunner/CliRunner.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpSchool\PhpWorkshop\ExerciseRunner;
66

7+
use PhpSchool\PhpWorkshop\Check\CodeExistsCheck;
78
use PhpSchool\PhpWorkshop\Check\CodeParseCheck;
89
use PhpSchool\PhpWorkshop\Check\FileExistsCheck;
910
use PhpSchool\PhpWorkshop\Check\PhpLintCheck;
@@ -50,6 +51,7 @@ class CliRunner implements ExerciseRunnerInterface
5051
*/
5152
private static $requiredChecks = [
5253
FileExistsCheck::class,
54+
CodeExistsCheck::class,
5355
PhpLintCheck::class,
5456
CodeParseCheck::class,
5557
];

test/Check/CodeExistsCheckTest.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace PhpSchool\PhpWorkshopTest\Check;
4+
5+
use PhpParser\ParserFactory;
6+
use PhpSchool\PhpWorkshop\Check\CodeExistsCheck;
7+
use PhpSchool\PhpWorkshop\Check\SimpleCheckInterface;
8+
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
9+
use PhpSchool\PhpWorkshop\Input\Input;
10+
use PHPUnit\Framework\TestCase;
11+
use PhpSchool\PhpWorkshop\Check\FileExistsCheck;
12+
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
13+
use PhpSchool\PhpWorkshop\Result\Failure;
14+
use PhpSchool\PhpWorkshop\Result\Success;
15+
16+
class CodeExistsCheckTest extends TestCase
17+
{
18+
/**
19+
* @var string
20+
*/
21+
private $testDir;
22+
23+
/**
24+
* @var FileExistsCheck
25+
*/
26+
private $check;
27+
28+
/**
29+
* @var ExerciseInterface
30+
*/
31+
private $exercise;
32+
33+
public function setUp(): void
34+
{
35+
$this->testDir = sprintf(
36+
'%s/%s/%s',
37+
str_replace('\\', '/', sys_get_temp_dir()),
38+
basename(str_replace('\\', '/', get_class($this))),
39+
$this->getName()
40+
);
41+
42+
mkdir($this->testDir, 0777, true);
43+
$this->check = new CodeExistsCheck((new ParserFactory())->create(ParserFactory::PREFER_PHP7));
44+
$this->exercise = $this->createMock(ExerciseInterface::class);
45+
$this->assertEquals('Code Exists Check', $this->check->getName());
46+
$this->assertEquals(ExerciseInterface::class, $this->check->getExerciseInterface());
47+
$this->assertEquals(SimpleCheckInterface::CHECK_BEFORE, $this->check->getPosition());
48+
49+
$this->assertTrue($this->check->canRun(ExerciseType::CGI()));
50+
$this->assertTrue($this->check->canRun(ExerciseType::CLI()));
51+
52+
$this->file = sprintf('%s/submission.php', $this->testDir);
53+
touch($this->file);
54+
}
55+
56+
public function testSuccess(): void
57+
{
58+
file_put_contents($this->file, '<?php echo "Hello World";');
59+
60+
$this->assertInstanceOf(
61+
Success::class,
62+
$this->check->check($this->exercise, new Input('app', ['program' => $this->file]))
63+
);
64+
}
65+
66+
public function testFailure(): void
67+
{
68+
file_put_contents($this->file, '<?php');
69+
70+
$failure = $this->check->check($this->exercise, new Input('app', ['program' => $this->file]));
71+
72+
$this->assertInstanceOf(Failure::class, $failure);
73+
$this->assertEquals('No code was found', $failure->getReason());
74+
}
75+
76+
public function tearDown(): void
77+
{
78+
unlink($this->file);
79+
rmdir($this->testDir);
80+
}
81+
}

test/ExerciseRunner/CgiRunnerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Colors\Color;
66
use GuzzleHttp\Psr7\Request;
7+
use PhpSchool\PhpWorkshop\Check\CodeExistsCheck;
78
use PhpSchool\Terminal\Terminal;
89
use PhpSchool\PhpWorkshop\Check\CodeParseCheck;
910
use PhpSchool\PhpWorkshop\Check\FileExistsCheck;
@@ -53,6 +54,7 @@ public function testRequiredChecks(): void
5354
{
5455
$requiredChecks = [
5556
FileExistsCheck::class,
57+
CodeExistsCheck::class,
5658
PhpLintCheck::class,
5759
CodeParseCheck::class,
5860
];

test/ExerciseRunner/CliRunnerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PhpSchool\PhpWorkshopTest\ExerciseRunner;
44

55
use Colors\Color;
6+
use PhpSchool\PhpWorkshop\Check\CodeExistsCheck;
67
use PhpSchool\Terminal\Terminal;
78
use PhpSchool\PhpWorkshop\Check\CodeParseCheck;
89
use PhpSchool\PhpWorkshop\Check\FileExistsCheck;
@@ -54,6 +55,7 @@ public function testRequiredChecks(): void
5455
{
5556
$requiredChecks = [
5657
FileExistsCheck::class,
58+
CodeExistsCheck::class,
5759
PhpLintCheck::class,
5860
CodeParseCheck::class,
5961
];

0 commit comments

Comments
 (0)