bug #30271 [Console] Fix command testing with missing user inputs (chalasr)

This PR was merged into the 3.4 branch.

Discussion
----------

[Console] Fix command testing with missing user inputs

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #30138
| License       | MIT
| Doc PR        | n/a

Fixes the remaining cases where test execution hangs using `CommandTester`.

Commits
-------

ac4e9b0b26 [Console] Fix command testing with missing inputs
This commit is contained in:
Robin Chalas 2019-02-19 14:37:40 +01:00
commit 3d4c5ddba7
5 changed files with 51 additions and 12 deletions

View File

@ -166,7 +166,7 @@ class QuestionHelper extends Helper
if (false === $ret) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new RuntimeException('Aborted');
throw new RuntimeException('Aborted.');
}
$ret = trim($ret);
}
@ -252,8 +252,10 @@ class QuestionHelper extends Helper
while (!feof($inputStream)) {
$c = fread($inputStream, 1);
// Backspace Character
if ("\177" === $c) {
// as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false.
if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) {
throw new RuntimeException('Aborted.');
} elseif ("\177" === $c) { // Backspace Character
if (0 === $numMatches && 0 !== $i) {
--$i;
// Move cursor backwards
@ -380,7 +382,7 @@ class QuestionHelper extends Helper
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new RuntimeException('Aborted');
throw new RuntimeException('Aborted.');
}
$value = trim($value);

View File

@ -62,9 +62,8 @@ class CommandTester
}
$this->input = new ArrayInput($input);
if ($this->inputs) {
$this->input->setStream(self::createStream($this->inputs));
}
// Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN.
$this->input->setStream(self::createStream($this->inputs));
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);

View File

@ -900,7 +900,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
* @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInput()
{
@ -910,7 +910,17 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
* @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInputForChoiceQuestion()
{
$dialog = new QuestionHelper();
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new ChoiceQuestion('Choice', ['a', 'b']));
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInputWithValidator()
{

View File

@ -124,7 +124,7 @@ class SymfonyQuestionHelperTest extends AbstractQuestionHelperTest
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
* @expectedExceptionMessage Aborted.
*/
public function testAskThrowsExceptionOnMissingInput()
{

View File

@ -17,6 +17,7 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
@ -139,7 +140,7 @@ class CommandTesterTest extends TestCase
/**
* @expectedException \RuntimeException
* @expectedMessage Aborted
* @expectedExceptionMessage Aborted.
*/
public function testCommandWithWrongInputsNumber()
{
@ -153,13 +154,40 @@ class CommandTesterTest extends TestCase
$command->setHelperSet(new HelperSet([new QuestionHelper()]));
$command->setCode(function ($input, $output) use ($questions, $command) {
$helper = $command->getHelper('question');
$helper->ask($input, $output, new ChoiceQuestion('choice', ['a', 'b']));
$helper->ask($input, $output, new Question($questions[0]));
$helper->ask($input, $output, new Question($questions[1]));
$helper->ask($input, $output, new Question($questions[2]));
});
$tester = new CommandTester($command);
$tester->setInputs(['a', 'Bobby', 'Fine']);
$tester->execute([]);
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Aborted.
*/
public function testCommandWithQuestionsButNoInputs()
{
$questions = [
'What\'s your name?',
'How are you?',
'Where do you come from?',
];
$command = new Command('foo');
$command->setHelperSet(new HelperSet([new QuestionHelper()]));
$command->setCode(function ($input, $output) use ($questions, $command) {
$helper = $command->getHelper('question');
$helper->ask($input, $output, new ChoiceQuestion('choice', ['a', 'b']));
$helper->ask($input, $output, new Question($questions[0]));
$helper->ask($input, $output, new Question($questions[1]));
$helper->ask($input, $output, new Question($questions[2]));
});
$tester = new CommandTester($command);
$tester->setInputs(['Bobby', 'Fine']);
$tester->execute([]);
}