<?php namespace MFB\Behat\Subcontext; use Behat\Gherkin\Node\PyStringNode; use Behat\MinkExtension\Context\RawMinkContext; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\EventDispatcher\EventDispatcher; /** * Class CommandContext */ class CommandContext extends RawMinkContext { /** * @var string */ private $consoleAppClass; /** * @var \Symfony\Component\Console\Application */ private $application; /** * @var array */ private $registeredCommands; /** * @var array */ private $loadedCommands; /** * @var \Symfony\Component\Console\Tester\CommandTester */ private $tester; /** * @var \Exception */ private $commandException; /** * @var array */ private $commandParameters; /** * @var string */ private $runCommand; /** * @var array */ private $listeners; /** * @var int */ private $exitCode; /** * @param string $consoleAppClass * @param array $registeredCommands * @param array $listeners * * @throws \InvalidArgumentException */ public function __construct($consoleAppClass, array $registeredCommands = array(), array $listeners = array()) { $this->checkClassIsLoaded($consoleAppClass); $this->consoleAppClass = $consoleAppClass; $this->registeredCommands = $registeredCommands; $this->loadedCommands = array(); $this->listeners = $listeners; $this->commandParameters = array(); $this->exitCode = 0; $this->useContext('exception', new ExceptionContext()); } /** * @Given /^I run a command "([^"]*)"$/ */ public function iRunACommand($command) { $commandInstance = $this->getCommand($command); $this->tester = new CommandTester($commandInstance); try { $this->exitCode = $this ->tester ->execute( $this->getCommandParams($command) ) ; $this->commandException = null; } catch (\Exception $exception) { $this->commandException = $exception; $this->exitCode = $exception->getCode(); } $this->runCommand = $command; $this->commandParameters = array(); } /** * @Given /^I run a command "([^"]*)" with parameters:$/ */ public function iRunACommandWithParameters($command, PyStringNode $parameterJson) { $this->commandParameters = json_decode($parameterJson->getRaw(), true); if (null === $this->commandParameters) { throw new \InvalidArgumentException( "PyStringNode could not be converted to json." ); } $this->iRunACommand($command); } /** * @Then /^The command exception "([^"]*)" should be thrown$/ */ public function theCommandExceptionShouldBeThrown($exceptionClass) { $this->checkThatCommandHasRun(); $this ->getSubcontext('exception') ->setException($this->commandException) ->assertException($exceptionClass) ; } /** * @Then /^The command exit code should be (\d+)$/ */ public function theCommandExitCodeShouldBe($exitCode) { $this->checkThatCommandHasRun(); assertEquals($exitCode, $this->exitCode); } /** * @Then /^I should see "([^"]*)" in the command output$/ */ public function iShouldSeeInTheCommandOutput($regexp) { $this->checkThatCommandHasRun(); assertRegExp($regexp, $this->tester->getDisplay()); } /** * @Then /^The command exception "([^"]*)" with message "([^"]*)" should be thrown$/ */ public function theCommandExceptionWithMessageShouldBeThrown($exceptionClass, $exceptionMessage) { $this->checkThatCommandHasRun(); $this ->getSubcontext('exception') ->setException($this->commandException) ->assertException($exceptionClass) ; $this ->getSubcontext('exception') ->assertExceptionMessage($exceptionMessage) ; } /** * @return \Symfony\Component\Console\Application * * @throws \LogicException */ public function getApplication() { if (null !== $this->application) { return $this->application; } $callingContext = $this->getMainContext(); if (!$callingContext instanceof KernelAwareInterface) { throw new \LogicException( "CommandContext or MainContext must implement 'KernelAwareInterface'" ); } $kernel = $callingContext->getKernel(); if (null === $kernel) { throw new \LogicException( "The kernel hasn't been initialized yet. Please use this method only in a step method." ); } $this->application = new $this->consoleAppClass($kernel); $this->processConsoleEventListeners(); return $this->application; } /** * @param string $command * * @return \Symfony\Component\Console\Command\Command * * @throws \InvalidArgumentException */ public function getCommand($command) { $command = (string) $command; if ($this->isLoaded($command)) { return $this->loadedCommands[$command]; } if (!$this->isRegistered($command)) { throw new \InvalidArgumentException( sprintf('Command with name "%s" is not registered with the Context', $command) ); } $commandInstance = new $this->registeredCommands[$command](); $application = $this->getApplication(); $application->add($commandInstance); return $this->loadedCommands[$command] = $commandInstance; } /** * @param string $commandName * @param string $commandClass */ public function registerCommand($commandName, $commandClass) { $this->checkClassIsLoaded($commandClass); $this->registeredCommands[(string) $commandName] = $commandClass; } /** * @param string $commandName * * @return bool */ public function unregisterCommand($commandName) { $commandName = (string) $commandName; if (!isset($this->registeredCommands[$commandName])) { return false; } unset($this->registeredCommands[$commandName]); return true; } /** * @param string $command * * @return bool */ public function isRegistered($command) { return (isset($this->registeredCommands[(string) $command])); } /** * @param string $command * * @return bool */ public function isLoaded($command) { return (isset($this->loadedCommands[(string) $command])); } /** * @param string $class * * @throws \InvalidArgumentException */ private function checkClassIsLoaded($class) { if (!class_exists((string) $class)) { throw new \InvalidArgumentException( 'Class "%s" could not be found or autoloaded.' ); } } /** * @return bool * * @throws \LogicException */ private function checkThatCommandHasRun() { if (null === $this->runCommand) { throw new \LogicException( "You first need to run a command to check to use this step" ); } return true; } /** * Processes the subscribers and listeners for this Application */ private function processConsoleEventListeners() { if (null === $this->application) { return null; } if (!empty($this->listeners)) { $dispatcher = new EventDispatcher(); $listeners = array_merge( array( 'subscriber' => array(), 'listener' => array() ), $this->listeners ); foreach ($listeners['listener'] as $event => $listener) { $priority = 0; if (is_array($listeners)) { list($listener, $priority) = $listener; } $dispatcher->addListener($event, $listener, $priority); } foreach ($listeners['subscriber'] as $subscriber) { $dispatcher->addSubscriber($subscriber); } $this ->application ->setDispatcher($dispatcher) ; } } /** * @param string $command * * @return array */ private function getCommandParams($command) { $default = array( 'command' => $command ); return array_merge( $this->commandParameters, $default ); } }