cf12acdb665b45972c23b436cd38ffbd1ac56c15
[yaffs-website] / vendor / symfony / console / Tests / Helper / QuestionHelperTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Console\Tests\Helper;
13
14 use Symfony\Component\Console\Formatter\OutputFormatter;
15 use Symfony\Component\Console\Helper\QuestionHelper;
16 use Symfony\Component\Console\Helper\HelperSet;
17 use Symfony\Component\Console\Helper\FormatterHelper;
18 use Symfony\Component\Console\Output\StreamOutput;
19 use Symfony\Component\Console\Question\ChoiceQuestion;
20 use Symfony\Component\Console\Question\ConfirmationQuestion;
21 use Symfony\Component\Console\Question\Question;
22
23 /**
24  * @group tty
25  */
26 class QuestionHelperTest extends AbstractQuestionHelperTest
27 {
28     public function testAskChoice()
29     {
30         $questionHelper = new QuestionHelper();
31
32         $helperSet = new HelperSet(array(new FormatterHelper()));
33         $questionHelper->setHelperSet($helperSet);
34
35         $heroes = array('Superman', 'Batman', 'Spiderman');
36
37         $inputStream = $this->getInputStream("\n1\n  1  \nFabien\n1\nFabien\n1\n0,2\n 0 , 2  \n\n\n");
38
39         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
40         $question->setMaxAttempts(1);
41         // first answer is an empty answer, we're supposed to receive the default value
42         $this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
43
44         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
45         $question->setMaxAttempts(1);
46         $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
47         $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
48
49         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
50         $question->setErrorMessage('Input "%s" is not a superhero!');
51         $question->setMaxAttempts(2);
52         $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
53
54         rewind($output->getStream());
55         $stream = stream_get_contents($output->getStream());
56         $this->assertContains('Input "Fabien" is not a superhero!', $stream);
57
58         try {
59             $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
60             $question->setMaxAttempts(1);
61             $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question);
62             $this->fail();
63         } catch (\InvalidArgumentException $e) {
64             $this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
65         }
66
67         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
68         $question->setMaxAttempts(1);
69         $question->setMultiselect(true);
70
71         $this->assertEquals(array('Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
72         $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
73         $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
74
75         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
76         $question->setMaxAttempts(1);
77         $question->setMultiselect(true);
78
79         $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
80
81         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
82         $question->setMaxAttempts(1);
83         $question->setMultiselect(true);
84
85         $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
86
87         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, 0);
88         // We are supposed to get the default value since we are not in interactive mode
89         $this->assertEquals('Superman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, true), $this->createOutputInterface(), $question));
90     }
91
92     public function testAsk()
93     {
94         $dialog = new QuestionHelper();
95
96         $inputStream = $this->getInputStream("\n8AM\n");
97
98         $question = new Question('What time is it?', '2PM');
99         $this->assertEquals('2PM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
100
101         $question = new Question('What time is it?', '2PM');
102         $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
103
104         rewind($output->getStream());
105         $this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
106     }
107
108     public function testAskWithAutocomplete()
109     {
110         if (!$this->hasSttyAvailable()) {
111             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
112         }
113
114         // Acm<NEWLINE>
115         // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
116         // <NEWLINE>
117         // <UP ARROW><UP ARROW><NEWLINE>
118         // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
119         // <DOWN ARROW><NEWLINE>
120         // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
121         // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
122         $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");
123
124         $dialog = new QuestionHelper();
125         $helperSet = new HelperSet(array(new FormatterHelper()));
126         $dialog->setHelperSet($helperSet);
127
128         $question = new Question('Please select a bundle', 'FrameworkBundle');
129         $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'));
130
131         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
132         $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
133         $this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
134         $this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
135         $this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
136         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
137         $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
138         $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
139     }
140
141     public function testAskWithAutocompleteWithNonSequentialKeys()
142     {
143         if (!$this->hasSttyAvailable()) {
144             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
145         }
146
147         // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE>
148         $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n");
149
150         $dialog = new QuestionHelper();
151         $dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
152
153         $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle'));
154         $question->setMaxAttempts(1);
155
156         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
157         $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
158     }
159
160     public function testAskWithAutocompleteWithExactMatch()
161     {
162         if (!$this->hasSttyAvailable()) {
163             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
164         }
165
166         $inputStream = $this->getInputStream("b\n");
167
168         $possibleChoices = array(
169             'a' => 'berlin',
170             'b' => 'copenhagen',
171             'c' => 'amsterdam',
172         );
173
174         $dialog = new QuestionHelper();
175         $dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
176
177         $question = new ChoiceQuestion('Please select a city', $possibleChoices);
178         $question->setMaxAttempts(1);
179
180         $this->assertSame('b', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
181     }
182
183     public function testAutocompleteWithTrailingBackslash()
184     {
185         if (!$this->hasSttyAvailable()) {
186             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
187         }
188
189         $inputStream = $this->getInputStream('E');
190
191         $dialog = new QuestionHelper();
192         $helperSet = new HelperSet(array(new FormatterHelper()));
193         $dialog->setHelperSet($helperSet);
194
195         $question = new Question('');
196         $expectedCompletion = 'ExampleNamespace\\';
197         $question->setAutocompleterValues(array($expectedCompletion));
198
199         $output = $this->createOutputInterface();
200         $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output, $question);
201
202         $outputStream = $output->getStream();
203         rewind($outputStream);
204         $actualOutput = stream_get_contents($outputStream);
205
206         // Shell control (esc) sequences are not so important: we only care that
207         // <hl> tag is interpreted correctly and replaced
208         $irrelevantEscSequences = array(
209             "\0337" => '', // Save cursor position
210             "\0338" => '', // Restore cursor position
211             "\033[K" => '', // Clear line from cursor till the end
212         );
213
214         $importantActualOutput = strtr($actualOutput, $irrelevantEscSequences);
215
216         // Remove colors (e.g. "\033[30m", "\033[31;41m")
217         $importantActualOutput = preg_replace('/\033\[\d+(;\d+)?m/', '', $importantActualOutput);
218
219         $this->assertEquals($expectedCompletion, $importantActualOutput);
220     }
221
222     public function testAskHiddenResponse()
223     {
224         if ('\\' === DIRECTORY_SEPARATOR) {
225             $this->markTestSkipped('This test is not supported on Windows');
226         }
227
228         $dialog = new QuestionHelper();
229
230         $question = new Question('What time is it?');
231         $question->setHidden(true);
232
233         $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("8AM\n")), $this->createOutputInterface(), $question));
234     }
235
236     /**
237      * @dataProvider getAskConfirmationData
238      */
239     public function testAskConfirmation($question, $expected, $default = true)
240     {
241         $dialog = new QuestionHelper();
242
243         $inputStream = $this->getInputStream($question."\n");
244         $question = new ConfirmationQuestion('Do you like French fries?', $default);
245         $this->assertEquals($expected, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel'));
246     }
247
248     public function getAskConfirmationData()
249     {
250         return array(
251             array('', true),
252             array('', false, false),
253             array('y', true),
254             array('yes', true),
255             array('n', false),
256             array('no', false),
257         );
258     }
259
260     public function testAskConfirmationWithCustomTrueAnswer()
261     {
262         $dialog = new QuestionHelper();
263
264         $inputStream = $this->getInputStream("j\ny\n");
265         $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
266         $this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
267         $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
268         $this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
269     }
270
271     public function testAskAndValidate()
272     {
273         $dialog = new QuestionHelper();
274         $helperSet = new HelperSet(array(new FormatterHelper()));
275         $dialog->setHelperSet($helperSet);
276
277         $error = 'This is not a color!';
278         $validator = function ($color) use ($error) {
279             if (!in_array($color, array('white', 'black'))) {
280                 throw new \InvalidArgumentException($error);
281             }
282
283             return $color;
284         };
285
286         $question = new Question('What color was the white horse of Henry IV?', 'white');
287         $question->setValidator($validator);
288         $question->setMaxAttempts(2);
289
290         $inputStream = $this->getInputStream("\nblack\n");
291         $this->assertEquals('white', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
292         $this->assertEquals('black', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
293
294         try {
295             $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("green\nyellow\norange\n")), $this->createOutputInterface(), $question);
296             $this->fail();
297         } catch (\InvalidArgumentException $e) {
298             $this->assertEquals($error, $e->getMessage());
299         }
300     }
301
302     /**
303      * @dataProvider simpleAnswerProvider
304      */
305     public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue)
306     {
307         $possibleChoices = array(
308             'My environment 1',
309             'My environment 2',
310             'My environment 3',
311         );
312
313         $dialog = new QuestionHelper();
314         $helperSet = new HelperSet(array(new FormatterHelper()));
315         $dialog->setHelperSet($helperSet);
316
317         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
318         $question->setMaxAttempts(1);
319         $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question);
320
321         $this->assertSame($expectedValue, $answer);
322     }
323
324     public function simpleAnswerProvider()
325     {
326         return array(
327             array(0, 'My environment 1'),
328             array(1, 'My environment 2'),
329             array(2, 'My environment 3'),
330             array('My environment 1', 'My environment 1'),
331             array('My environment 2', 'My environment 2'),
332             array('My environment 3', 'My environment 3'),
333         );
334     }
335
336     /**
337      * @dataProvider specialCharacterInMultipleChoice
338      */
339     public function testSpecialCharacterChoiceFromMultipleChoiceList($providedAnswer, $expectedValue)
340     {
341         $possibleChoices = array(
342             '.',
343             'src',
344         );
345
346         $dialog = new QuestionHelper();
347         $inputStream = $this->getInputStream($providedAnswer."\n");
348         $helperSet = new HelperSet(array(new FormatterHelper()));
349         $dialog->setHelperSet($helperSet);
350
351         $question = new ChoiceQuestion('Please select the directory', $possibleChoices);
352         $question->setMaxAttempts(1);
353         $question->setMultiselect(true);
354         $answer = $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question);
355
356         $this->assertSame($expectedValue, $answer);
357     }
358
359     public function specialCharacterInMultipleChoice()
360     {
361         return array(
362             array('.', array('.')),
363             array('., src', array('.', 'src')),
364         );
365     }
366
367     /**
368      * @dataProvider mixedKeysChoiceListAnswerProvider
369      */
370     public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue)
371     {
372         $possibleChoices = array(
373             '0' => 'No environment',
374             '1' => 'My environment 1',
375             'env_2' => 'My environment 2',
376             3 => 'My environment 3',
377         );
378
379         $dialog = new QuestionHelper();
380         $helperSet = new HelperSet(array(new FormatterHelper()));
381         $dialog->setHelperSet($helperSet);
382
383         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
384         $question->setMaxAttempts(1);
385         $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question);
386
387         $this->assertSame($expectedValue, $answer);
388     }
389
390     public function mixedKeysChoiceListAnswerProvider()
391     {
392         return array(
393             array('0', '0'),
394             array('No environment', '0'),
395             array('1', '1'),
396             array('env_2', 'env_2'),
397             array(3, '3'),
398             array('My environment 1', '1'),
399         );
400     }
401
402     /**
403      * @dataProvider answerProvider
404      */
405     public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue)
406     {
407         $possibleChoices = array(
408             'env_1' => 'My environment 1',
409             'env_2' => 'My environment',
410             'env_3' => 'My environment',
411         );
412
413         $dialog = new QuestionHelper();
414         $helperSet = new HelperSet(array(new FormatterHelper()));
415         $dialog->setHelperSet($helperSet);
416
417         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
418         $question->setMaxAttempts(1);
419         $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question);
420
421         $this->assertSame($expectedValue, $answer);
422     }
423
424     /**
425      * @expectedException        \InvalidArgumentException
426      * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3.
427      */
428     public function testAmbiguousChoiceFromChoicelist()
429     {
430         $possibleChoices = array(
431             'env_1' => 'My first environment',
432             'env_2' => 'My environment',
433             'env_3' => 'My environment',
434         );
435
436         $dialog = new QuestionHelper();
437         $helperSet = new HelperSet(array(new FormatterHelper()));
438         $dialog->setHelperSet($helperSet);
439
440         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
441         $question->setMaxAttempts(1);
442
443         $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("My environment\n")), $this->createOutputInterface(), $question);
444     }
445
446     public function answerProvider()
447     {
448         return array(
449             array('env_1', 'env_1'),
450             array('env_2', 'env_2'),
451             array('env_3', 'env_3'),
452             array('My environment 1', 'env_1'),
453         );
454     }
455
456     public function testNoInteraction()
457     {
458         $dialog = new QuestionHelper();
459         $question = new Question('Do you have a job?', 'not yet');
460         $this->assertEquals('not yet', $dialog->ask($this->createStreamableInputInterfaceMock(null, false), $this->createOutputInterface(), $question));
461     }
462
463     /**
464      * @requires function mb_strwidth
465      */
466     public function testChoiceOutputFormattingQuestionForUtf8Keys()
467     {
468         $question = 'Lorem ipsum?';
469         $possibleChoices = array(
470             'foo' => 'foo',
471             'żółw' => 'bar',
472             'łabądź' => 'baz',
473         );
474         $outputShown = array(
475             $question,
476             '  [<info>foo   </info>] foo',
477             '  [<info>żółw  </info>] bar',
478             '  [<info>łabądź</info>] baz',
479         );
480         $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
481         $output->method('getFormatter')->willReturn(new OutputFormatter());
482
483         $dialog = new QuestionHelper();
484         $helperSet = new HelperSet(array(new FormatterHelper()));
485         $dialog->setHelperSet($helperSet);
486
487         $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown));
488
489         $question = new ChoiceQuestion($question, $possibleChoices, 'foo');
490         $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("\n")), $output, $question);
491     }
492
493     /**
494      * @group legacy
495      */
496     public function testLegacyAskChoice()
497     {
498         $questionHelper = new QuestionHelper();
499
500         $helperSet = new HelperSet(array(new FormatterHelper()));
501         $questionHelper->setHelperSet($helperSet);
502
503         $heroes = array('Superman', 'Batman', 'Spiderman');
504
505         $questionHelper->setInputStream($this->getInputStream("\n1\n  1  \nFabien\n1\nFabien\n1\n0,2\n 0 , 2  \n\n\n"));
506
507         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
508         $question->setMaxAttempts(1);
509         // first answer is an empty answer, we're supposed to receive the default value
510         $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
511
512         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
513         $question->setMaxAttempts(1);
514         $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
515         $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
516
517         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
518         $question->setErrorMessage('Input "%s" is not a superhero!');
519         $question->setMaxAttempts(2);
520         $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
521
522         rewind($output->getStream());
523         $stream = stream_get_contents($output->getStream());
524         $this->assertContains('Input "Fabien" is not a superhero!', $stream);
525
526         try {
527             $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
528             $question->setMaxAttempts(1);
529             $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question);
530             $this->fail();
531         } catch (\InvalidArgumentException $e) {
532             $this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
533         }
534
535         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
536         $question->setMaxAttempts(1);
537         $question->setMultiselect(true);
538
539         $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
540         $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
541         $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
542
543         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
544         $question->setMaxAttempts(1);
545         $question->setMultiselect(true);
546
547         $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
548
549         $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
550         $question->setMaxAttempts(1);
551         $question->setMultiselect(true);
552
553         $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
554     }
555
556     /**
557      * @group legacy
558      */
559     public function testLegacyAsk()
560     {
561         $dialog = new QuestionHelper();
562
563         $dialog->setInputStream($this->getInputStream("\n8AM\n"));
564
565         $question = new Question('What time is it?', '2PM');
566         $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
567
568         $question = new Question('What time is it?', '2PM');
569         $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
570
571         rewind($output->getStream());
572         $this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
573     }
574
575     /**
576      * @group legacy
577      */
578     public function testLegacyAskWithAutocomplete()
579     {
580         if (!$this->hasSttyAvailable()) {
581             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
582         }
583
584         // Acm<NEWLINE>
585         // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
586         // <NEWLINE>
587         // <UP ARROW><UP ARROW><NEWLINE>
588         // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
589         // <DOWN ARROW><NEWLINE>
590         // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
591         // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
592         $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");
593
594         $dialog = new QuestionHelper();
595         $dialog->setInputStream($inputStream);
596         $helperSet = new HelperSet(array(new FormatterHelper()));
597         $dialog->setHelperSet($helperSet);
598
599         $question = new Question('Please select a bundle', 'FrameworkBundle');
600         $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'));
601
602         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
603         $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
604         $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
605         $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
606         $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
607         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
608         $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
609         $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
610     }
611
612     /**
613      * @group legacy
614      */
615     public function testLegacyAskWithAutocompleteWithNonSequentialKeys()
616     {
617         if (!$this->hasSttyAvailable()) {
618             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
619         }
620
621         // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE>
622         $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n");
623
624         $dialog = new QuestionHelper();
625         $dialog->setInputStream($inputStream);
626         $dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
627
628         $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle'));
629         $question->setMaxAttempts(1);
630
631         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
632         $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
633     }
634
635     /**
636      * @group legacy
637      */
638     public function testLegacyAskHiddenResponse()
639     {
640         if ('\\' === DIRECTORY_SEPARATOR) {
641             $this->markTestSkipped('This test is not supported on Windows');
642         }
643
644         $dialog = new QuestionHelper();
645         $dialog->setInputStream($this->getInputStream("8AM\n"));
646
647         $question = new Question('What time is it?');
648         $question->setHidden(true);
649
650         $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
651     }
652
653     /**
654      * @group        legacy
655      * @dataProvider getAskConfirmationData
656      */
657     public function testLegacyAskConfirmation($question, $expected, $default = true)
658     {
659         $dialog = new QuestionHelper();
660
661         $dialog->setInputStream($this->getInputStream($question."\n"));
662         $question = new ConfirmationQuestion('Do you like French fries?', $default);
663         $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel'));
664     }
665
666     /**
667      * @group legacy
668      */
669     public function testLegacyAskConfirmationWithCustomTrueAnswer()
670     {
671         $dialog = new QuestionHelper();
672
673         $dialog->setInputStream($this->getInputStream("j\ny\n"));
674         $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
675         $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
676         $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
677         $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
678     }
679
680     /**
681      * @group legacy
682      */
683     public function testLegacyAskAndValidate()
684     {
685         $dialog = new QuestionHelper();
686         $helperSet = new HelperSet(array(new FormatterHelper()));
687         $dialog->setHelperSet($helperSet);
688
689         $error = 'This is not a color!';
690         $validator = function ($color) use ($error) {
691             if (!in_array($color, array('white', 'black'))) {
692                 throw new \InvalidArgumentException($error);
693             }
694
695             return $color;
696         };
697
698         $question = new Question('What color was the white horse of Henry IV?', 'white');
699         $question->setValidator($validator);
700         $question->setMaxAttempts(2);
701
702         $dialog->setInputStream($this->getInputStream("\nblack\n"));
703         $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
704         $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
705
706         $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n"));
707         try {
708             $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
709             $this->fail();
710         } catch (\InvalidArgumentException $e) {
711             $this->assertEquals($error, $e->getMessage());
712         }
713     }
714
715     /**
716      * @group        legacy
717      * @dataProvider simpleAnswerProvider
718      */
719     public function testLegacySelectChoiceFromSimpleChoices($providedAnswer, $expectedValue)
720     {
721         $possibleChoices = array(
722             'My environment 1',
723             'My environment 2',
724             'My environment 3',
725         );
726
727         $dialog = new QuestionHelper();
728         $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
729         $helperSet = new HelperSet(array(new FormatterHelper()));
730         $dialog->setHelperSet($helperSet);
731
732         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
733         $question->setMaxAttempts(1);
734         $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
735
736         $this->assertSame($expectedValue, $answer);
737     }
738
739     /**
740      * @group        legacy
741      * @dataProvider mixedKeysChoiceListAnswerProvider
742      */
743     public function testLegacyChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue)
744     {
745         $possibleChoices = array(
746             '0' => 'No environment',
747             '1' => 'My environment 1',
748             'env_2' => 'My environment 2',
749             3 => 'My environment 3',
750         );
751
752         $dialog = new QuestionHelper();
753         $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
754         $helperSet = new HelperSet(array(new FormatterHelper()));
755         $dialog->setHelperSet($helperSet);
756
757         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
758         $question->setMaxAttempts(1);
759         $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
760
761         $this->assertSame($expectedValue, $answer);
762     }
763
764     /**
765      * @group        legacy
766      * @dataProvider answerProvider
767      */
768     public function testLegacySelectChoiceFromChoiceList($providedAnswer, $expectedValue)
769     {
770         $possibleChoices = array(
771             'env_1' => 'My environment 1',
772             'env_2' => 'My environment',
773             'env_3' => 'My environment',
774         );
775
776         $dialog = new QuestionHelper();
777         $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
778         $helperSet = new HelperSet(array(new FormatterHelper()));
779         $dialog->setHelperSet($helperSet);
780
781         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
782         $question->setMaxAttempts(1);
783         $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
784
785         $this->assertSame($expectedValue, $answer);
786     }
787
788     /**
789      * @group                    legacy
790      * @expectedException        \InvalidArgumentException
791      * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3.
792      */
793     public function testLegacyAmbiguousChoiceFromChoicelist()
794     {
795         $possibleChoices = array(
796             'env_1' => 'My first environment',
797             'env_2' => 'My environment',
798             'env_3' => 'My environment',
799         );
800
801         $dialog = new QuestionHelper();
802         $dialog->setInputStream($this->getInputStream("My environment\n"));
803         $helperSet = new HelperSet(array(new FormatterHelper()));
804         $dialog->setHelperSet($helperSet);
805
806         $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
807         $question->setMaxAttempts(1);
808
809         $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
810     }
811
812     /**
813      * @requires function mb_strwidth
814      * @group legacy
815      */
816     public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys()
817     {
818         $question = 'Lorem ipsum?';
819         $possibleChoices = array(
820             'foo' => 'foo',
821             'żółw' => 'bar',
822             'łabądź' => 'baz',
823         );
824         $outputShown = array(
825             $question,
826             '  [<info>foo   </info>] foo',
827             '  [<info>żółw  </info>] bar',
828             '  [<info>łabądź</info>] baz',
829         );
830         $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
831         $output->method('getFormatter')->willReturn(new OutputFormatter());
832
833         $dialog = new QuestionHelper();
834         $dialog->setInputStream($this->getInputStream("\n"));
835         $helperSet = new HelperSet(array(new FormatterHelper()));
836         $dialog->setHelperSet($helperSet);
837
838         $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown));
839
840         $question = new ChoiceQuestion($question, $possibleChoices, 'foo');
841         $dialog->ask($this->createInputInterfaceMock(), $output, $question);
842     }
843
844     /**
845      * @expectedException        \Symfony\Component\Console\Exception\RuntimeException
846      * @expectedExceptionMessage Aborted
847      */
848     public function testAskThrowsExceptionOnMissingInput()
849     {
850         $dialog = new QuestionHelper();
851         $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?'));
852     }
853
854     /**
855      * @expectedException        \Symfony\Component\Console\Exception\RuntimeException
856      * @expectedExceptionMessage Aborted
857      */
858     public function testAskThrowsExceptionOnMissingInputWithValidator()
859     {
860         $dialog = new QuestionHelper();
861
862         $question = new Question('What\'s your name?');
863         $question->setValidator(function () {
864             if (!$value) {
865                 throw new \Exception('A value is required.');
866             }
867         });
868
869         $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question);
870     }
871
872     /**
873      * @expectedException \LogicException
874      * @expectedExceptionMessage Choice question must have at least 1 choice available.
875      */
876     public function testEmptyChoices()
877     {
878         new ChoiceQuestion('Question', array(), 'irrelevant');
879     }
880
881     public function testTraversableAutocomplete()
882     {
883         if (!$this->hasSttyAvailable()) {
884             $this->markTestSkipped('`stty` is required to test autocomplete functionality');
885         }
886
887         // Acm<NEWLINE>
888         // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
889         // <NEWLINE>
890         // <UP ARROW><UP ARROW><NEWLINE>
891         // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
892         // <DOWN ARROW><NEWLINE>
893         // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
894         // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
895         $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");
896
897         $dialog = new QuestionHelper();
898         $helperSet = new HelperSet(array(new FormatterHelper()));
899         $dialog->setHelperSet($helperSet);
900
901         $question = new Question('Please select a bundle', 'FrameworkBundle');
902         $question->setAutocompleterValues(new AutocompleteValues(array('irrelevant' => 'AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')));
903
904         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
905         $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
906         $this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
907         $this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
908         $this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
909         $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
910         $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
911         $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
912     }
913
914     protected function getInputStream($input)
915     {
916         $stream = fopen('php://memory', 'r+', false);
917         fwrite($stream, $input);
918         rewind($stream);
919
920         return $stream;
921     }
922
923     protected function createOutputInterface()
924     {
925         return new StreamOutput(fopen('php://memory', 'r+', false));
926     }
927
928     protected function createInputInterfaceMock($interactive = true)
929     {
930         $mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
931         $mock->expects($this->any())
932             ->method('isInteractive')
933             ->will($this->returnValue($interactive));
934
935         return $mock;
936     }
937
938     private function hasSttyAvailable()
939     {
940         exec('stty 2>&1', $output, $exitcode);
941
942         return 0 === $exitcode;
943     }
944 }
945
946 class AutocompleteValues implements \IteratorAggregate
947 {
948     private $values;
949
950     public function __construct(array $values)
951     {
952         $this->values = $values;
953     }
954
955     public function getIterator()
956     {
957         return new \ArrayIterator($this->values);
958     }
959 }