/**
  * @param ExerciseInterface $exercise
  * @param string $fileName
  * @return ResultInterface
  */
 public function check(ExerciseInterface $exercise, $fileName)
 {
     if (!$exercise instanceof FunctionRequirementsExerciseCheck) {
         throw new \InvalidArgumentException();
     }
     $requiredFunctions = $exercise->getRequiredFunctions();
     $bannedFunctions = $exercise->getBannedFunctions();
     $code = file_get_contents($fileName);
     try {
         $ast = $this->parser->parse($code);
     } catch (Error $e) {
         return Failure::fromCheckAndCodeParseFailure($this, $e, $fileName);
     }
     $visitor = new FunctionVisitor($requiredFunctions, $bannedFunctions);
     $traverser = new NodeTraverser();
     $traverser->addVisitor($visitor);
     $traverser->traverse($ast);
     $bannedFunctions = [];
     if ($visitor->hasUsedBannedFunctions()) {
         $bannedFunctions = array_map(function (FuncCall $node) {
             return ['function' => $node->name->__toString(), 'line' => $node->getLine()];
         }, $visitor->getBannedUsages());
     }
     $missingFunctions = [];
     if (!$visitor->hasMetFunctionRequirements()) {
         $missingFunctions = $visitor->getMissingRequirements();
     }
     if (!empty($bannedFunctions) || !empty($missingFunctions)) {
         return new FunctionRequirementsFailure($this, $bannedFunctions, $missingFunctions);
     }
     return Success::fromCheck($this);
 }
 /**
  * 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.
  * @return ResultInterface The result of the check.
  */
 public function check(ExerciseInterface $exercise, Input $input)
 {
     if (file_exists($input->getArgument('program'))) {
         return Success::fromCheck($this);
     }
     return Failure::fromCheckAndReason($this, sprintf('File: "%s" does not exist', $input->getArgument('program')));
 }
 /**
  * Simply check that the file exists.
  *
  * @param ExerciseInterface $exercise The exercise to check against.
  * @param string $fileName The absolute path to the student's solution.
  * @return ResultInterface The result of the check.
  */
 public function check(ExerciseInterface $exercise, $fileName)
 {
     if (file_exists($fileName)) {
         return Success::fromCheck($this);
     }
     return Failure::fromCheckAndReason($this, sprintf('File: "%s" does not exist', $fileName));
 }
示例#4
0
 /**
  * @param RequestInterface $request
  * @param string $fileName
  * @return ResultInterface
  */
 private function checkRequest(RequestInterface $request, $fileName)
 {
     try {
         $event = $this->eventDispatcher->dispatch(new CgiExecuteEvent('cgi.verify.solution-execute.pre', $request));
         $solutionResponse = $this->executePhpFile($this->exercise->getSolution()->getEntryPoint(), $event->getRequest(), 'solution');
     } catch (CodeExecutionException $e) {
         $this->eventDispatcher->dispatch(new Event('cgi.verify.solution-execute.fail', ['exception' => $e]));
         throw new SolutionExecutionException($e->getMessage());
     }
     try {
         $event = $this->eventDispatcher->dispatch(new CgiExecuteEvent('cgi.verify.user-execute.pre', $request));
         $userResponse = $this->executePhpFile($fileName, $event->getRequest(), 'user');
     } catch (CodeExecutionException $e) {
         $this->eventDispatcher->dispatch(new Event('cgi.verify.user-execute.fail', ['exception' => $e]));
         return Failure::fromNameAndCodeExecutionFailure($this->getName(), $e);
     }
     $solutionBody = (string) $solutionResponse->getBody();
     $userBody = (string) $userResponse->getBody();
     $solutionHeaders = $this->getHeaders($solutionResponse);
     $userHeaders = $this->getHeaders($userResponse);
     if ($solutionBody !== $userBody || $solutionHeaders !== $userHeaders) {
         return new CgiOutRequestFailure($request, $solutionBody, $userBody, $solutionHeaders, $userHeaders);
     }
     return new Success($this->getName());
 }
 /**
  * @param EventDispatcher $eventDispatcher
  */
 public function attach(EventDispatcher $eventDispatcher)
 {
     $studentClient = CouchDBClient::create(['dbname' => static::$studentDb]);
     $solutionClient = CouchDBClient::create(['dbname' => static::$solutionDb]);
     $studentClient->createDatabase($studentClient->getDatabase());
     $solutionClient->createDatabase($solutionClient->getDatabase());
     $eventDispatcher->listen('verify.start', function (Event $e) use($studentClient, $solutionClient) {
         $e->getParameter('exercise')->seed($studentClient);
         $this->replicateDbFromStudentToSolution($studentClient, $solutionClient);
     });
     $eventDispatcher->listen('run.start', function (Event $e) use($studentClient) {
         $e->getParameter('exercise')->seed($studentClient);
     });
     $eventDispatcher->listen('cli.verify.reference-execute.pre', function (CliExecuteEvent $e) {
         $e->prependArg('phpschool');
     });
     $eventDispatcher->listen(['cli.verify.student-execute.pre', 'cli.run.student-execute.pre'], function (CliExecuteEvent $e) {
         $e->prependArg('phpschool-student');
     });
     $eventDispatcher->insertVerifier('verify.finish', function (Event $e) use($studentClient) {
         $verifyResult = $e->getParameter('exercise')->verify($studentClient);
         if (false === $verifyResult) {
             return Failure::fromNameAndReason($this->getName(), 'Database verification failed');
         }
         return Success::fromCheck($this);
     });
     $eventDispatcher->listen(['cli.verify.reference-execute.fail', 'verify.finish', 'run.finish'], function (Event $e) use($studentClient, $solutionClient) {
         $studentClient->deleteDatabase(static::$studentDb);
         $solutionClient->deleteDatabase(static::$solutionDb);
     });
 }
示例#6
0
 public function testFailureFromCodeParseException()
 {
     $e = new Error('Something went wrong yo');
     $failure = Failure::fromCheckAndCodeParseFailure($this->check, $e, 'exercise.php');
     $this->assertInstanceOf(ResultInterface::class, $failure);
     $this->assertEquals('File: "exercise.php" could not be parsed. Error: "Something went wrong yo on unknown line"', $failure->getReason());
     $this->assertEquals('Some Check', $failure->getCheckName());
 }
示例#7
0
 /**
  * @param ExerciseInterface $exercise
  * @param string $fileName
  * @return Failure|Success
  */
 public function check(ExerciseInterface $exercise, $fileName)
 {
     $process = new Process(sprintf('%s -l %s', PHP_BINARY, $fileName));
     $process->run();
     if ($process->isSuccessful()) {
         return Success::fromCheck($this);
     }
     return Failure::fromCheckAndReason($this, $process->getErrorOutput());
 }
 /**
  * This check grabs the contents of the student's solution and
  * 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.
  * @return ResultInterface The result of the check.
  */
 public function check(ExerciseInterface $exercise, Input $input)
 {
     $code = file_get_contents($input->getArgument('program'));
     try {
         $this->parser->parse($code);
     } catch (Error $e) {
         return Failure::fromCheckAndCodeParseFailure($this, $e, $input->getArgument('program'));
     }
     return Success::fromCheck($this);
 }
示例#9
0
 /**
  * @param ExerciseInterface $exercise
  * @param string $fileName
  * @return ResultInterface
  */
 public function check(ExerciseInterface $exercise, $fileName)
 {
     $code = file_get_contents($fileName);
     try {
         $this->parser->parse($code);
     } catch (Error $e) {
         return Failure::fromCheckAndCodeParseFailure($this, $e, $fileName);
     }
     return Success::fromCheck($this);
 }
示例#10
0
 /**
  * @param ExerciseDispatcher $exerciseDispatcher
  */
 public function configure(ExerciseDispatcher $exerciseDispatcher)
 {
     $eventDispatcher = $exerciseDispatcher->getEventDispatcher();
     $appendArgsListener = function (CliExecuteEvent $event) {
         $event->appendArg('127.0.0.1');
         $event->appendArg($this->getRandomPort());
     };
     $eventDispatcher->listen('cli.verify.reference-execute.pre', $appendArgsListener);
     $eventDispatcher->listen('cli.verify.student-execute.pre', $appendArgsListener);
     $eventDispatcher->listen('cli.run.student-execute.pre', $appendArgsListener);
     $eventDispatcher->listen('cli.verify.reference.executing', function (CliExecuteEvent $event) {
         $args = $event->getArgs()->getArrayCopy();
         $client = $this->socketFactory->createClient(...$args);
         //wait for server to boot
         usleep(100000);
         $client->connect();
         $client->readAll();
         //wait for shutdown
         usleep(100000);
     });
     $eventDispatcher->insertVerifier('cli.verify.student.executing', function (CliExecuteEvent $event) {
         $args = $event->getArgs()->getArrayCopy();
         $client = $this->socketFactory->createClient(...$args);
         //wait for server to boot
         usleep(100000);
         try {
             $client->connect();
         } catch (Exception $e) {
             return Failure::fromNameAndReason($this->getName(), $e->getMessage());
         }
         $out = $client->readAll();
         //wait for shutdown
         usleep(100000);
         $date = new \DateTime();
         //match the current date but any seconds
         //since we can't mock time in PHP easily
         if (!preg_match(sprintf('/^%s:([0-5][0-9]|60)\\n$/', $date->format('Y-m-d H:i')), $out)) {
             return new StdOutFailure($this->getName(), $date->format("Y-m-d H:i:s\n"), $out);
         }
         return new Success($this->getName());
     });
     $eventDispatcher->listen('cli.run.student.executing', function (CliExecuteEvent $event) {
         /** @var OutputInterface $output */
         $output = $event->getParameter('output');
         $args = $event->getArgs()->getArrayCopy();
         $client = $this->socketFactory->createClient(...$args);
         //wait for server to boot
         usleep(100000);
         $client->connect();
         $out = $client->readAll();
         //wait for shutdown
         usleep(100000);
         $output->write($out);
     });
 }
示例#11
0
 /**
  * @param RequestInterface $request The request that caused the failure.
  * @param string|null $reason
  */
 public function __construct(RequestInterface $request, $reason = null)
 {
     $this->request = $request;
     parent::__construct(static::$name, $reason);
 }
 /**
  * @param string $fileName
  * @return ResultInterface
  */
 public function check($fileName)
 {
     $statements = $this->parser->parse(file_get_contents($fileName));
     $include = null;
     foreach ($statements as $statement) {
         if ($statement instanceof Include_) {
             $include = $statement;
             break;
         }
     }
     if (null === $include) {
         return Failure::fromNameAndReason($this->getName(), 'No require statement found');
     }
     return new Success($this->getName());
 }
示例#13
0
 /**
  * Simply print the reason.
  *
  * @param ResultsRenderer $renderer
  * @return string
  */
 public function render(ResultsRenderer $renderer)
 {
     return $renderer->center($this->result->getReason()) . "\n";
 }
示例#14
0
 /**
  * Here we attach to various events to seed, verify and inject the DSN's
  * to the student & reference solution programs's CLI arguments.
  *
  * @param EventDispatcher $eventDispatcher
  */
 public function attach(EventDispatcher $eventDispatcher)
 {
     if (file_exists($this->databaseDirectory)) {
         throw new \RuntimeException(sprintf('Database directory: "%s" already exists', $this->databaseDirectory));
     }
     mkdir($this->databaseDirectory, 0777, true);
     try {
         $db = new PDO($this->userDsn);
         $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
     } catch (\PDOException $e) {
         rmdir($this->databaseDirectory);
         throw $e;
     }
     $eventDispatcher->listen('verify.start', function (Event $e) use($db) {
         $e->getParameter('exercise')->seed($db);
         //make a copy - so solution can modify without effecting database user has access to
         copy($this->userDatabasePath, $this->solutionDatabasePath);
     });
     $eventDispatcher->listen('run.start', function (Event $e) use($db) {
         $e->getParameter('exercise')->seed($db);
     });
     $eventDispatcher->listen('cli.verify.reference-execute.pre', function (CliExecuteEvent $e) {
         $e->prependArg($this->solutionDsn);
     });
     $eventDispatcher->listen(['cli.verify.student-execute.pre', 'cli.run.student-execute.pre'], function (CliExecuteEvent $e) {
         $e->prependArg($this->userDsn);
     });
     $eventDispatcher->insertVerifier('verify.finish', function (Event $e) use($db) {
         $verifyResult = $e->getParameter('exercise')->verify($db);
         if (false === $verifyResult) {
             return Failure::fromNameAndReason($this->getName(), 'Database verification failed');
         }
         return new Success('Database Verification Check');
     });
     $eventDispatcher->listen(['cli.verify.reference-execute.fail', 'verify.finish', 'run.finish'], function (Event $e) use($db) {
         unset($db);
         @unlink($this->userDatabasePath);
         @unlink($this->solutionDatabasePath);
         rmdir($this->databaseDirectory);
     });
 }
示例#15
0
 /**
  * @param string $fileName
  * @return ResultInterface
  */
 public function verify($fileName)
 {
     //arrays are not pass-by-ref
     $args = new ArrayObject($this->exercise->getArgs());
     try {
         $event = $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.verify.solution-execute.pre', $args));
         $solutionOutput = $this->executePhpFile($this->exercise->getSolution()->getEntryPoint(), $event->getArgs(), 'solution');
     } catch (CodeExecutionException $e) {
         $this->eventDispatcher->dispatch(new Event('cli.verify.solution-execute.fail', ['exception' => $e]));
         throw new SolutionExecutionException($e->getMessage());
     }
     try {
         $event = $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.verify.user-execute.pre', $args));
         $userOutput = $this->executePhpFile($fileName, $event->getArgs(), 'user');
     } catch (CodeExecutionException $e) {
         $this->eventDispatcher->dispatch(new Event('cli.verify.user-execute.fail', ['exception' => $e]));
         return Failure::fromNameAndCodeExecutionFailure($this->getName(), $e);
     }
     if ($solutionOutput === $userOutput) {
         return new Success($this->getName());
     }
     return StdOutFailure::fromNameAndOutput($this->getName(), $solutionOutput, $userOutput);
 }
示例#16
0
 /**
  * @param ResultsRenderer $renderer
  * @return string
  */
 public function render(ResultsRenderer $renderer)
 {
     return '  ' . $this->result->getReason() . "\n";
 }
示例#17
0
 /**
  * @param ArrayObject $args The arguments that caused the failure.
  * @param string|null $reason The reason (if any) of the failure.
  */
 public function __construct(ArrayObject $args, $reason = null)
 {
     $this->args = $args;
     parent::__construct(static::$name, $reason);
 }