[Console][2.3] ApplicationTester - test stdout and stderr
This commit is contained in:
parent
464a4920de
commit
6ff6a28d20
@ -14,15 +14,16 @@ namespace Symfony\Component\Console\Output;
|
|||||||
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
|
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConsoleOutput is the default class for all CLI output. It uses STDOUT.
|
* ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR.
|
||||||
*
|
*
|
||||||
* This class is a convenient wrapper around `StreamOutput`.
|
* This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR.
|
||||||
*
|
*
|
||||||
* $output = new ConsoleOutput();
|
* $output = new ConsoleOutput();
|
||||||
*
|
*
|
||||||
* This is equivalent to:
|
* This is equivalent to:
|
||||||
*
|
*
|
||||||
* $output = new StreamOutput(fopen('php://stdout', 'w'));
|
* $output = new StreamOutput(fopen('php://stdout', 'w'));
|
||||||
|
* $stdErr = new StreamOutput(fopen('php://stderr', 'w'));
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
@ -139,9 +140,11 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
|
|||||||
*/
|
*/
|
||||||
private function openOutputStream()
|
private function openOutputStream()
|
||||||
{
|
{
|
||||||
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
|
if (!$this->hasStdoutSupport()) {
|
||||||
|
return fopen('php://output', 'w');
|
||||||
|
}
|
||||||
|
|
||||||
return @fopen($outputStream, 'w') ?: fopen('php://output', 'w');
|
return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,8 +152,6 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
|
|||||||
*/
|
*/
|
||||||
private function openErrorStream()
|
private function openErrorStream()
|
||||||
{
|
{
|
||||||
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
|
return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
|
||||||
|
|
||||||
return fopen($errorStream, 'w');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Console\Tester;
|
|||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Output\StreamOutput;
|
use Symfony\Component\Console\Output\StreamOutput;
|
||||||
|
|
||||||
@ -31,14 +32,13 @@ class ApplicationTester
|
|||||||
{
|
{
|
||||||
private $application;
|
private $application;
|
||||||
private $input;
|
private $input;
|
||||||
private $output;
|
|
||||||
private $statusCode;
|
private $statusCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* @var OutputInterface
|
||||||
*
|
|
||||||
* @param Application $application An Application instance to test.
|
|
||||||
*/
|
*/
|
||||||
|
private $output;
|
||||||
|
private $captureStreamsIndependently = false;
|
||||||
|
|
||||||
public function __construct(Application $application)
|
public function __construct(Application $application)
|
||||||
{
|
{
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
@ -49,9 +49,10 @@ class ApplicationTester
|
|||||||
*
|
*
|
||||||
* Available options:
|
* Available options:
|
||||||
*
|
*
|
||||||
* * interactive: Sets the input interactive flag
|
* * interactive: Sets the input interactive flag
|
||||||
* * decorated: Sets the output decorated flag
|
* * decorated: Sets the output decorated flag
|
||||||
* * verbosity: Sets the output verbosity flag
|
* * verbosity: Sets the output verbosity flag
|
||||||
|
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
|
||||||
*
|
*
|
||||||
* @param array $input An array of arguments and options
|
* @param array $input An array of arguments and options
|
||||||
* @param array $options An array of options
|
* @param array $options An array of options
|
||||||
@ -65,12 +66,35 @@ class ApplicationTester
|
|||||||
$this->input->setInteractive($options['interactive']);
|
$this->input->setInteractive($options['interactive']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
|
$this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
|
||||||
if (isset($options['decorated'])) {
|
if (!$this->captureStreamsIndependently) {
|
||||||
$this->output->setDecorated($options['decorated']);
|
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
|
||||||
}
|
if (isset($options['decorated'])) {
|
||||||
if (isset($options['verbosity'])) {
|
$this->output->setDecorated($options['decorated']);
|
||||||
$this->output->setVerbosity($options['verbosity']);
|
}
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->statusCode = $this->application->run($this->input, $this->output);
|
return $this->statusCode = $this->application->run($this->input, $this->output);
|
||||||
@ -96,6 +120,30 @@ class ApplicationTester
|
|||||||
return $display;
|
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 application.
|
* Gets the input instance used by the last execution of the application.
|
||||||
*
|
*
|
||||||
|
@ -485,9 +485,14 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$application->setCatchExceptions(true);
|
$application->setCatchExceptions(true);
|
||||||
$this->assertTrue($application->areExceptionsCaught());
|
$this->assertTrue($application->areExceptionsCaught());
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag');
|
||||||
|
|
||||||
|
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag');
|
||||||
|
$this->assertSame('', $tester->getDisplay(true));
|
||||||
|
|
||||||
$application->setCatchExceptions(false);
|
$application->setCatchExceptions(false);
|
||||||
try {
|
try {
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
||||||
@ -516,19 +521,19 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
->will($this->returnValue(120));
|
->will($this->returnValue(120));
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception');
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
|
$tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true));
|
||||||
$this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose');
|
$this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose');
|
||||||
|
|
||||||
$tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false));
|
$tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command');
|
||||||
|
|
||||||
$application->add(new \Foo3Command());
|
$application->add(new \Foo3Command());
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
$tester->run(array('command' => 'foo3:bar'), array('decorated' => false));
|
$tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
|
$tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
|
||||||
$this->assertRegExp('/\[Exception\]\s*First exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is default and verbosity is verbose');
|
$this->assertRegExp('/\[Exception\]\s*First exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is default and verbosity is verbose');
|
||||||
@ -538,6 +543,9 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
$tester->run(array('command' => 'foo3:bar'), array('decorated' => true));
|
$tester->run(array('command' => 'foo3:bar'), array('decorated' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
|
$tester->run(array('command' => 'foo3:bar'), array('decorated' => true, 'capture_stderr_separately' => true));
|
||||||
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
|
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
|
||||||
$application->setAutoExit(false);
|
$application->setAutoExit(false);
|
||||||
$application->expects($this->any())
|
$application->expects($this->any())
|
||||||
@ -545,8 +553,8 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
->will($this->returnValue(32));
|
->will($this->returnValue(32));
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRenderExceptionWithDoubleWidthCharacters()
|
public function testRenderExceptionWithDoubleWidthCharacters()
|
||||||
@ -561,11 +569,11 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
});
|
});
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => true));
|
$tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
|
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
|
||||||
$application->setAutoExit(false);
|
$application->setAutoExit(false);
|
||||||
@ -576,8 +584,8 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
throw new \Exception('コマンドの実行中にエラーが発生しました。');
|
throw new \Exception('コマンドの実行中にエラーが発生しました。');
|
||||||
});
|
});
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
$tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRun()
|
public function testRun()
|
||||||
@ -702,8 +710,8 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
$application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
|
$application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
|
||||||
$application->setAutoExit(false);
|
$application->setAutoExit(false);
|
||||||
$application->expects($this->once())
|
$application->expects($this->once())
|
||||||
->method('doRun')
|
->method('doRun')
|
||||||
->will($this->throwException($exception));
|
->will($this->throwException($exception));
|
||||||
|
|
||||||
$exitCode = $application->run(new ArrayInput(array()), new NullOutput());
|
$exitCode = $application->run(new ArrayInput(array()), new NullOutput());
|
||||||
|
|
||||||
@ -717,8 +725,8 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
$application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
|
$application = $this->getMock('Symfony\Component\Console\Application', array('doRun'));
|
||||||
$application->setAutoExit(false);
|
$application->setAutoExit(false);
|
||||||
$application->expects($this->once())
|
$application->expects($this->once())
|
||||||
->method('doRun')
|
->method('doRun')
|
||||||
->will($this->throwException($exception));
|
->will($this->throwException($exception));
|
||||||
|
|
||||||
$exitCode = $application->run(new ArrayInput(array()), new NullOutput());
|
$exitCode = $application->run(new ArrayInput(array()), new NullOutput());
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user