diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 87979d7cac30a..b9175109c434a 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG pass it the command as an array of its arguments instead * made the `ProcessHelper` class final * added `WrappableOutputFormatterInterface::formatAndWrap()` (implemented in `OutputFormatter`) + * added `capture_stderr_separately` option to `CommandTester::execute()` 4.1.0 ----- diff --git a/src/Symfony/Component/Console/Tester/ApplicationTester.php b/src/Symfony/Component/Console/Tester/ApplicationTester.php index 3ae31152503cb..24976f08960b8 100644 --- a/src/Symfony/Component/Console/Tester/ApplicationTester.php +++ b/src/Symfony/Component/Console/Tester/ApplicationTester.php @@ -13,8 +13,6 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Console\Output\StreamOutput; /** * Eases the testing of console applications. @@ -33,7 +31,6 @@ class ApplicationTester private $application; private $input; private $statusCode; - private $captureStreamsIndependently = false; public function __construct(Application $application) { @@ -69,36 +66,7 @@ public function run(array $input, $options = array()) putenv('SHELL_INTERACTIVE=1'); } - $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; - if (!$this->captureStreamsIndependently) { - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) { - $this->output->setDecorated($options['decorated']); - } - if (isset($options['verbosity'])) { - $this->output->setVerbosity($options['verbosity']); - } - } else { - $this->output = new ConsoleOutput( - isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL, - isset($options['decorated']) ? $options['decorated'] : null - ); - - $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); - $errorOutput->setFormatter($this->output->getFormatter()); - $errorOutput->setVerbosity($this->output->getVerbosity()); - $errorOutput->setDecorated($this->output->isDecorated()); - - $reflectedOutput = new \ReflectionObject($this->output); - $strErrProperty = $reflectedOutput->getProperty('stderr'); - $strErrProperty->setAccessible(true); - $strErrProperty->setValue($this->output, $errorOutput); - - $reflectedParent = $reflectedOutput->getParentClass(); - $streamProperty = $reflectedParent->getProperty('stream'); - $streamProperty->setAccessible(true); - $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); - } + $this->initOutput($options); $this->statusCode = $this->application->run($this->input, $this->output); @@ -106,28 +74,4 @@ public function run(array $input, $options = array()) return $this->statusCode; } - - /** - * Gets the output written to STDERR by the application. - * - * @param bool $normalize Whether to normalize end of lines to \n or not - * - * @return string - */ - public function getErrorOutput($normalize = false) - { - if (!$this->captureStreamsIndependently) { - throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); - } - - rewind($this->output->getErrorOutput()->getStream()); - - $display = stream_get_contents($this->output->getErrorOutput()->getStream()); - - if ($normalize) { - $display = str_replace(PHP_EOL, "\n", $display); - } - - return $display; - } } diff --git a/src/Symfony/Component/Console/Tester/CommandTester.php b/src/Symfony/Component/Console/Tester/CommandTester.php index ecdc40c046adc..c5b178f2a717b 100644 --- a/src/Symfony/Component/Console/Tester/CommandTester.php +++ b/src/Symfony/Component/Console/Tester/CommandTester.php @@ -13,7 +13,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\StreamOutput; /** * Eases the testing of console commands. @@ -39,9 +38,10 @@ public function __construct(Command $command) * * Available execution options: * - * * interactive: Sets the input interactive flag - * * decorated: Sets the output decorated flag - * * verbosity: Sets the output verbosity flag + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available * * @param array $input An array of command arguments and options * @param array $options An array of execution options @@ -68,12 +68,12 @@ public function execute(array $input, array $options = array()) $this->input->setInteractive($options['interactive']); } - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - $this->output->setDecorated(isset($options['decorated']) ? $options['decorated'] : false); - if (isset($options['verbosity'])) { - $this->output->setVerbosity($options['verbosity']); + if (!isset($options['decorated'])) { + $options['decorated'] = false; } + $this->initOutput($options); + return $this->statusCode = $this->command->run($this->input, $this->output); } } diff --git a/src/Symfony/Component/Console/Tester/TesterTrait.php b/src/Symfony/Component/Console/Tester/TesterTrait.php index 4e1e0795ca2c7..b2f8bde118f06 100644 --- a/src/Symfony/Component/Console/Tester/TesterTrait.php +++ b/src/Symfony/Component/Console/Tester/TesterTrait.php @@ -12,19 +12,19 @@ namespace Symfony\Component\Console\Tester; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\StreamOutput; /** * @author Amrouche Hamza - * - * @internal */ trait TesterTrait { /** @var StreamOutput */ private $output; private $inputs = array(); + private $captureStreamsIndependently = false; /** * Gets the display returned by the last execution of the command or application. @@ -46,6 +46,30 @@ public function getDisplay($normalize = false) return $display; } + /** + * Gets the output written to STDERR by the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string + */ + public function getErrorOutput($normalize = false) + { + if (!$this->captureStreamsIndependently) { + throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); + } + + rewind($this->output->getErrorOutput()->getStream()); + + $display = stream_get_contents($this->output->getErrorOutput()->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + /** * Gets the input instance used by the last execution of the command or application. * @@ -91,6 +115,49 @@ public function setInputs(array $inputs) return $this; } + /** + * Initializes the output property. + * + * Available options: + * + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + */ + private function initOutput(array $options) + { + $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; + if (!$this->captureStreamsIndependently) { + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + } else { + $this->output = new ConsoleOutput( + isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL, + isset($options['decorated']) ? $options['decorated'] : null + ); + + $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); + $errorOutput->setFormatter($this->output->getFormatter()); + $errorOutput->setVerbosity($this->output->getVerbosity()); + $errorOutput->setDecorated($this->output->isDecorated()); + + $reflectedOutput = new \ReflectionObject($this->output); + $strErrProperty = $reflectedOutput->getProperty('stderr'); + $strErrProperty->setAccessible(true); + $strErrProperty->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $streamProperty->setAccessible(true); + $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); + } + } + private static function createStream(array $inputs) { $stream = fopen('php://memory', 'r+', false); diff --git a/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php b/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php index 71547e7b798ec..49ef8029f1645 100644 --- a/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php +++ b/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php @@ -90,4 +90,24 @@ public function testGetStatusCode() { $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); } + + public function testErrorOutput() + { + $application = new Application(); + $application->setAutoExit(false); + $application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { + $output->getErrorOutput()->write('foo'); + }) + ; + + $tester = new ApplicationTester($application); + $tester->run( + array('command' => 'foo', 'foo' => 'bar'), + array('capture_stderr_separately' => true) + ); + + $this->assertSame('foo', $tester->getErrorOutput()); + } } diff --git a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php index 58eb8103fcc9a..afaa2fcf6b2c7 100644 --- a/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php +++ b/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php @@ -160,4 +160,23 @@ public function testSymfonyStyleCommandWithInputs() $this->assertEquals(0, $tester->getStatusCode()); } + + public function testErrorOutput() + { + $command = new Command('foo'); + $command->addArgument('command'); + $command->addArgument('foo'); + $command->setCode(function ($input, $output) { + $output->getErrorOutput()->write('foo'); + } + ); + + $tester = new CommandTester($command); + $tester->execute( + array('foo' => 'bar'), + array('capture_stderr_separately' => true) + ); + + $this->assertSame('foo', $tester->getErrorOutput()); + } }