First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Input\StreamableInputInterface;
abstract class AbstractQuestionHelperTest extends TestCase
{
protected function createStreamableInputInterfaceMock($stream = null, $interactive = true)
{
$mock = $this->getMockBuilder(StreamableInputInterface::class)->getMock();
$mock->expects($this->any())
->method('isInteractive')
->will($this->returnValue($interactive));
if ($stream) {
$mock->expects($this->any())
->method('getStream')
->willReturn($stream);
}
return $mock;
}
}

View file

@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\FormatterHelper;
class FormatterHelperTest extends TestCase
{
public function testFormatSection()
{
$formatter = new FormatterHelper();
$this->assertEquals(
'<info>[cli]</info> Some text to display',
$formatter->formatSection('cli', 'Some text to display'),
'::formatSection() formats a message in a section'
);
}
public function testFormatBlock()
{
$formatter = new FormatterHelper();
$this->assertEquals(
'<error> Some text to display </error>',
$formatter->formatBlock('Some text to display', 'error'),
'::formatBlock() formats a message in a block'
);
$this->assertEquals(
'<error> Some text to display </error>'."\n".
'<error> foo bar </error>',
$formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'),
'::formatBlock() formats a message in a block'
);
$this->assertEquals(
'<error> </error>'."\n".
'<error> Some text to display </error>'."\n".
'<error> </error>',
$formatter->formatBlock('Some text to display', 'error', true),
'::formatBlock() formats a message in a block'
);
}
public function testFormatBlockWithDiacriticLetters()
{
$formatter = new FormatterHelper();
$this->assertEquals(
'<error> </error>'."\n".
'<error> Du texte à afficher </error>'."\n".
'<error> </error>',
$formatter->formatBlock('Du texte à afficher', 'error', true),
'::formatBlock() formats a message in a block'
);
}
public function testFormatBlockWithDoubleWidthDiacriticLetters()
{
$formatter = new FormatterHelper();
$this->assertEquals(
'<error> </error>'."\n".
'<error> 表示するテキスト </error>'."\n".
'<error> </error>',
$formatter->formatBlock('表示するテキスト', 'error', true),
'::formatBlock() formats a message in a block'
);
}
public function testFormatBlockLGEscaping()
{
$formatter = new FormatterHelper();
$this->assertEquals(
'<error> </error>'."\n".
'<error> \<info>some info\</info> </error>'."\n".
'<error> </error>',
$formatter->formatBlock('<info>some info</info>', 'error', true),
'::formatBlock() escapes \'<\' chars'
);
}
public function testTruncatingWithShorterLengthThanMessageWithSuffix()
{
$formatter = new FormatterHelper();
$message = 'testing truncate';
$this->assertSame('test...', $formatter->truncate($message, 4));
$this->assertSame('testing truncat...', $formatter->truncate($message, 15));
$this->assertSame('testing truncate...', $formatter->truncate($message, 16));
$this->assertSame('zażółć gęślą...', $formatter->truncate('zażółć gęślą jaźń', 12));
}
public function testTruncatingMessageWithCustomSuffix()
{
$formatter = new FormatterHelper();
$message = 'testing truncate';
$this->assertSame('test!', $formatter->truncate($message, 4, '!'));
}
public function testTruncatingWithLongerLengthThanMessageWithSuffix()
{
$formatter = new FormatterHelper();
$message = 'test';
$this->assertSame($message, $formatter->truncate($message, 10));
}
public function testTruncatingWithNegativeLength()
{
$formatter = new FormatterHelper();
$message = 'testing truncate';
$this->assertSame('testing tru...', $formatter->truncate($message, -5));
$this->assertSame('...', $formatter->truncate($message, -100));
}
}

View file

@ -0,0 +1,127 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Command\Command;
class HelperSetTest extends TestCase
{
public function testConstructor()
{
$mock_helper = $this->getGenericMockHelper('fake_helper');
$helperset = new HelperSet(array('fake_helper_alias' => $mock_helper));
$this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers');
$this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper');
}
public function testSet()
{
$helperset = new HelperSet();
$helperset->set($this->getGenericMockHelper('fake_helper', $helperset));
$this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers');
$helperset = new HelperSet();
$helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset));
$helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset));
$this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls');
$this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls');
$helperset = new HelperSet();
$helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias');
$this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set');
$this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set');
}
public function testHas()
{
$helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper')));
$this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper');
$this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias');
}
public function testGet()
{
$helper_01 = $this->getGenericMockHelper('fake_helper_01');
$helper_02 = $this->getGenericMockHelper('fake_helper_02');
$helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02));
$this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name');
$this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias');
$this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name');
$this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias');
$helperset = new HelperSet();
try {
$helperset->get('foo');
$this->fail('->get() throws InvalidArgumentException when helper not found');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found');
$this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found');
$this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found');
}
}
public function testSetCommand()
{
$cmd_01 = new Command('foo');
$cmd_02 = new Command('bar');
$helperset = new HelperSet();
$helperset->setCommand($cmd_01);
$this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command');
$helperset = new HelperSet();
$helperset->setCommand($cmd_01);
$helperset->setCommand($cmd_02);
$this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls');
}
public function testGetCommand()
{
$cmd = new Command('foo');
$helperset = new HelperSet();
$helperset->setCommand($cmd);
$this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command');
}
public function testIteration()
{
$helperset = new HelperSet();
$helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset));
$helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset));
$helpers = array('fake_helper_01', 'fake_helper_02');
$i = 0;
foreach ($helperset as $helper) {
$this->assertEquals($helpers[$i++], $helper->getName());
}
}
private function getGenericMockHelper($name, HelperSet $helperset = null)
{
$mock_helper = $this->getMockBuilder('\Symfony\Component\Console\Helper\HelperInterface')->getMock();
$mock_helper->expects($this->any())
->method('getName')
->will($this->returnValue($name));
if ($helperset) {
$mock_helper->expects($this->any())
->method('setHelperSet')
->with($this->equalTo($helperset));
}
return $mock_helper;
}
}

View file

@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\Helper;
class HelperTest extends TestCase
{
public function formatTimeProvider()
{
return array(
array(0, '< 1 sec'),
array(1, '1 sec'),
array(2, '2 secs'),
array(59, '59 secs'),
array(60, '1 min'),
array(61, '1 min'),
array(119, '1 min'),
array(120, '2 mins'),
array(121, '2 mins'),
array(3599, '59 mins'),
array(3600, '1 hr'),
array(7199, '1 hr'),
array(7200, '2 hrs'),
array(7201, '2 hrs'),
array(86399, '23 hrs'),
array(86400, '1 day'),
array(86401, '1 day'),
array(172799, '1 day'),
array(172800, '2 days'),
array(172801, '2 days'),
);
}
/**
* @dataProvider formatTimeProvider
*
* @param int $secs
* @param string $expectedFormat
*/
public function testFormatTime($secs, $expectedFormat)
{
$this->assertEquals($expectedFormat, Helper::formatTime($secs));
}
}

View file

@ -0,0 +1,118 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Process\Process;
class ProcessHelperTest extends TestCase
{
/**
* @dataProvider provideCommandsAndOutput
*/
public function testVariousProcessRuns($expected, $cmd, $verbosity, $error)
{
$helper = new ProcessHelper();
$helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper())));
$output = $this->getOutputStream($verbosity);
$helper->run($output, $cmd, $error);
$this->assertEquals($expected, $this->getOutput($output));
}
public function testPassedCallbackIsExecuted()
{
$helper = new ProcessHelper();
$helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper())));
$output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL);
$executed = false;
$callback = function () use (&$executed) { $executed = true; };
$helper->run($output, 'php -r "echo 42;"', null, $callback);
$this->assertTrue($executed);
}
public function provideCommandsAndOutput()
{
$successOutputVerbose = <<<'EOT'
RUN php -r "echo 42;"
RES Command ran successfully
EOT;
$successOutputDebug = <<<'EOT'
RUN php -r "echo 42;"
OUT 42
RES Command ran successfully
EOT;
$successOutputDebugWithTags = <<<'EOT'
RUN php -r "echo '<info>42</info>';"
OUT <info>42</info>
RES Command ran successfully
EOT;
$successOutputProcessDebug = <<<'EOT'
RUN 'php' '-r' 'echo 42;'
OUT 42
RES Command ran successfully
EOT;
$syntaxErrorOutputVerbose = <<<'EOT'
RUN php -r "fwrite(STDERR, 'error message');usleep(50000);fwrite(STDOUT, 'out message');exit(252);"
RES 252 Command did not run successfully
EOT;
$syntaxErrorOutputDebug = <<<'EOT'
RUN php -r "fwrite(STDERR, 'error message');usleep(500000);fwrite(STDOUT, 'out message');exit(252);"
ERR error message
OUT out message
RES 252 Command did not run successfully
EOT;
$errorMessage = 'An error occurred';
$args = new Process(array('php', '-r', 'echo 42;'));
$args = $args->getCommandLine();
$successOutputProcessDebug = str_replace("'php' '-r' 'echo 42;'", $args, $successOutputProcessDebug);
return array(
array('', 'php -r "echo 42;"', StreamOutput::VERBOSITY_VERBOSE, null),
array($successOutputVerbose, 'php -r "echo 42;"', StreamOutput::VERBOSITY_VERY_VERBOSE, null),
array($successOutputDebug, 'php -r "echo 42;"', StreamOutput::VERBOSITY_DEBUG, null),
array($successOutputDebugWithTags, 'php -r "echo \'<info>42</info>\';"', StreamOutput::VERBOSITY_DEBUG, null),
array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null),
array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null),
array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null),
array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage),
array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage),
array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage),
array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null),
array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null),
);
}
private function getOutputStream($verbosity)
{
return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false);
}
private function getOutput(StreamOutput $output)
{
rewind($output->getStream());
return stream_get_contents($output->getStream());
}
}

View file

@ -0,0 +1,792 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Output\StreamOutput;
/**
* @group time-sensitive
*/
class ProgressBarTest extends TestCase
{
public function testMultipleStart()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance();
$bar->start();
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]').
$this->generateOutput(' 0 [>---------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testAdvance()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testAdvanceWithStep()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance(5);
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 5 [----->----------------------]'),
stream_get_contents($output->getStream())
);
}
public function testAdvanceMultipleTimes()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance(3);
$bar->advance(2);
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 3 [--->------------------------]').
$this->generateOutput(' 5 [----->----------------------]'),
stream_get_contents($output->getStream())
);
}
public function testAdvanceOverMax()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->setProgress(9);
$bar->advance();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 9/10 [=========================>--] 90%'.
$this->generateOutput(' 10/10 [============================] 100%').
$this->generateOutput(' 11/11 [============================] 100%'),
stream_get_contents($output->getStream())
);
}
public function testRegress()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance();
$bar->advance();
$bar->advance(-1);
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]').
$this->generateOutput(' 2 [-->-------------------------]').
$this->generateOutput(' 1 [->--------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testRegressWithStep()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance(4);
$bar->advance(4);
$bar->advance(-2);
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 4 [---->-----------------------]').
$this->generateOutput(' 8 [-------->-------------------]').
$this->generateOutput(' 6 [------>---------------------]'),
stream_get_contents($output->getStream())
);
}
public function testRegressMultipleTimes()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->advance(3);
$bar->advance(3);
$bar->advance(-1);
$bar->advance(-2);
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 3 [--->------------------------]').
$this->generateOutput(' 6 [------>---------------------]').
$this->generateOutput(' 5 [----->----------------------]').
$this->generateOutput(' 3 [--->------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testRegressBelowMin()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->setProgress(1);
$bar->advance(-1);
$bar->advance(-1);
rewind($output->getStream());
$this->assertEquals(
' 1/10 [==>-------------------------] 10%'.
$this->generateOutput(' 0/10 [>---------------------------] 0%'),
stream_get_contents($output->getStream())
);
}
public function testFormat()
{
$expected =
' 0/10 [>---------------------------] 0%'.
$this->generateOutput(' 10/10 [============================] 100%').
$this->generateOutput(' 10/10 [============================] 100%')
;
// max in construct, no format
$bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->start();
$bar->advance(10);
$bar->finish();
rewind($output->getStream());
$this->assertEquals($expected, stream_get_contents($output->getStream()));
// max in start, no format
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start(10);
$bar->advance(10);
$bar->finish();
rewind($output->getStream());
$this->assertEquals($expected, stream_get_contents($output->getStream()));
// max in construct, explicit format before
$bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->setFormat('normal');
$bar->start();
$bar->advance(10);
$bar->finish();
rewind($output->getStream());
$this->assertEquals($expected, stream_get_contents($output->getStream()));
// max in start, explicit format before
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setFormat('normal');
$bar->start(10);
$bar->advance(10);
$bar->finish();
rewind($output->getStream());
$this->assertEquals($expected, stream_get_contents($output->getStream()));
}
public function testCustomizations()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->setBarWidth(10);
$bar->setBarCharacter('_');
$bar->setEmptyBarCharacter(' ');
$bar->setProgressCharacter('/');
$bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
$bar->start();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0/10 [/ ] 0%'.
$this->generateOutput(' 1/10 [_/ ] 10%'),
stream_get_contents($output->getStream())
);
}
public function testDisplayWithoutStart()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->display();
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------] 0%',
stream_get_contents($output->getStream())
);
}
public function testDisplayWithQuietVerbosity()
{
$bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50);
$bar->display();
rewind($output->getStream());
$this->assertEquals(
'',
stream_get_contents($output->getStream())
);
}
public function testFinishWithoutStart()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
' 50/50 [============================] 100%',
stream_get_contents($output->getStream())
);
}
public function testPercent()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->start();
$bar->display();
$bar->advance();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 0/50 [>---------------------------] 0%').
$this->generateOutput(' 1/50 [>---------------------------] 2%').
$this->generateOutput(' 2/50 [=>--------------------------] 4%'),
stream_get_contents($output->getStream())
);
}
public function testOverwriteWithShorterLine()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%');
$bar->start();
$bar->display();
$bar->advance();
// set shorter format
$bar->setFormat(' %current%/%max% [%bar%]');
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 0/50 [>---------------------------] 0%').
$this->generateOutput(' 1/50 [>---------------------------] 2%').
$this->generateOutput(' 2/50 [=>--------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testStartWithMax()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setFormat('%current%/%max% [%bar%]');
$bar->start(50);
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------]'.
$this->generateOutput(' 1/50 [>---------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testSetCurrentProgress()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->start();
$bar->display();
$bar->advance();
$bar->setProgress(15);
$bar->setProgress(25);
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 0/50 [>---------------------------] 0%').
$this->generateOutput(' 1/50 [>---------------------------] 2%').
$this->generateOutput(' 15/50 [========>-------------------] 30%').
$this->generateOutput(' 25/50 [==============>-------------] 50%'),
stream_get_contents($output->getStream())
);
}
public function testSetCurrentBeforeStarting()
{
$bar = new ProgressBar($this->getOutputStream());
$bar->setProgress(15);
$this->assertNotNull($bar->getStartTime());
}
public function testRedrawFrequency()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 6);
$bar->setRedrawFrequency(2);
$bar->start();
$bar->setProgress(1);
$bar->advance(2);
$bar->advance(2);
$bar->advance(1);
rewind($output->getStream());
$this->assertEquals(
' 0/6 [>---------------------------] 0%'.
$this->generateOutput(' 3/6 [==============>-------------] 50%').
$this->generateOutput(' 5/6 [=======================>----] 83%').
$this->generateOutput(' 6/6 [============================] 100%'),
stream_get_contents($output->getStream())
);
}
public function testRedrawFrequencyIsAtLeastOneIfZeroGiven()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setRedrawFrequency(0);
$bar->start();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setRedrawFrequency(0.9);
$bar->start();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 1 [->--------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testMultiByteSupport()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->start();
$bar->setBarCharacter('■');
$bar->advance(3);
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.
$this->generateOutput(' 3 [■■■>------------------------]'),
stream_get_contents($output->getStream())
);
}
public function testClear()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 50);
$bar->start();
$bar->setProgress(25);
$bar->clear();
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------] 0%'.
$this->generateOutput(' 25/50 [==============>-------------] 50%').
$this->generateOutput(''),
stream_get_contents($output->getStream())
);
}
public function testPercentNotHundredBeforeComplete()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 200);
$bar->start();
$bar->display();
$bar->advance(199);
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0/200 [>---------------------------] 0%'.
$this->generateOutput(' 0/200 [>---------------------------] 0%').
$this->generateOutput(' 199/200 [===========================>] 99%').
$this->generateOutput(' 200/200 [============================] 100%'),
stream_get_contents($output->getStream())
);
}
public function testNonDecoratedOutput()
{
$bar = new ProgressBar($output = $this->getOutputStream(false), 200);
$bar->start();
for ($i = 0; $i < 200; ++$i) {
$bar->advance();
}
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
' 0/200 [>---------------------------] 0%'.PHP_EOL.
' 20/200 [==>-------------------------] 10%'.PHP_EOL.
' 40/200 [=====>----------------------] 20%'.PHP_EOL.
' 60/200 [========>-------------------] 30%'.PHP_EOL.
' 80/200 [===========>----------------] 40%'.PHP_EOL.
' 100/200 [==============>-------------] 50%'.PHP_EOL.
' 120/200 [================>-----------] 60%'.PHP_EOL.
' 140/200 [===================>--------] 70%'.PHP_EOL.
' 160/200 [======================>-----] 80%'.PHP_EOL.
' 180/200 [=========================>--] 90%'.PHP_EOL.
' 200/200 [============================] 100%',
stream_get_contents($output->getStream())
);
}
public function testNonDecoratedOutputWithClear()
{
$bar = new ProgressBar($output = $this->getOutputStream(false), 50);
$bar->start();
$bar->setProgress(25);
$bar->clear();
$bar->setProgress(50);
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
' 0/50 [>---------------------------] 0%'.PHP_EOL.
' 25/50 [==============>-------------] 50%'.PHP_EOL.
' 50/50 [============================] 100%',
stream_get_contents($output->getStream())
);
}
public function testNonDecoratedOutputWithoutMax()
{
$bar = new ProgressBar($output = $this->getOutputStream(false));
$bar->start();
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]'.PHP_EOL.
' 1 [->--------------------------]',
stream_get_contents($output->getStream())
);
}
public function testParallelBars()
{
$output = $this->getOutputStream();
$bar1 = new ProgressBar($output, 2);
$bar2 = new ProgressBar($output, 3);
$bar2->setProgressCharacter('#');
$bar3 = new ProgressBar($output);
$bar1->start();
$output->write("\n");
$bar2->start();
$output->write("\n");
$bar3->start();
for ($i = 1; $i <= 3; ++$i) {
// up two lines
$output->write("\033[2A");
if ($i <= 2) {
$bar1->advance();
}
$output->write("\n");
$bar2->advance();
$output->write("\n");
$bar3->advance();
}
$output->write("\033[2A");
$output->write("\n");
$output->write("\n");
$bar3->finish();
rewind($output->getStream());
$this->assertEquals(
' 0/2 [>---------------------------] 0%'."\n".
' 0/3 [#---------------------------] 0%'."\n".
rtrim(' 0 [>---------------------------]').
"\033[2A".
$this->generateOutput(' 1/2 [==============>-------------] 50%')."\n".
$this->generateOutput(' 1/3 [=========#------------------] 33%')."\n".
rtrim($this->generateOutput(' 1 [->--------------------------]')).
"\033[2A".
$this->generateOutput(' 2/2 [============================] 100%')."\n".
$this->generateOutput(' 2/3 [==================#---------] 66%')."\n".
rtrim($this->generateOutput(' 2 [-->-------------------------]')).
"\033[2A".
"\n".
$this->generateOutput(' 3/3 [============================] 100%')."\n".
rtrim($this->generateOutput(' 3 [--->------------------------]')).
"\033[2A".
"\n".
"\n".
rtrim($this->generateOutput(' 3 [============================]')),
stream_get_contents($output->getStream())
);
}
public function testWithoutMax()
{
$output = $this->getOutputStream();
$bar = new ProgressBar($output);
$bar->start();
$bar->advance();
$bar->advance();
$bar->advance();
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
rtrim(' 0 [>---------------------------]').
rtrim($this->generateOutput(' 1 [->--------------------------]')).
rtrim($this->generateOutput(' 2 [-->-------------------------]')).
rtrim($this->generateOutput(' 3 [--->------------------------]')).
rtrim($this->generateOutput(' 3 [============================]')),
stream_get_contents($output->getStream())
);
}
public function testWithSmallScreen()
{
$output = $this->getOutputStream();
$bar = new ProgressBar($output);
putenv('COLUMNS=12');
$bar->start();
$bar->advance();
putenv('COLUMNS=120');
rewind($output->getStream());
$this->assertEquals(
' 0 [>---]'.
$this->generateOutput(' 1 [->--]'),
stream_get_contents($output->getStream())
);
}
public function testAddingPlaceholderFormatter()
{
ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) {
return $bar->getMaxSteps() - $bar->getProgress();
});
$bar = new ProgressBar($output = $this->getOutputStream(), 3);
$bar->setFormat(' %remaining_steps% [%bar%]');
$bar->start();
$bar->advance();
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
' 3 [>---------------------------]'.
$this->generateOutput(' 2 [=========>------------------]').
$this->generateOutput(' 0 [============================]'),
stream_get_contents($output->getStream())
);
}
public function testMultilineFormat()
{
$bar = new ProgressBar($output = $this->getOutputStream(), 3);
$bar->setFormat("%bar%\nfoobar");
$bar->start();
$bar->advance();
$bar->clear();
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
">---------------------------\nfoobar".
$this->generateOutput("=========>------------------\nfoobar").
"\x0D\x1B[2K\x1B[1A\x1B[2K".
$this->generateOutput("============================\nfoobar"),
stream_get_contents($output->getStream())
);
}
public function testAnsiColorsAndEmojis()
{
putenv('COLUMNS=156');
$bar = new ProgressBar($output = $this->getOutputStream(), 15);
ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
static $i = 0;
$mem = 100000 * $i;
$colors = $i++ ? '41;37' : '44;37';
return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m";
});
$bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
$bar->setBarCharacter($done = "\033[32m●\033[0m");
$bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
$bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
$bar->setMessage('Starting the demo... fingers crossed', 'title');
$bar->start();
rewind($output->getStream());
$this->assertEquals(
" \033[44;37m Starting the demo... fingers crossed \033[0m\n".
' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n".
" \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m",
stream_get_contents($output->getStream())
);
ftruncate($output->getStream(), 0);
rewind($output->getStream());
$bar->setMessage('Looks good to me...', 'title');
$bar->advance(4);
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(
" \033[44;37m Looks good to me... \033[0m\n".
' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
" \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m"
),
stream_get_contents($output->getStream())
);
ftruncate($output->getStream(), 0);
rewind($output->getStream());
$bar->setMessage('Thanks, bye', 'title');
$bar->finish();
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(
" \033[44;37m Thanks, bye \033[0m\n".
' 15/15 '.str_repeat($done, 28)." 100%\n".
" \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m"
),
stream_get_contents($output->getStream())
);
putenv('COLUMNS=120');
}
public function testSetFormat()
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setFormat('normal');
$bar->start();
rewind($output->getStream());
$this->assertEquals(
' 0 [>---------------------------]',
stream_get_contents($output->getStream())
);
$bar = new ProgressBar($output = $this->getOutputStream(), 10);
$bar->setFormat('normal');
$bar->start();
rewind($output->getStream());
$this->assertEquals(
' 0/10 [>---------------------------] 0%',
stream_get_contents($output->getStream())
);
}
/**
* @dataProvider provideFormat
*/
public function testFormatsWithoutMax($format)
{
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setFormat($format);
$bar->start();
rewind($output->getStream());
$this->assertNotEmpty(stream_get_contents($output->getStream()));
}
/**
* Provides each defined format.
*
* @return array
*/
public function provideFormat()
{
return array(
array('normal'),
array('verbose'),
array('very_verbose'),
array('debug'),
);
}
protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
{
return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
}
protected function generateOutput($expected)
{
$count = substr_count($expected, "\n");
return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected;
}
public function testBarWidthWithMultilineFormat()
{
putenv('COLUMNS=10');
$bar = new ProgressBar($output = $this->getOutputStream());
$bar->setFormat("%bar%\n0123456789");
// before starting
$bar->setBarWidth(5);
$this->assertEquals(5, $bar->getBarWidth());
// after starting
$bar->start();
rewind($output->getStream());
$this->assertEquals(5, $bar->getBarWidth(), stream_get_contents($output->getStream()));
putenv('COLUMNS=120');
}
}

View file

@ -0,0 +1,183 @@
<?php
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\ProgressIndicator;
use Symfony\Component\Console\Output\StreamOutput;
/**
* @group time-sensitive
*/
class ProgressIndicatorTest extends TestCase
{
public function testDefaultIndicator()
{
$bar = new ProgressIndicator($output = $this->getOutputStream());
$bar->start('Starting...');
usleep(101000);
$bar->advance();
usleep(101000);
$bar->advance();
usleep(101000);
$bar->advance();
usleep(101000);
$bar->advance();
usleep(101000);
$bar->advance();
usleep(101000);
$bar->setMessage('Advancing...');
$bar->advance();
$bar->finish('Done...');
$bar->start('Starting Again...');
usleep(101000);
$bar->advance();
$bar->finish('Done Again...');
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' - Starting...').
$this->generateOutput(' \\ Starting...').
$this->generateOutput(' | Starting...').
$this->generateOutput(' / Starting...').
$this->generateOutput(' - Starting...').
$this->generateOutput(' \\ Starting...').
$this->generateOutput(' \\ Advancing...').
$this->generateOutput(' | Advancing...').
$this->generateOutput(' | Done...').
PHP_EOL.
$this->generateOutput(' - Starting Again...').
$this->generateOutput(' \\ Starting Again...').
$this->generateOutput(' \\ Done Again...').
PHP_EOL,
stream_get_contents($output->getStream())
);
}
public function testNonDecoratedOutput()
{
$bar = new ProgressIndicator($output = $this->getOutputStream(false));
$bar->start('Starting...');
$bar->advance();
$bar->advance();
$bar->setMessage('Midway...');
$bar->advance();
$bar->advance();
$bar->finish('Done...');
rewind($output->getStream());
$this->assertEquals(
' Starting...'.PHP_EOL.
' Midway...'.PHP_EOL.
' Done...'.PHP_EOL.PHP_EOL,
stream_get_contents($output->getStream())
);
}
public function testCustomIndicatorValues()
{
$bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c'));
$bar->start('Starting...');
usleep(101000);
$bar->advance();
usleep(101000);
$bar->advance();
usleep(101000);
$bar->advance();
rewind($output->getStream());
$this->assertEquals(
$this->generateOutput(' a Starting...').
$this->generateOutput(' b Starting...').
$this->generateOutput(' c Starting...').
$this->generateOutput(' a Starting...'),
stream_get_contents($output->getStream())
);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Must have at least 2 indicator value characters.
*/
public function testCannotSetInvalidIndicatorCharacters()
{
$bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1'));
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Progress indicator already started.
*/
public function testCannotStartAlreadyStartedIndicator()
{
$bar = new ProgressIndicator($this->getOutputStream());
$bar->start('Starting...');
$bar->start('Starting Again.');
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Progress indicator has not yet been started.
*/
public function testCannotAdvanceUnstartedIndicator()
{
$bar = new ProgressIndicator($this->getOutputStream());
$bar->advance();
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Progress indicator has not yet been started.
*/
public function testCannotFinishUnstartedIndicator()
{
$bar = new ProgressIndicator($this->getOutputStream());
$bar->finish('Finished');
}
/**
* @dataProvider provideFormat
*/
public function testFormats($format)
{
$bar = new ProgressIndicator($output = $this->getOutputStream(), $format);
$bar->start('Starting...');
$bar->advance();
rewind($output->getStream());
$this->assertNotEmpty(stream_get_contents($output->getStream()));
}
/**
* Provides each defined format.
*
* @return array
*/
public function provideFormat()
{
return array(
array('normal'),
array('verbose'),
array('very_verbose'),
array('debug'),
);
}
protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
{
return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
}
protected function generateOutput($expected)
{
$count = substr_count($expected, "\n");
return "\x0D\x1B[2K".($count ? sprintf("\033[%dA", $count) : '').$expected;
}
}

View file

@ -0,0 +1,936 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* @group tty
*/
class QuestionHelperTest extends AbstractQuestionHelperTest
{
public function testAskChoice()
{
$questionHelper = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$questionHelper->setHelperSet($helperSet);
$heroes = array('Superman', 'Batman', 'Spiderman');
$inputStream = $this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n");
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
$question->setMaxAttempts(1);
// first answer is an empty answer, we're supposed to receive the default value
$this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setMaxAttempts(1);
$this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setErrorMessage('Input "%s" is not a superhero!');
$question->setMaxAttempts(2);
$this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
rewind($output->getStream());
$stream = stream_get_contents($output->getStream());
$this->assertContains('Input "Fabien" is not a superhero!', $stream);
try {
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
$question->setMaxAttempts(1);
$questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
}
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, 0);
// We are supposed to get the default value since we are not in interactive mode
$this->assertEquals('Superman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, true), $this->createOutputInterface(), $question));
}
public function testAsk()
{
$dialog = new QuestionHelper();
$inputStream = $this->getInputStream("\n8AM\n");
$question = new Question('What time is it?', '2PM');
$this->assertEquals('2PM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new Question('What time is it?', '2PM');
$this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
rewind($output->getStream());
$this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
}
public function testAskWithAutocomplete()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
// Acm<NEWLINE>
// Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
// <NEWLINE>
// <UP ARROW><UP ARROW><NEWLINE>
// <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
// <DOWN ARROW><NEWLINE>
// S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
// F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
$inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n");
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new Question('Please select a bundle', 'FrameworkBundle');
$question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
public function testAskWithAutocompleteWithNonSequentialKeys()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
// <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE>
$inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n");
$dialog = new QuestionHelper();
$dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
$question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle'));
$question->setMaxAttempts(1);
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
public function testAutocompleteWithTrailingBackslash()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
$inputStream = $this->getInputStream('E');
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new Question('');
$expectedCompletion = 'ExampleNamespace\\';
$question->setAutocompleterValues(array($expectedCompletion));
$output = $this->createOutputInterface();
$dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output, $question);
$outputStream = $output->getStream();
rewind($outputStream);
$actualOutput = stream_get_contents($outputStream);
// Shell control (esc) sequences are not so important: we only care that
// <hl> tag is interpreted correctly and replaced
$irrelevantEscSequences = array(
"\0337" => '', // Save cursor position
"\0338" => '', // Restore cursor position
"\033[K" => '', // Clear line from cursor till the end
);
$importantActualOutput = strtr($actualOutput, $irrelevantEscSequences);
// Remove colors (e.g. "\033[30m", "\033[31;41m")
$importantActualOutput = preg_replace('/\033\[\d+(;\d+)?m/', '', $importantActualOutput);
$this->assertEquals($expectedCompletion, $importantActualOutput);
}
public function testAskHiddenResponse()
{
if ('\\' === DIRECTORY_SEPARATOR) {
$this->markTestSkipped('This test is not supported on Windows');
}
$dialog = new QuestionHelper();
$question = new Question('What time is it?');
$question->setHidden(true);
$this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("8AM\n")), $this->createOutputInterface(), $question));
}
/**
* @dataProvider getAskConfirmationData
*/
public function testAskConfirmation($question, $expected, $default = true)
{
$dialog = new QuestionHelper();
$inputStream = $this->getInputStream($question."\n");
$question = new ConfirmationQuestion('Do you like French fries?', $default);
$this->assertEquals($expected, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel'));
}
public function getAskConfirmationData()
{
return array(
array('', true),
array('', false, false),
array('y', true),
array('yes', true),
array('n', false),
array('no', false),
);
}
public function testAskConfirmationWithCustomTrueAnswer()
{
$dialog = new QuestionHelper();
$inputStream = $this->getInputStream("j\ny\n");
$question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
$this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
$this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
public function testAskAndValidate()
{
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$error = 'This is not a color!';
$validator = function ($color) use ($error) {
if (!in_array($color, array('white', 'black'))) {
throw new \InvalidArgumentException($error);
}
return $color;
};
$question = new Question('What color was the white horse of Henry IV?', 'white');
$question->setValidator($validator);
$question->setMaxAttempts(2);
$inputStream = $this->getInputStream("\nblack\n");
$this->assertEquals('white', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('black', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
try {
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("green\nyellow\norange\n")), $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals($error, $e->getMessage());
}
}
/**
* @dataProvider simpleAnswerProvider
*/
public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'My environment 1',
'My environment 2',
'My environment 3',
);
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
public function simpleAnswerProvider()
{
return array(
array(0, 'My environment 1'),
array(1, 'My environment 2'),
array(2, 'My environment 3'),
array('My environment 1', 'My environment 1'),
array('My environment 2', 'My environment 2'),
array('My environment 3', 'My environment 3'),
);
}
/**
* @dataProvider specialCharacterInMultipleChoice
*/
public function testSpecialCharacterChoiceFromMultipleChoiceList($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'.',
'src',
);
$dialog = new QuestionHelper();
$inputStream = $this->getInputStream($providedAnswer."\n");
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the directory', $possibleChoices);
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$answer = $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
public function specialCharacterInMultipleChoice()
{
return array(
array('.', array('.')),
array('., src', array('.', 'src')),
);
}
/**
* @dataProvider mixedKeysChoiceListAnswerProvider
*/
public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'0' => 'No environment',
'1' => 'My environment 1',
'env_2' => 'My environment 2',
3 => 'My environment 3',
);
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
public function mixedKeysChoiceListAnswerProvider()
{
return array(
array('0', '0'),
array('No environment', '0'),
array('1', '1'),
array('env_2', 'env_2'),
array(3, '3'),
array('My environment 1', '1'),
);
}
/**
* @dataProvider answerProvider
*/
public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'env_1' => 'My environment 1',
'env_2' => 'My environment',
'env_3' => 'My environment',
);
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3.
*/
public function testAmbiguousChoiceFromChoicelist()
{
$possibleChoices = array(
'env_1' => 'My first environment',
'env_2' => 'My environment',
'env_3' => 'My environment',
);
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("My environment\n")), $this->createOutputInterface(), $question);
}
public function answerProvider()
{
return array(
array('env_1', 'env_1'),
array('env_2', 'env_2'),
array('env_3', 'env_3'),
array('My environment 1', 'env_1'),
);
}
public function testNoInteraction()
{
$dialog = new QuestionHelper();
$question = new Question('Do you have a job?', 'not yet');
$this->assertEquals('not yet', $dialog->ask($this->createStreamableInputInterfaceMock(null, false), $this->createOutputInterface(), $question));
}
/**
* @requires function mb_strwidth
*/
public function testChoiceOutputFormattingQuestionForUtf8Keys()
{
$question = 'Lorem ipsum?';
$possibleChoices = array(
'foo' => 'foo',
'żółw' => 'bar',
'łabądź' => 'baz',
);
$outputShown = array(
$question,
' [<info>foo </info>] foo',
' [<info>żółw </info>] bar',
' [<info>łabądź</info>] baz',
);
$output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
$output->method('getFormatter')->willReturn(new OutputFormatter());
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown));
$question = new ChoiceQuestion($question, $possibleChoices, 'foo');
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("\n")), $output, $question);
}
/**
* @group legacy
*/
public function testLegacyAskChoice()
{
$questionHelper = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$questionHelper->setHelperSet($helperSet);
$heroes = array('Superman', 'Batman', 'Spiderman');
$questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
$question->setMaxAttempts(1);
// first answer is an empty answer, we're supposed to receive the default value
$this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setMaxAttempts(1);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setErrorMessage('Input "%s" is not a superhero!');
$question->setMaxAttempts(2);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
rewind($output->getStream());
$stream = stream_get_contents($output->getStream());
$this->assertContains('Input "Fabien" is not a superhero!', $stream);
try {
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
$question->setMaxAttempts(1);
$questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
}
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
}
/**
* @group legacy
*/
public function testLegacyAsk()
{
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream("\n8AM\n"));
$question = new Question('What time is it?', '2PM');
$this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new Question('What time is it?', '2PM');
$this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
rewind($output->getStream());
$this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
}
/**
* @group legacy
*/
public function testLegacyAskWithAutocomplete()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
// Acm<NEWLINE>
// Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
// <NEWLINE>
// <UP ARROW><UP ARROW><NEWLINE>
// <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
// <DOWN ARROW><NEWLINE>
// S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
// F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
$inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n");
$dialog = new QuestionHelper();
$dialog->setInputStream($inputStream);
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new Question('Please select a bundle', 'FrameworkBundle');
$question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
}
/**
* @group legacy
*/
public function testLegacyAskWithAutocompleteWithNonSequentialKeys()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
// <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE>
$inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n");
$dialog = new QuestionHelper();
$dialog->setInputStream($inputStream);
$dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
$question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle'));
$question->setMaxAttempts(1);
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
}
/**
* @group legacy
*/
public function testLegacyAskHiddenResponse()
{
if ('\\' === DIRECTORY_SEPARATOR) {
$this->markTestSkipped('This test is not supported on Windows');
}
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream("8AM\n"));
$question = new Question('What time is it?');
$question->setHidden(true);
$this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
}
/**
* @group legacy
* @dataProvider getAskConfirmationData
*/
public function testLegacyAskConfirmation($question, $expected, $default = true)
{
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream($question."\n"));
$question = new ConfirmationQuestion('Do you like French fries?', $default);
$this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel'));
}
/**
* @group legacy
*/
public function testLegacyAskConfirmationWithCustomTrueAnswer()
{
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream("j\ny\n"));
$question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
$this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
$this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
}
/**
* @group legacy
*/
public function testLegacyAskAndValidate()
{
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$error = 'This is not a color!';
$validator = function ($color) use ($error) {
if (!in_array($color, array('white', 'black'))) {
throw new \InvalidArgumentException($error);
}
return $color;
};
$question = new Question('What color was the white horse of Henry IV?', 'white');
$question->setValidator($validator);
$question->setMaxAttempts(2);
$dialog->setInputStream($this->getInputStream("\nblack\n"));
$this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$dialog->setInputStream($this->getInputStream("green\nyellow\norange\n"));
try {
$dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals($error, $e->getMessage());
}
}
/**
* @group legacy
* @dataProvider simpleAnswerProvider
*/
public function testLegacySelectChoiceFromSimpleChoices($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'My environment 1',
'My environment 2',
'My environment 3',
);
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
/**
* @group legacy
* @dataProvider mixedKeysChoiceListAnswerProvider
*/
public function testLegacyChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'0' => 'No environment',
'1' => 'My environment 1',
'env_2' => 'My environment 2',
3 => 'My environment 3',
);
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
/**
* @group legacy
* @dataProvider answerProvider
*/
public function testLegacySelectChoiceFromChoiceList($providedAnswer, $expectedValue)
{
$possibleChoices = array(
'env_1' => 'My environment 1',
'env_2' => 'My environment',
'env_3' => 'My environment',
);
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
$this->assertSame($expectedValue, $answer);
}
/**
* @group legacy
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3.
*/
public function testLegacyAmbiguousChoiceFromChoicelist()
{
$possibleChoices = array(
'env_1' => 'My first environment',
'env_2' => 'My environment',
'env_3' => 'My environment',
);
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream("My environment\n"));
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
$question->setMaxAttempts(1);
$dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
}
/**
* @requires function mb_strwidth
* @group legacy
*/
public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys()
{
$question = 'Lorem ipsum?';
$possibleChoices = array(
'foo' => 'foo',
'żółw' => 'bar',
'łabądź' => 'baz',
);
$outputShown = array(
$question,
' [<info>foo </info>] foo',
' [<info>żółw </info>] bar',
' [<info>łabądź</info>] baz',
);
$output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
$output->method('getFormatter')->willReturn(new OutputFormatter());
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream("\n"));
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown));
$question = new ChoiceQuestion($question, $possibleChoices, 'foo');
$dialog->ask($this->createInputInterfaceMock(), $output, $question);
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
*/
public function testAskThrowsExceptionOnMissingInput()
{
$dialog = new QuestionHelper();
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?'));
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
*/
public function testAskThrowsExceptionOnMissingInputWithValidator()
{
$dialog = new QuestionHelper();
$question = new Question('What\'s your name?');
$question->setValidator(function () {
if (!$value) {
throw new \Exception('A value is required.');
}
});
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Choice question must have at least 1 choice available.
*/
public function testEmptyChoices()
{
new ChoiceQuestion('Question', array(), 'irrelevant');
}
public function testTraversableAutocomplete()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
// Acm<NEWLINE>
// Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
// <NEWLINE>
// <UP ARROW><UP ARROW><NEWLINE>
// <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
// <DOWN ARROW><NEWLINE>
// S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
// F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
$inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n");
$dialog = new QuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new Question('Please select a bundle', 'FrameworkBundle');
$question->setAutocompleterValues(new AutocompleteValues(array('irrelevant' => 'AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fwrite($stream, $input);
rewind($stream);
return $stream;
}
protected function createOutputInterface()
{
return new StreamOutput(fopen('php://memory', 'r+', false));
}
protected function createInputInterfaceMock($interactive = true)
{
$mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
$mock->expects($this->any())
->method('isInteractive')
->will($this->returnValue($interactive));
return $mock;
}
private function hasSttyAvailable()
{
exec('stty 2>&1', $output, $exitcode);
return 0 === $exitcode;
}
}
class AutocompleteValues implements \IteratorAggregate
{
private $values;
public function __construct(array $values)
{
$this->values = $values;
}
public function getIterator()
{
return new \ArrayIterator($this->values);
}
}

View file

@ -0,0 +1,156 @@
<?php
namespace Symfony\Component\Console\Tests\Helper;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ChoiceQuestion;
/**
* @group tty
*/
class SymfonyQuestionHelperTest extends AbstractQuestionHelperTest
{
public function testAskChoice()
{
$questionHelper = new SymfonyQuestionHelper();
$helperSet = new HelperSet(array(new FormatterHelper()));
$questionHelper->setHelperSet($helperSet);
$heroes = array('Superman', 'Batman', 'Spiderman');
$inputStream = $this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n");
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
$question->setMaxAttempts(1);
// first answer is an empty answer, we're supposed to receive the default value
$this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Spiderman]', $output);
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setMaxAttempts(1);
$this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setErrorMessage('Input "%s" is not a superhero!');
$question->setMaxAttempts(2);
$this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('Input "Fabien" is not a superhero!', $output);
try {
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
$question->setMaxAttempts(1);
$questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
}
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
$question->setMaxAttempts(1);
$question->setMultiselect(true);
$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
}
public function testAskReturnsNullIfValidatorAllowsIt()
{
$questionHelper = new SymfonyQuestionHelper();
$question = new Question('What is your favorite superhero?');
$question->setValidator(function ($value) { return $value; });
$input = $this->createStreamableInputInterfaceMock($this->getInputStream("\n"));
$this->assertNull($questionHelper->ask($input, $this->createOutputInterface(), $question));
}
public function testAskEscapeDefaultValue()
{
$helper = new SymfonyQuestionHelper();
$input = $this->createStreamableInputInterfaceMock($this->getInputStream('\\'));
$helper->ask($input, $output = $this->createOutputInterface(), new Question('Can I have a backslash?', '\\'));
$this->assertOutputContains('Can I have a backslash? [\]', $output);
}
public function testAskEscapeAndFormatLabel()
{
$helper = new SymfonyQuestionHelper();
$input = $this->createStreamableInputInterfaceMock($this->getInputStream('Foo\\Bar'));
$helper->ask($input, $output = $this->createOutputInterface(), new Question('Do you want to use Foo\\Bar <comment>or</comment> Foo\\Baz\\?', 'Foo\\Baz'));
$this->assertOutputContains('Do you want to use Foo\\Bar or Foo\\Baz\\? [Foo\\Baz]:', $output);
}
public function testLabelTrailingBackslash()
{
$helper = new SymfonyQuestionHelper();
$input = $this->createStreamableInputInterfaceMock($this->getInputStream('sure'));
$helper->ask($input, $output = $this->createOutputInterface(), new Question('Question with a trailing \\'));
$this->assertOutputContains('Question with a trailing \\', $output);
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
*/
public function testAskThrowsExceptionOnMissingInput()
{
$dialog = new SymfonyQuestionHelper();
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?'));
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fwrite($stream, $input);
rewind($stream);
return $stream;
}
protected function createOutputInterface()
{
$output = new StreamOutput(fopen('php://memory', 'r+', false));
$output->setDecorated(false);
return $output;
}
protected function createInputInterfaceMock($interactive = true)
{
$mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
$mock->expects($this->any())
->method('isInteractive')
->will($this->returnValue($interactive));
return $mock;
}
private function assertOutputContains($expected, StreamOutput $output)
{
rewind($output->getStream());
$stream = stream_get_contents($output->getStream());
$this->assertContains($expected, $stream);
}
}

View file

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\TableStyle;
class TableStyleTest extends TestCase
{
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).
*/
public function testSetPadTypeWithInvalidType()
{
$style = new TableStyle();
$style->setPadType('TEST');
}
}

View file

@ -0,0 +1,822 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Output\StreamOutput;
class TableTest extends TestCase
{
protected $stream;
protected function setUp()
{
$this->stream = fopen('php://memory', 'r+');
}
protected function tearDown()
{
fclose($this->stream);
$this->stream = null;
}
/**
* @dataProvider renderProvider
*/
public function testRender($headers, $rows, $style, $expected, $decorated = false)
{
$table = new Table($output = $this->getOutputStream($decorated));
$table
->setHeaders($headers)
->setRows($rows)
->setStyle($style)
;
$table->render();
$this->assertEquals($expected, $this->getOutputContent($output));
}
/**
* @dataProvider renderProvider
*/
public function testRenderAddRows($headers, $rows, $style, $expected, $decorated = false)
{
$table = new Table($output = $this->getOutputStream($decorated));
$table
->setHeaders($headers)
->addRows($rows)
->setStyle($style)
;
$table->render();
$this->assertEquals($expected, $this->getOutputContent($output));
}
/**
* @dataProvider renderProvider
*/
public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $decorated = false)
{
$table = new Table($output = $this->getOutputStream($decorated));
$table
->setHeaders($headers)
->setStyle($style)
;
foreach ($rows as $row) {
$table->addRow($row);
}
$table->render();
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function renderProvider()
{
$books = array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
);
return array(
array(
array('ISBN', 'Title', 'Author'),
$books,
'default',
<<<'TABLE'
+---------------+--------------------------+------------------+
| ISBN | Title | Author |
+---------------+--------------------------+------------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
| 80-902734-1-6 | And Then There Were None | Agatha Christie |
+---------------+--------------------------+------------------+
TABLE
),
array(
array('ISBN', 'Title', 'Author'),
$books,
'compact',
<<<'TABLE'
ISBN Title Author
99921-58-10-7 Divine Comedy Dante Alighieri
9971-5-0210-0 A Tale of Two Cities Charles Dickens
960-425-059-0 The Lord of the Rings J. R. R. Tolkien
80-902734-1-6 And Then There Were None Agatha Christie
TABLE
),
array(
array('ISBN', 'Title', 'Author'),
$books,
'borderless',
<<<'TABLE'
=============== ========================== ==================
ISBN Title Author
=============== ========================== ==================
99921-58-10-7 Divine Comedy Dante Alighieri
9971-5-0210-0 A Tale of Two Cities Charles Dickens
960-425-059-0 The Lord of the Rings J. R. R. Tolkien
80-902734-1-6 And Then There Were None Agatha Christie
=============== ========================== ==================
TABLE
),
array(
array('ISBN', 'Title'),
array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
array('9971-5-0210-0'),
array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
),
'default',
<<<'TABLE'
+---------------+--------------------------+------------------+
| ISBN | Title | |
+---------------+--------------------------+------------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
| 9971-5-0210-0 | | |
| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
| 80-902734-1-6 | And Then There Were None | Agatha Christie |
+---------------+--------------------------+------------------+
TABLE
),
array(
array(),
array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
array('9971-5-0210-0'),
array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
),
'default',
<<<'TABLE'
+---------------+--------------------------+------------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
| 9971-5-0210-0 | | |
| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
| 80-902734-1-6 | And Then There Were None | Agatha Christie |
+---------------+--------------------------+------------------+
TABLE
),
array(
array('ISBN', 'Title', 'Author'),
array(
array('99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'),
array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."),
array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."),
array('960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"),
),
'default',
<<<'TABLE'
+---------------+----------------------------+-----------------+
| ISBN | Title | Author |
+---------------+----------------------------+-----------------+
| 99921-58-10-7 | Divine | Dante Alighieri |
| | Comedy | |
| 9971-5-0210-2 | Harry Potter | Rowling |
| | and the Chamber of Secrets | Joanne K. |
| 9971-5-0210-2 | Harry Potter | Rowling |
| | and the Chamber of Secrets | Joanne K. |
| 960-425-059-0 | The Lord of the Rings | J. R. R. |
| | | Tolkien |
+---------------+----------------------------+-----------------+
TABLE
),
array(
array('ISBN', 'Title'),
array(),
'default',
<<<'TABLE'
+------+-------+
| ISBN | Title |
+------+-------+
TABLE
),
array(
array(),
array(),
'default',
'',
),
'Cell text with tags used for Output styling' => array(
array('ISBN', 'Title', 'Author'),
array(
array('<info>99921-58-10-7</info>', '<error>Divine Comedy</error>', '<fg=blue;bg=white>Dante Alighieri</fg=blue;bg=white>'),
array('9971-5-0210-0', 'A Tale of Two Cities', '<info>Charles Dickens</>'),
),
'default',
<<<'TABLE'
+---------------+----------------------+-----------------+
| ISBN | Title | Author |
+---------------+----------------------+-----------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
+---------------+----------------------+-----------------+
TABLE
),
'Cell text with tags not used for Output styling' => array(
array('ISBN', 'Title', 'Author'),
array(
array('<strong>99921-58-10-700</strong>', '<f>Divine Com</f>', 'Dante Alighieri'),
array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
),
'default',
<<<'TABLE'
+----------------------------------+----------------------+-----------------+
| ISBN | Title | Author |
+----------------------------------+----------------------+-----------------+
| <strong>99921-58-10-700</strong> | <f>Divine Com</f> | Dante Alighieri |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
+----------------------------------+----------------------+-----------------+
TABLE
),
'Cell with colspan' => array(
array('ISBN', 'Title', 'Author'),
array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
new TableSeparator(),
array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))),
new TableSeparator(),
array(
new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)),
'Mark Schmidt',
),
new TableSeparator(),
array(
'9971-5-0210-0',
new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)),
),
new TableSeparator(),
array(
new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)),
),
),
'default',
<<<'TABLE'
+-------------------------------+-------------------------------+-----------------------------+
| ISBN | Title | Author |
+-------------------------------+-------------------------------+-----------------------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
+-------------------------------+-------------------------------+-----------------------------+
| Divine Comedy(Dante Alighieri) |
+-------------------------------+-------------------------------+-----------------------------+
| Arduino: A Quick-Start Guide | Mark Schmidt |
+-------------------------------+-------------------------------+-----------------------------+
| 9971-5-0210-0 | A Tale of |
| | Two Cities |
+-------------------------------+-------------------------------+-----------------------------+
| Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil! |
+-------------------------------+-------------------------------+-----------------------------+
TABLE
),
'Cell with rowspan' => array(
array('ISBN', 'Title', 'Author'),
array(
array(
new TableCell('9971-5-0210-0', array('rowspan' => 3)),
new TableCell('Divine Comedy', array('rowspan' => 2)),
'Dante Alighieri',
),
array(),
array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"),
new TableSeparator(),
array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'),
array('80-902734-1-7', 'Test'),
),
'default',
<<<'TABLE'
+---------------+---------------+-----------------+
| ISBN | Title | Author |
+---------------+---------------+-----------------+
| 9971-5-0210-0 | Divine Comedy | Dante Alighieri |
| | | |
| | The Lord of | J. R. |
| | the Rings | R. Tolkien |
+---------------+---------------+-----------------+
| 80-902734-1-6 | And Then | Agatha Christie |
| 80-902734-1-7 | There | Test |
| | Were None | |
+---------------+---------------+-----------------+
TABLE
),
'Cell with rowspan and colspan' => array(
array('ISBN', 'Title', 'Author'),
array(
array(
new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)),
'Dante Alighieri',
),
array('Charles Dickens'),
new TableSeparator(),
array(
'Dante Alighieri',
new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)),
),
array('J. R. R. Tolkien'),
array('J. R. R'),
),
'default',
<<<'TABLE'
+------------------+---------+-----------------+
| ISBN | Title | Author |
+------------------+---------+-----------------+
| 9971-5-0210-0 | Dante Alighieri |
| | Charles Dickens |
+------------------+---------+-----------------+
| Dante Alighieri | 9971-5-0210-0 |
| J. R. R. Tolkien | |
| J. R. R | |
+------------------+---------+-----------------+
TABLE
),
'Cell with rowspan and colspan contains new line break' => array(
array('ISBN', 'Title', 'Author'),
array(
array(
new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
'Dante Alighieri',
),
array('Charles Dickens'),
new TableSeparator(),
array(
'Dante Alighieri',
new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
),
array('Charles Dickens'),
new TableSeparator(),
array(
new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)),
),
),
'default',
<<<'TABLE'
+-----------------+-------+-----------------+
| ISBN | Title | Author |
+-----------------+-------+-----------------+
| 9971 | Dante Alighieri |
| -5- | Charles Dickens |
| 021 | |
| 0-0 | |
+-----------------+-------+-----------------+
| Dante Alighieri | 9971 |
| Charles Dickens | -5- |
| | 021 |
| | 0-0 |
+-----------------+-------+-----------------+
| 9971 | Dante |
| -5- | Alighieri |
| 021 | |
| 0-0 | |
+-----------------+-------+-----------------+
TABLE
),
'Cell with rowspan and colspan without using TableSeparator' => array(
array('ISBN', 'Title', 'Author'),
array(
array(
new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
'Dante Alighieri',
),
array('Charles Dickens'),
array(
'Dante Alighieri',
new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
),
array('Charles Dickens'),
),
'default',
<<<'TABLE'
+-----------------+-------+-----------------+
| ISBN | Title | Author |
+-----------------+-------+-----------------+
| 9971 | Dante Alighieri |
| -5- | Charles Dickens |
| 021 | |
| 0-0 | |
| Dante Alighieri | 9971 |
| Charles Dickens | -5- |
| | 021 |
| | 0-0 |
+-----------------+-------+-----------------+
TABLE
),
'Cell with rowspan and colspan with separator inside a rowspan' => array(
array('ISBN', 'Author'),
array(
array(
new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)),
'Dante Alighieri',
),
array(new TableSeparator()),
array('Charles Dickens'),
),
'default',
<<<'TABLE'
+---------------+-----------------+
| ISBN | Author |
+---------------+-----------------+
| 9971-5-0210-0 | Dante Alighieri |
| |-----------------|
| | Charles Dickens |
+---------------+-----------------+
TABLE
),
'Multiple header lines' => array(
array(
array(new TableCell('Main title', array('colspan' => 3))),
array('ISBN', 'Title', 'Author'),
),
array(),
'default',
<<<'TABLE'
+------+-------+--------+
| Main title |
+------+-------+--------+
| ISBN | Title | Author |
+------+-------+--------+
TABLE
),
'Row with multiple cells' => array(
array(),
array(
array(
new TableCell('1', array('colspan' => 3)),
new TableCell('2', array('colspan' => 2)),
new TableCell('3', array('colspan' => 2)),
new TableCell('4', array('colspan' => 2)),
),
),
'default',
<<<'TABLE'
+---+--+--+---+--+---+--+---+--+
| 1 | 2 | 3 | 4 |
+---+--+--+---+--+---+--+---+--+
TABLE
),
'Coslpan and table cells with comment style' => array(
array(
new TableCell('<comment>Long Title</comment>', array('colspan' => 3)),
),
array(
array(
new TableCell('9971-5-0210-0', array('colspan' => 3)),
),
new TableSeparator(),
array(
'Dante Alighieri',
'J. R. R. Tolkien',
'J. R. R',
),
),
'default',
<<<TABLE
+-----------------+------------------+---------+
|\033[32m \033[39m\033[33mLong Title\033[39m\033[32m \033[39m|
+-----------------+------------------+---------+
| 9971-5-0210-0 |
+-----------------+------------------+---------+
| Dante Alighieri | J. R. R. Tolkien | J. R. R |
+-----------------+------------------+---------+
TABLE
,
true,
),
'Row with formatted cells containing a newline' => array(
array(),
array(
array(
new TableCell('<error>Dont break'."\n".'here</error>', array('colspan' => 2)),
),
new TableSeparator(),
array(
'foo',
new TableCell('<error>Dont break'."\n".'here</error>', array('rowspan' => 2)),
),
array(
'bar',
),
),
'default',
<<<'TABLE'
+-------+------------+
| Dont break |
| here |
+-------+------------+
| foo | Dont break |
| bar | here |
+-------+------------+
TABLE
,
true,
),
);
}
public function testRenderMultiByte()
{
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('■■'))
->setRows(array(array(1234)))
->setStyle('default')
;
$table->render();
$expected =
<<<'TABLE'
+------+
| ■■ |
+------+
| 1234 |
+------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testTableCellWithNumericIntValue()
{
$table = new Table($output = $this->getOutputStream());
$table->setRows(array(array(new TableCell(12345))));
$table->render();
$expected =
<<<'TABLE'
+-------+
| 12345 |
+-------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testTableCellWithNumericFloatValue()
{
$table = new Table($output = $this->getOutputStream());
$table->setRows(array(array(new TableCell(12345.01))));
$table->render();
$expected =
<<<'TABLE'
+----------+
| 12345.01 |
+----------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testStyle()
{
$style = new TableStyle();
$style
->setHorizontalBorderChar('.')
->setVerticalBorderChar('.')
->setCrossingChar('.')
;
Table::setStyleDefinition('dotfull', $style);
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('Foo'))
->setRows(array(array('Bar')))
->setStyle('dotfull');
$table->render();
$expected =
<<<'TABLE'
.......
. Foo .
.......
. Bar .
.......
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testRowSeparator()
{
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('Foo'))
->setRows(array(
array('Bar1'),
new TableSeparator(),
array('Bar2'),
new TableSeparator(),
array('Bar3'),
));
$table->render();
$expected =
<<<'TABLE'
+------+
| Foo |
+------+
| Bar1 |
+------+
| Bar2 |
+------+
| Bar3 |
+------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
$this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works');
}
public function testRenderMultiCalls()
{
$table = new Table($output = $this->getOutputStream());
$table->setRows(array(
array(new TableCell('foo', array('colspan' => 2))),
));
$table->render();
$table->render();
$table->render();
$expected =
<<<TABLE
+----+---+
| foo |
+----+---+
+----+---+
| foo |
+----+---+
+----+---+
| foo |
+----+---+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testColumnStyle()
{
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('ISBN', 'Title', 'Author', 'Price'))
->setRows(array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'),
array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'),
));
$style = new TableStyle();
$style->setPadType(STR_PAD_LEFT);
$table->setColumnStyle(3, $style);
$table->render();
$expected =
<<<TABLE
+---------------+----------------------+-----------------+--------+
| ISBN | Title | Author | Price |
+---------------+----------------------+-----------------+--------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
+---------------+----------------------+-----------------+--------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testColumnWith()
{
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('ISBN', 'Title', 'Author', 'Price'))
->setRows(array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'),
array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'),
))
->setColumnWidth(0, 15)
->setColumnWidth(3, 10);
$style = new TableStyle();
$style->setPadType(STR_PAD_LEFT);
$table->setColumnStyle(3, $style);
$table->render();
$expected =
<<<TABLE
+-----------------+----------------------+-----------------+------------+
| ISBN | Title | Author | Price |
+-----------------+----------------------+-----------------+------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
+-----------------+----------------------+-----------------+------------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testColumnWiths()
{
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('ISBN', 'Title', 'Author', 'Price'))
->setRows(array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'),
array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'),
))
->setColumnWidths(array(15, 0, -1, 10));
$style = new TableStyle();
$style->setPadType(STR_PAD_LEFT);
$table->setColumnStyle(3, $style);
$table->render();
$expected =
<<<TABLE
+-----------------+----------------------+-----------------+------------+
| ISBN | Title | Author | Price |
+-----------------+----------------------+-----------------+------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
+-----------------+----------------------+-----------------+------------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
/**
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
* @expectedExceptionMessage Style "absent" is not defined.
*/
public function testIsNotDefinedStyleException()
{
$table = new Table($this->getOutputStream());
$table->setStyle('absent');
}
/**
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
* @expectedExceptionMessage Style "absent" is not defined.
*/
public function testGetStyleDefinition()
{
Table::getStyleDefinition('absent');
}
protected function getOutputStream($decorated = false)
{
return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, $decorated);
}
protected function getOutputContent(StreamOutput $output)
{
rewind($output->getStream());
return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream()));
}
}