From 54d3d63cf15bacbfa331a26883781f15009fe238 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 8 Jun 2016 20:13:57 +0200 Subject: [PATCH] Centralize input stream in base Input class Add StreamableInputInterface Removed commented code Prevent BC by looking for QuestionHelper:: Logic fixes Check for that implements StreamableInputInterface Rollback E_USER_DEPRECATED notice in getInputStream Remove legacy tests to avoid KO because of deprecations Add missing use Keep old tests marked as legacy Undeprecate getInputStream, CS Fixes Move legacy tests in separated class Revert separated legacy test class Keep legacy createInputInterfaceMock() Depreciate QuestionHelper::getInputStream() --- src/Symfony/Component/Console/Application.php | 16 +- .../Console/Helper/QuestionHelper.php | 19 +- src/Symfony/Component/Console/Input/Input.php | 19 +- .../Input/StreamableInputInterface.php | 37 ++ .../Console/Tests/ApplicationTest.php | 2 +- .../Tests/Helper/QuestionHelperTest.php | 489 +++++++++++++++--- .../Console/Tests/Input/InputTest.php | 8 + 7 files changed, 520 insertions(+), 70 deletions(-) create mode 100644 src/Symfony/Component/Console/Input/StreamableInputInterface.php diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index ae737ad45d..0b7fe9b19c 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -16,6 +16,7 @@ use Symfony\Component\Console\Helper\DebugFormatterHelper; use Symfony\Component\Console\Helper\ProcessHelper; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StreamableInputInterface; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputDefinition; @@ -769,8 +770,19 @@ class Application if (true === $input->hasParameterOption(array('--no-interaction', '-n'), true)) { $input->setInteractive(false); - } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { - $inputStream = $this->getHelperSet()->get('question')->getInputStream(); + } elseif (function_exists('posix_isatty')) { + $inputStream = null; + + if ($input instanceof StreamableInputInterface) { + $inputStream = $input->getStream(); + } + + // This check ensures that calling QuestionHelper::setInputStream() works + // To be removed in 4.0 (in the same time as QuestionHelper::setInputStream) + if (!$inputStream && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(false); + } + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { $input->setInteractive(false); } diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index c6f5a409c3..c6d7af60aa 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Console\Helper; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StreamableInputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Formatter\OutputFormatterStyle; @@ -52,6 +53,10 @@ class QuestionHelper extends Helper return $question->getDefault(); } + if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { + $this->inputStream = $stream; + } + if (!$question->getValidator()) { return $this->doAsk($output, $question); } @@ -68,12 +73,17 @@ class QuestionHelper extends Helper * * This is mainly useful for testing purpose. * + * @deprecated since version 3.2, to be removed in 4.0. Use + * StreamableInputInterface::setStream() instead. + * * @param resource $stream The input stream * * @throws InvalidArgumentException In case the stream is not a resource */ public function setInputStream($stream) { + @trigger_error(sprintf('The %s() method is deprecated since version 3.2 and will be removed in 4.0. Use %s:setStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); + if (!is_resource($stream)) { throw new InvalidArgumentException('Input stream must be a valid resource.'); } @@ -84,10 +94,17 @@ class QuestionHelper extends Helper /** * Returns the helper's input stream. * + * @deprecated since version 3.2, to be removed in 4.0. Use + * StreamableInputInterface::getStream() instead. + * * @return resource */ - public function getInputStream() + public function getInputStream($triggerDeprecationError = true) { + if ($triggerDeprecationError) { + @trigger_error(sprintf('The %s() method is deprecated since version 3.2 and will be removed in 4.0. Use %s:getStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); + } + return $this->inputStream; } diff --git a/src/Symfony/Component/Console/Input/Input.php b/src/Symfony/Component/Console/Input/Input.php index 85499fc489..2a8d0628aa 100644 --- a/src/Symfony/Component/Console/Input/Input.php +++ b/src/Symfony/Component/Console/Input/Input.php @@ -25,12 +25,13 @@ use Symfony\Component\Console\Exception\RuntimeException; * * @author Fabien Potencier */ -abstract class Input implements InputInterface +abstract class Input implements InputInterface, StreamableInputInterface { /** * @var InputDefinition */ protected $definition; + protected $stream; protected $options = array(); protected $arguments = array(); protected $interactive = true; @@ -233,4 +234,20 @@ abstract class Input implements InputInterface { return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); } + + /** + * {@inheritdoc} + */ + public function setStream($stream) + { + $this->stream = $stream; + } + + /** + * {@inheritdoc} + */ + public function getStream() + { + return $this->stream; + } } diff --git a/src/Symfony/Component/Console/Input/StreamableInputInterface.php b/src/Symfony/Component/Console/Input/StreamableInputInterface.php new file mode 100644 index 0000000000..d7e462f244 --- /dev/null +++ b/src/Symfony/Component/Console/Input/StreamableInputInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * StreamableInputInterface is the interface implemented by all input classes + * that have an input stream. + * + * @author Robin Chalas + */ +interface StreamableInputInterface extends InputInterface +{ + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setStream($stream); + + /** + * Returns the input stream. + * + * @return resource|null + */ + public function getStream(); +} diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 954d02eaad..493299ba11 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -1099,7 +1099,7 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); - $inputStream = $application->getHelperSet()->get('question')->getInputStream(); + $inputStream = $tester->getInput()->getStream(); $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); } } diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 6a4f8aceae..84abeaa300 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Input\StreamableInputInterface; /** * @group tty @@ -34,6 +35,377 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $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)); + } + + 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 + // AcsTest + // + // + // Test + // + // S + // F00oo + $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'); + } + + // + $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 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 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, + ' [foo ] foo', + ' [żółw ] bar', + ' [łabądź] baz', + ); + $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); + $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'); @@ -85,7 +457,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); } - public function testAsk() + /** + * @group legacy + */ + public function testLegacyAsk() { $dialog = new QuestionHelper(); @@ -101,7 +476,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); } - public function testAskWithAutocomplete() + /** + * @group legacy + */ + public function testLegacyAskWithAutocomplete() { if (!$this->hasSttyAvailable()) { $this->markTestSkipped('`stty` is required to test autocomplete functionality'); @@ -135,7 +513,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); } - public function testAskWithAutocompleteWithNonSequentialKeys() + /** + * @group legacy + */ + public function testLegacyAskWithAutocompleteWithNonSequentialKeys() { if (!$this->hasSttyAvailable()) { $this->markTestSkipped('`stty` is required to test autocomplete functionality'); @@ -155,7 +536,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); } - public function testAskHiddenResponse() + /** + * @group legacy + */ + public function testLegacyAskHiddenResponse() { if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('This test is not supported on Windows'); @@ -171,9 +555,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase } /** + * @group legacy * @dataProvider getAskConfirmationData */ - public function testAskConfirmation($question, $expected, $default = true) + public function testLegacyAskConfirmation($question, $expected, $default = true) { $dialog = new QuestionHelper(); @@ -182,19 +567,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $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() + /** + * @group legacy + */ + public function testLegacyAskConfirmationWithCustomTrueAnswer() { $dialog = new QuestionHelper(); @@ -205,7 +581,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); } - public function testAskAndValidate() + /** + * @group legacy + */ + public function testLegacyAskAndValidate() { $dialog = new QuestionHelper(); $helperSet = new HelperSet(array(new FormatterHelper())); @@ -238,9 +617,10 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase } /** + * @group legacy * @dataProvider simpleAnswerProvider */ - public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) + public function testLegacySelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) { $possibleChoices = array( 'My environment 1', @@ -260,22 +640,11 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $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'), - ); - } - /** + * @group legacy * @dataProvider mixedKeysChoiceListAnswerProvider */ - public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) + public function testLegacyChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) { $possibleChoices = array( '0' => 'No environment', @@ -296,22 +665,11 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $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'), - ); - } - /** + * @group legacy * @dataProvider answerProvider */ - public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) + public function testLegacySelectChoiceFromChoiceList($providedAnswer, $expectedValue) { $possibleChoices = array( 'env_1' => 'My environment 1', @@ -332,10 +690,11 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase } /** + * @group legacy * @expectedException \InvalidArgumentException * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. */ - public function testAmbiguousChoiceFromChoicelist() + public function testLegacyAmbiguousChoiceFromChoicelist() { $possibleChoices = array( 'env_1' => 'My first environment', @@ -354,27 +713,11 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase $dialog->ask($this->createInputInterfaceMock(), $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->createInputInterfaceMock(false), $this->createOutputInterface(), $question)); - } - /** * @requires function mb_strwidth + * @group legacy */ - public function testChoiceOutputFormattingQuestionForUtf8Keys() + public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys() { $question = 'Lorem ipsum?'; $possibleChoices = array( @@ -426,6 +769,22 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase return $mock; } + protected function createStreamableInputInterfaceMock($stream = null, $interactive = true) + { + $mock = $this->getMock(StreamableInputInterface::class); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + if ($stream) { + $mock->expects($this->any()) + ->method('getStream') + ->willReturn($stream); + } + + return $mock; + } + private function hasSttyAvailable() { exec('stty 2>&1', $output, $exitcode); diff --git a/src/Symfony/Component/Console/Tests/Input/InputTest.php b/src/Symfony/Component/Console/Tests/Input/InputTest.php index eb1c6617f5..ef7e5c4309 100644 --- a/src/Symfony/Component/Console/Tests/Input/InputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/InputTest.php @@ -129,4 +129,12 @@ class InputTest extends \PHPUnit_Framework_TestCase $input->setInteractive(false); $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); } + + public function testSetGetStream() + { + $input = new ArrayInput(array()); + $stream = fopen('php://memory', 'r+', false); + $input->setStream($stream); + $this->assertSame($stream, $input->getStream()); + } }