public function testListenWithMultipleEvents()
 {
     $e1 = new Event('some-event', ['arg1' => 1, 'arg2' => 2]);
     $e2 = new Event('some-event', ['arg1' => 1, 'arg2' => 2]);
     $mockCallback1 = $this->getMock('stdClass', ['callback']);
     $mockCallback1->expects($this->exactly(2))->method('callback')->withConsecutive([$e1], [$e2])->will($this->returnValue(true));
     $this->eventDispatcher->listen(['some-event', 'second-event'], [$mockCallback1, 'callback']);
     $this->eventDispatcher->dispatch($e1);
     $this->eventDispatcher->dispatch($e2);
 }
示例#2
0
 /**
  * Runs a student's solution by invoking PHP from the CLI passing the arguments gathered from the exercise
  * as command line arguments to PHP.
  *
  * Running only runs the student's solution, the reference solution is not run and no verification is performed,
  * the output of the student's solution is written directly to the output.
  *
  * Events dispatched:
  *
  *  * cli.run.student-execute.pre
  *  * cli.run.student.executing
  *
  * @param Input $input The command line arguments passed to the command.
  * @param OutputInterface $output A wrapper around STDOUT.
  * @return bool If the solution was successfully executed, eg. exit code was 0.
  */
 public function run(Input $input, OutputInterface $output)
 {
     $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.run.start', $this->exercise, $input));
     $success = true;
     foreach ($this->preserveOldArgFormat($this->exercise->getArgs()) as $i => $args) {
         /** @var CliExecuteEvent $event */
         $event = $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.run.student-execute.pre', new ArrayObject($args)));
         $args = $event->getArgs();
         if (count($args)) {
             $glue = max(array_map('strlen', $args->getArrayCopy())) > 30 ? "\n" : ', ';
             $output->writeTitle('Arguments');
             $output->write(implode($glue, $args->getArrayCopy()));
             $output->emptyLine();
         }
         $output->writeTitle("Output");
         $process = $this->getPhpProcess($input->getArgument('program'), $args);
         $process->start();
         $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.run.student.executing', $args, ['output' => $output]));
         $process->wait(function ($outputType, $outputBuffer) use($output) {
             $output->write($outputBuffer);
         });
         $output->emptyLine();
         if (!$process->isSuccessful()) {
             $success = false;
         }
         $output->lineBreak();
     }
     $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cli.run.finish', $this->exercise, $input));
     return $success;
 }
示例#3
0
 /**
  * Runs a student's solution by invoking PHP via the `php-cgi` binary, populating all the super globals with
  * the information from the request objects returned from the exercise. The exercise can return multiple
  * requests so the solution will be invoked for however many requests there are.
  *
  * Running only runs the student's solution, the reference solution is not run and no verification is performed,
  * the output of the student's solution is written directly to the output.
  *
  * Events dispatched (for each request):
  *
  * * cgi.run.student-execute.pre
  * * cgi.run.student.executing
  *
  * @param Input $input The command line arguments passed to the command.
  * @param OutputInterface $output A wrapper around STDOUT.
  * @return bool If the solution was successfully executed, eg. exit code was 0.
  */
 public function run(Input $input, OutputInterface $output)
 {
     $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cgi.run.start', $this->exercise, $input));
     $success = true;
     foreach ($this->exercise->getRequests() as $i => $request) {
         $event = $this->eventDispatcher->dispatch(new CgiExecuteEvent('cgi.run.student-execute.pre', $request));
         $process = $this->getProcess($input->getArgument('program'), $event->getRequest());
         $output->writeTitle("Request");
         $output->emptyLine();
         $output->write($this->requestRenderer->renderRequest($request));
         $output->writeTitle("Output");
         $output->emptyLine();
         $process->start();
         $this->eventDispatcher->dispatch(new CgiExecuteEvent('cgi.run.student.executing', $request, ['output' => $output]));
         $process->wait(function ($outputType, $outputBuffer) use($output) {
             $output->write($outputBuffer);
         });
         $output->emptyLine();
         if (!$process->isSuccessful()) {
             $success = false;
         }
         $output->lineBreak();
     }
     $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('cgi.run.finish', $this->exercise, $input));
     return $success;
 }
 /**
  * @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);
     });
 }
 /**
  * @param ContainerInterface $container
  * @return EventDispatcher
  */
 public function __invoke(ContainerInterface $container)
 {
     $dispatcher = new EventDispatcher($container->get(ResultAggregator::class));
     $prepareSolutionListener = $container->get(PrepareSolutionListener::class);
     $dispatcher->listen('verify.start', $prepareSolutionListener);
     $dispatcher->listen('run.start', $prepareSolutionListener);
     $codePatcherListener = $container->get(CodePatchListener::class);
     $dispatcher->listen('verify.pre.execute', [$codePatcherListener, 'patch']);
     $dispatcher->listen('verify.post.execute', [$codePatcherListener, 'revert']);
     $dispatcher->listen('run.start', [$codePatcherListener, 'patch']);
     $dispatcher->listen('run.finish', [$codePatcherListener, 'revert']);
     $dispatcher->listen('verify.post.check', $container->get(SelfCheckListener::class));
     return $dispatcher;
 }
 public function testVerifyPostExecuteIsStillDispatchedEvenIfRunnerThrowsException()
 {
     $this->eventDispatcher->expects($this->exactly(3))->method('dispatch')->withConsecutive([$this->isInstanceOf(Event::class)], [$this->isInstanceOf(Event::class)], [$this->isInstanceOf(Event::class)]);
     $this->createExercise();
     $this->mockRunner();
     $this->runner->expects($this->once())->method('verify')->with($this->file)->will($this->throwException(new RuntimeException()));
     $this->setExpectedException(RuntimeException::class);
     $this->exerciseDispatcher->verify($this->exercise, $this->file);
 }
 /**
  * @param string $eventName
  * @param array $listeners
  * @param ContainerInterface $container
  * @param EventDispatcher $dispatcher
  * @throws \PhpSchool\PhpWorkshop\Exception\InvalidArgumentException
  */
 private function attachListeners($eventName, array $listeners, ContainerInterface $container, EventDispatcher $dispatcher)
 {
     array_walk($listeners, function ($listener) use($eventName, $dispatcher, $container) {
         if (is_callable($listener)) {
             return $dispatcher->listen($eventName, $listener);
         }
         if (!is_string($listener)) {
             throw new InvalidArgumentException(sprintf('Listener must be a callable or a container entry for a callable service.'));
         }
         if (!$container->has($listener)) {
             throw new InvalidArgumentException(sprintf('Container has no entry named: "%s"', $listener));
         }
         $listener = $container->get($listener);
         if (!is_callable($listener)) {
             throw InvalidArgumentException::typeMisMatch('callable', $listener);
         }
         return $dispatcher->listen($eventName, $listener);
     });
 }
 /**
  * @param ExerciseInterface $exercise
  * @param string $fileName
  * @param OutputInterface $output
  * @return bool
  */
 public function run(ExerciseInterface $exercise, $fileName, OutputInterface $output)
 {
     $exercise->configure($this);
     $this->eventDispatcher->dispatch(new Event('run.start', compact('exercise', 'fileName')));
     try {
         $exitStatus = $this->runnerFactory->create($exercise, $this->eventDispatcher)->run($fileName, $output);
     } finally {
         $this->eventDispatcher->dispatch(new Event('run.finish', compact('exercise', 'fileName')));
     }
     return $exitStatus;
 }
 /**
  * Run a student's solution against a specific exercise. Does not invoke checks. Invokes the
  * correct runner for the exercise based on the exercise type. Various events are triggered throughout the process.
  * The output of the solution is written directly to the `OutputInterface` instance.
  *
  * @param ExerciseInterface $exercise The exercise instance.
  * @param Input $input The command line arguments passed to the command.
  * @param OutputInterface $output An output instance capable of writing to stdout.
  * @return bool Whether the solution ran successfully or not.
  */
 public function run(ExerciseInterface $exercise, Input $input, OutputInterface $output)
 {
     $exercise->configure($this);
     $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('run.start', $exercise, $input));
     try {
         $exitStatus = $this->runnerManager->getRunner($exercise)->run($input, $output);
     } finally {
         $this->eventDispatcher->dispatch(new ExerciseRunnerEvent('run.finish', $exercise, $input));
     }
     return $exitStatus;
 }
 /**
  * @param string $eventName
  * @param array $listeners
  * @param ContainerInterface $container
  * @param EventDispatcher $dispatcher
  * @throws \PhpSchool\PhpWorkshop\Exception\InvalidArgumentException
  */
 private function attachListeners($eventName, array $listeners, ContainerInterface $container, EventDispatcher $dispatcher)
 {
     array_walk($listeners, function ($listener) use($eventName, $dispatcher, $container) {
         if ($listener instanceof ContainerListenerHelper) {
             if (!$container->has($listener->getService())) {
                 throw new InvalidArgumentException(sprintf('Container has no entry named: "%s"', $listener->getService()));
             }
             return $dispatcher->listen($eventName, function (...$args) use($container, $listener) {
                 $service = $container->get($listener->getService());
                 if (!method_exists($service, $listener->getMethod())) {
                     throw new InvalidArgumentException(sprintf('Method "%s" does not exist on "%s"', $listener->getMethod(), get_class($service)));
                 }
                 $service->{$listener->getMethod()}(...$args);
             });
         }
         if (!is_callable($listener)) {
             throw new InvalidArgumentException(sprintf('Listener must be a callable or a container entry for a callable service.'));
         }
         return $dispatcher->listen($eventName, $listener);
     });
 }
示例#11
0
 /**
  * @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);
     $solutionDsn = sprintf('sqlite:%s', $this->solutionDatabasePath);
     $userDsn = sprintf('sqlite:%s', $this->userDatabasePath);
     $db = new PDO($userDsn);
     $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
     $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.solution-execute.pre', function (CliExecuteEvent $e) use($solutionDsn) {
         $e->prependArg($solutionDsn);
     });
     $eventDispatcher->listen(['cli.verify.user-execute.pre', 'cli.run.user-execute.pre'], function (CliExecuteEvent $e) use($userDsn) {
         $e->prependArg($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.solution-execute.fail', 'verify.finish', 'run.finish'], function (Event $e) use($db) {
         unset($db);
         @unlink($this->userDatabasePath);
         @unlink($this->solutionDatabasePath);
         rmdir($this->databaseDirectory);
     });
 }
 public function testListenersAndVerifiersAreCalledInOrderOfAttachment()
 {
     $e1 = new Event('first-event', ['arg1' => 1, 'arg2' => 2]);
     $counter = 0;
     $this->eventDispatcher->insertVerifier('first-event', function (Event $e) use(&$counter) {
         $this->assertEquals(0, $counter);
         $counter++;
     });
     $this->eventDispatcher->listen('first-event', function (Event $e) use(&$counter) {
         $this->assertEquals(1, $counter);
         $counter++;
     });
     $this->eventDispatcher->dispatch($e1);
 }
示例#13
0
 /**
  * @param string $fileName
  * @param OutputInterface $output
  * @return bool
  */
 public function run($fileName, OutputInterface $output)
 {
     /** @var CliExecuteEvent $event */
     $event = $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.run.user-execute.pre', new ArrayObject($this->exercise->getArgs())));
     $args = $event->getArgs();
     if (count($args)) {
         $glue = max(array_map('strlen', $args->getArrayCopy())) > 30 ? "\n" : ', ';
         $output->writeTitle('Arguments');
         $output->write(implode($glue, $args->getArrayCopy()));
         $output->emptyLine();
     }
     $output->writeTitle("Output");
     $process = $this->getPhpProcess($fileName, $args);
     $process->start();
     $this->eventDispatcher->dispatch(new CliExecuteEvent('cli.run.executing', $args, ['output' => $output]));
     $process->wait(function ($outputType, $outputBuffer) use($output) {
         $output->writeLine($outputBuffer);
     });
     return $process->isSuccessful();
 }
示例#14
0
 /**
  * Runs a student's solution by invoking PHP via the `php-cgi` binary, populating all the super globals with
  * the information from the request objects returned from the exercise. The exercise can return multiple
  * requests so the solution will be invoked for however many requests there are.
  *
  * Running only runs the student's solution, the reference solution is not run and no verification is performed,
  * the output of the student's solution is written directly to the output.
  *
  * Events dispatched (for each request):
  *
  * * cgi.run.student-execute.pre
  * * cgi.run.student.executing
  *
  * @param string $fileName The absolute path to the student's solution.
  * @param OutputInterface $output A wrapper around STDOUT.
  * @return bool If the solution was successfully executed, eg. exit code was 0.
  */
 public function run($fileName, OutputInterface $output)
 {
     $success = true;
     foreach ($this->exercise->getRequests() as $i => $request) {
         $event = $this->eventDispatcher->dispatch(new CgiExecuteEvent('cgi.run.student-execute.pre', $request));
         $process = $this->getProcess($fileName, $event->getRequest());
         $output->writeTitle("Request");
         $output->emptyLine();
         $output->writeRequest($request);
         $output->writeTitle("Output");
         $output->emptyLine();
         $process->start();
         $this->eventDispatcher->dispatch(new CgiExecuteEvent('cgi.run.student.executing', $request, ['output' => $output]));
         $process->wait(function ($outputType, $outputBuffer) use($output) {
             $output->write($outputBuffer);
         });
         $output->emptyLine();
         if (!$process->isSuccessful()) {
             $success = false;
         }
         $output->lineBreak();
     }
     return $success;
 }
示例#15
0
 /**
  * @param EventDispatcher $eventDispatcher
  * @param ExerciseInterface $exercise
  */
 private function dispatchExerciseSelectedEvent(EventDispatcher $eventDispatcher, ExerciseInterface $exercise)
 {
     $eventDispatcher->dispatch(new Event(sprintf('exercise.selected.%s', AbstractExercise::normaliseName($exercise->getName()))));
 }
示例#16
0
 /**
  * @param CommandDefinition $command
  * @param callable $callable
  * @param Input $input
  * @return int
  */
 private function callCommand(CommandDefinition $command, callable $callable, Input $input)
 {
     $this->eventDispatcher->dispatch(new Event\Event('route.pre.invoke'));
     $this->eventDispatcher->dispatch(new Event\Event(sprintf('route.pre.invoke.%s', $command->getName())));
     return $callable($input);
 }