4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Console\Tests;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Console\Application;
16 use Symfony\Component\Console\Command\Command;
17 use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
18 use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
19 use Symfony\Component\Console\Event\ConsoleCommandEvent;
20 use Symfony\Component\Console\Event\ConsoleErrorEvent;
21 use Symfony\Component\Console\Event\ConsoleExceptionEvent;
22 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
23 use Symfony\Component\Console\Exception\CommandNotFoundException;
24 use Symfony\Component\Console\Helper\FormatterHelper;
25 use Symfony\Component\Console\Helper\HelperSet;
26 use Symfony\Component\Console\Input\ArgvInput;
27 use Symfony\Component\Console\Input\ArrayInput;
28 use Symfony\Component\Console\Input\InputArgument;
29 use Symfony\Component\Console\Input\InputDefinition;
30 use Symfony\Component\Console\Input\InputInterface;
31 use Symfony\Component\Console\Input\InputOption;
32 use Symfony\Component\Console\Output\NullOutput;
33 use Symfony\Component\Console\Output\Output;
34 use Symfony\Component\Console\Output\OutputInterface;
35 use Symfony\Component\Console\Output\StreamOutput;
36 use Symfony\Component\Console\Tester\ApplicationTester;
37 use Symfony\Component\DependencyInjection\ContainerBuilder;
38 use Symfony\Component\EventDispatcher\EventDispatcher;
40 class ApplicationTest extends TestCase
42 protected static $fixturesPath;
44 public static function setUpBeforeClass()
46 self::$fixturesPath = realpath(__DIR__.'/Fixtures/');
47 require_once self::$fixturesPath.'/FooCommand.php';
48 require_once self::$fixturesPath.'/FooOptCommand.php';
49 require_once self::$fixturesPath.'/Foo1Command.php';
50 require_once self::$fixturesPath.'/Foo2Command.php';
51 require_once self::$fixturesPath.'/Foo3Command.php';
52 require_once self::$fixturesPath.'/Foo4Command.php';
53 require_once self::$fixturesPath.'/Foo5Command.php';
54 require_once self::$fixturesPath.'/FooSameCaseUppercaseCommand.php';
55 require_once self::$fixturesPath.'/FooSameCaseLowercaseCommand.php';
56 require_once self::$fixturesPath.'/FoobarCommand.php';
57 require_once self::$fixturesPath.'/BarBucCommand.php';
58 require_once self::$fixturesPath.'/FooSubnamespaced1Command.php';
59 require_once self::$fixturesPath.'/FooSubnamespaced2Command.php';
60 require_once self::$fixturesPath.'/TestTiti.php';
61 require_once self::$fixturesPath.'/TestToto.php';
64 protected function normalizeLineBreaks($text)
66 return str_replace(PHP_EOL, "\n", $text);
70 * Replaces the dynamic placeholders of the command help text with a static version.
71 * The placeholder %command.full_name% includes the script path that is not predictable
72 * and can not be tested against.
74 protected function ensureStaticCommandHelp(Application $application)
76 foreach ($application->all() as $command) {
77 $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp()));
81 public function testConstructor()
83 $application = new Application('foo', 'bar');
84 $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument');
85 $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument');
86 $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default');
89 public function testSetGetName()
91 $application = new Application();
92 $application->setName('foo');
93 $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application');
96 public function testSetGetVersion()
98 $application = new Application();
99 $application->setVersion('bar');
100 $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application');
103 public function testGetLongVersion()
105 $application = new Application('foo', 'bar');
106 $this->assertEquals('foo <info>bar</info>', $application->getLongVersion(), '->getLongVersion() returns the long version of the application');
109 public function testHelp()
111 $application = new Application();
112 $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message');
115 public function testAll()
117 $application = new Application();
118 $commands = $application->all();
119 $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands');
121 $application->add(new \FooCommand());
122 $commands = $application->all('foo');
123 $this->assertCount(1, $commands, '->all() takes a namespace as its first argument');
126 public function testAllWithCommandLoader()
128 $application = new Application();
129 $commands = $application->all();
130 $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands');
132 $application->add(new \FooCommand());
133 $commands = $application->all('foo');
134 $this->assertCount(1, $commands, '->all() takes a namespace as its first argument');
136 $application->setCommandLoader(new FactoryCommandLoader(array(
137 'foo:bar1' => function () { return new \Foo1Command(); },
139 $commands = $application->all('foo');
140 $this->assertCount(2, $commands, '->all() takes a namespace as its first argument');
141 $this->assertInstanceOf(\FooCommand::class, $commands['foo:bar'], '->all() returns the registered commands');
142 $this->assertInstanceOf(\Foo1Command::class, $commands['foo:bar1'], '->all() returns the registered commands');
145 public function testRegister()
147 $application = new Application();
148 $command = $application->register('foo');
149 $this->assertEquals('foo', $command->getName(), '->register() registers a new command');
152 public function testAdd()
154 $application = new Application();
155 $application->add($foo = new \FooCommand());
156 $commands = $application->all();
157 $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command');
159 $application = new Application();
160 $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command()));
161 $commands = $application->all();
162 $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands');
166 * @expectedException \LogicException
167 * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor.
169 public function testAddCommandWithEmptyConstructor()
171 $application = new Application();
172 $application->add(new \Foo5Command());
175 public function testHasGet()
177 $application = new Application();
178 $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered');
179 $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered');
181 $application->add($foo = new \FooCommand());
182 $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered');
183 $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name');
184 $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias');
186 $application = new Application();
187 $application->add($foo = new \FooCommand());
189 $r = new \ReflectionObject($application);
190 $p = $r->getProperty('wantHelps');
191 $p->setAccessible(true);
192 $p->setValue($application, true);
193 $command = $application->get('foo:bar');
194 $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input');
197 public function testHasGetWithCommandLoader()
199 $application = new Application();
200 $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered');
201 $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered');
203 $application->add($foo = new \FooCommand());
204 $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered');
205 $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name');
206 $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias');
208 $application->setCommandLoader(new FactoryCommandLoader(array(
209 'foo:bar1' => function () { return new \Foo1Command(); },
212 $this->assertTrue($application->has('afoobar'), '->has() returns true if an instance is registered for an alias even with command loader');
213 $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns an instance by name even with command loader');
214 $this->assertEquals($foo, $application->get('afoobar'), '->get() returns an instance by alias even with command loader');
215 $this->assertTrue($application->has('foo:bar1'), '->has() returns true for commands registered in the loader');
216 $this->assertInstanceOf(\Foo1Command::class, $foo1 = $application->get('foo:bar1'), '->get() returns a command by name from the command loader');
217 $this->assertTrue($application->has('afoobar1'), '->has() returns true for commands registered in the loader');
218 $this->assertEquals($foo1, $application->get('afoobar1'), '->get() returns a command by name from the command loader');
221 public function testSilentHelp()
223 $application = new Application();
224 $application->setAutoExit(false);
225 $application->setCatchExceptions(false);
227 $tester = new ApplicationTester($application);
228 $tester->run(array('-h' => true, '-q' => true), array('decorated' => false));
230 $this->assertEmpty($tester->getDisplay(true));
234 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
235 * @expectedExceptionMessage The command "foofoo" does not exist.
237 public function testGetInvalidCommand()
239 $application = new Application();
240 $application->get('foofoo');
243 public function testGetNamespaces()
245 $application = new Application();
246 $application->add(new \FooCommand());
247 $application->add(new \Foo1Command());
248 $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces');
251 public function testFindNamespace()
253 $application = new Application();
254 $application->add(new \FooCommand());
255 $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists');
256 $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation');
257 $application->add(new \Foo2Command());
258 $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists');
261 public function testFindNamespaceWithSubnamespaces()
263 $application = new Application();
264 $application->add(new \FooSubnamespaced1Command());
265 $application->add(new \FooSubnamespaced2Command());
266 $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces');
269 public function testFindAmbiguousNamespace()
271 $application = new Application();
272 $application->add(new \BarBucCommand());
273 $application->add(new \FooCommand());
274 $application->add(new \Foo2Command());
276 $expectedMsg = "The namespace \"f\" is ambiguous.\nDid you mean one of these?\n foo\n foo1";
278 if (method_exists($this, 'expectException')) {
279 $this->expectException(CommandNotFoundException::class);
280 $this->expectExceptionMessage($expectedMsg);
282 $this->setExpectedException(CommandNotFoundException::class, $expectedMsg);
285 $application->findNamespace('f');
288 public function testFindNonAmbiguous()
290 $application = new Application();
291 $application->add(new \TestTiti());
292 $application->add(new \TestToto());
293 $this->assertEquals('test-toto', $application->find('test')->getName());
297 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
298 * @expectedExceptionMessage There are no commands defined in the "bar" namespace.
300 public function testFindInvalidNamespace()
302 $application = new Application();
303 $application->findNamespace('bar');
307 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
308 * @expectedExceptionMessage Command "foo1" is not defined
310 public function testFindUniqueNameButNamespaceName()
312 $application = new Application();
313 $application->add(new \FooCommand());
314 $application->add(new \Foo1Command());
315 $application->add(new \Foo2Command());
317 $application->find($commandName = 'foo1');
320 public function testFind()
322 $application = new Application();
323 $application->add(new \FooCommand());
325 $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists');
326 $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists');
327 $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists');
328 $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist');
329 $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias');
332 public function testFindCaseSensitiveFirst()
334 $application = new Application();
335 $application->add(new \FooSameCaseUppercaseCommand());
336 $application->add(new \FooSameCaseLowercaseCommand());
338 $this->assertInstanceOf('FooSameCaseUppercaseCommand', $application->find('f:B'), '->find() returns a command if the abbreviation is the correct case');
339 $this->assertInstanceOf('FooSameCaseUppercaseCommand', $application->find('f:BAR'), '->find() returns a command if the abbreviation is the correct case');
340 $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:b'), '->find() returns a command if the abbreviation is the correct case');
341 $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation is the correct case');
344 public function testFindCaseInsensitiveAsFallback()
346 $application = new Application();
347 $application->add(new \FooSameCaseLowercaseCommand());
349 $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:b'), '->find() returns a command if the abbreviation is the correct case');
350 $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:B'), '->find() will fallback to case insensitivity');
351 $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('FoO:BaR'), '->find() will fallback to case insensitivity');
355 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
356 * @expectedExceptionMessage Command "FoO:BaR" is ambiguous
358 public function testFindCaseInsensitiveSuggestions()
360 $application = new Application();
361 $application->add(new \FooSameCaseLowercaseCommand());
362 $application->add(new \FooSameCaseUppercaseCommand());
364 $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('FoO:BaR'), '->find() will find two suggestions with case insensitivity');
367 public function testFindWithCommandLoader()
369 $application = new Application();
370 $application->setCommandLoader(new FactoryCommandLoader(array(
371 'foo:bar' => $f = function () { return new \FooCommand(); },
374 $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists');
375 $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists');
376 $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists');
377 $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist');
378 $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias');
382 * @dataProvider provideAmbiguousAbbreviations
384 public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage)
386 if (method_exists($this, 'expectException')) {
387 $this->expectException('Symfony\Component\Console\Exception\CommandNotFoundException');
388 $this->expectExceptionMessage($expectedExceptionMessage);
390 $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage);
393 $application = new Application();
394 $application->add(new \FooCommand());
395 $application->add(new \Foo1Command());
396 $application->add(new \Foo2Command());
398 $application->find($abbreviation);
401 public function provideAmbiguousAbbreviations()
404 array('f', 'Command "f" is not defined.'),
407 "Command \"a\" is ambiguous.\nDid you mean one of these?\n".
408 " afoobar The foo:bar command\n".
409 " afoobar1 The foo:bar1 command\n".
410 ' afoobar2 The foo1:bar command',
414 "Command \"foo:b\" is ambiguous.\nDid you mean one of these?\n".
415 " foo:bar The foo:bar command\n".
416 " foo:bar1 The foo:bar1 command\n".
417 ' foo1:bar The foo1:bar command',
422 public function testFindCommandEqualNamespace()
424 $application = new Application();
425 $application->add(new \Foo3Command());
426 $application->add(new \Foo4Command());
428 $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name');
429 $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name');
432 public function testFindCommandWithAmbiguousNamespacesButUniqueName()
434 $application = new Application();
435 $application->add(new \FooCommand());
436 $application->add(new \FoobarCommand());
438 $this->assertInstanceOf('FoobarCommand', $application->find('f:f'));
441 public function testFindCommandWithMissingNamespace()
443 $application = new Application();
444 $application->add(new \Foo4Command());
446 $this->assertInstanceOf('Foo4Command', $application->find('f::t'));
450 * @dataProvider provideInvalidCommandNamesSingle
451 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
452 * @expectedExceptionMessage Did you mean this
454 public function testFindAlternativeExceptionMessageSingle($name)
456 $application = new Application();
457 $application->add(new \Foo3Command());
458 $application->find($name);
461 public function provideInvalidCommandNamesSingle()
469 public function testFindAlternativeExceptionMessageMultiple()
471 $application = new Application();
472 $application->add(new \FooCommand());
473 $application->add(new \Foo1Command());
474 $application->add(new \Foo2Command());
478 $application->find('foo:baR');
479 $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives');
480 } catch (\Exception $e) {
481 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives');
482 $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives');
483 $this->assertRegExp('/foo1:bar/', $e->getMessage());
484 $this->assertRegExp('/foo:bar/', $e->getMessage());
487 // Namespace + plural
489 $application->find('foo2:bar');
490 $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives');
491 } catch (\Exception $e) {
492 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives');
493 $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives');
494 $this->assertRegExp('/foo1/', $e->getMessage());
497 $application->add(new \Foo3Command());
498 $application->add(new \Foo4Command());
500 // Subnamespace + plural
502 $a = $application->find('foo3:');
503 $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives');
504 } catch (\Exception $e) {
505 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e);
506 $this->assertRegExp('/foo3:bar/', $e->getMessage());
507 $this->assertRegExp('/foo3:bar:toh/', $e->getMessage());
511 public function testFindAlternativeCommands()
513 $application = new Application();
515 $application->add(new \FooCommand());
516 $application->add(new \Foo1Command());
517 $application->add(new \Foo2Command());
520 $application->find($commandName = 'Unknown command');
521 $this->fail('->find() throws a CommandNotFoundException if command does not exist');
522 } catch (\Exception $e) {
523 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist');
524 $this->assertSame(array(), $e->getAlternatives());
525 $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives');
528 // Test if "bar1" command throw a "CommandNotFoundException" and does not contain
529 // "foo:bar" as alternative because "bar1" is too far from "foo:bar"
531 $application->find($commandName = 'bar1');
532 $this->fail('->find() throws a CommandNotFoundException if command does not exist');
533 } catch (\Exception $e) {
534 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist');
535 $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives());
536 $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives');
537 $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"');
538 $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"');
539 $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative');
543 public function testFindAlternativeCommandsWithAnAlias()
545 $fooCommand = new \FooCommand();
546 $fooCommand->setAliases(array('foo2'));
548 $application = new Application();
549 $application->add($fooCommand);
551 $result = $application->find('foo');
553 $this->assertSame($fooCommand, $result);
556 public function testFindAlternativeNamespace()
558 $application = new Application();
560 $application->add(new \FooCommand());
561 $application->add(new \Foo1Command());
562 $application->add(new \Foo2Command());
563 $application->add(new \Foo3Command());
566 $application->find('Unknown-namespace:Unknown-command');
567 $this->fail('->find() throws a CommandNotFoundException if namespace does not exist');
568 } catch (\Exception $e) {
569 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist');
570 $this->assertSame(array(), $e->getAlternatives());
571 $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives');
575 $application->find('foo2:command');
576 $this->fail('->find() throws a CommandNotFoundException if namespace does not exist');
577 } catch (\Exception $e) {
578 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist');
579 $this->assertCount(3, $e->getAlternatives());
580 $this->assertContains('foo', $e->getAlternatives());
581 $this->assertContains('foo1', $e->getAlternatives());
582 $this->assertContains('foo3', $e->getAlternatives());
583 $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative');
584 $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"');
585 $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"');
586 $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"');
590 public function testFindAlternativesOutput()
592 $application = new Application();
594 $application->add(new \FooCommand());
595 $application->add(new \Foo1Command());
596 $application->add(new \Foo2Command());
597 $application->add(new \Foo3Command());
599 $expectedAlternatives = array(
610 $application->find('foo');
611 $this->fail('->find() throws a CommandNotFoundException if command is not defined');
612 } catch (\Exception $e) {
613 $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command is not defined');
614 $this->assertSame($expectedAlternatives, $e->getAlternatives());
616 $this->assertRegExp('/Command "foo" is not defined\..*Did you mean one of these\?.*/Ums', $e->getMessage());
620 public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces()
622 $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('getNamespaces'))->getMock();
623 $application->expects($this->once())
624 ->method('getNamespaces')
625 ->will($this->returnValue(array('foo:sublong', 'bar:sub')));
627 $this->assertEquals('foo:sublong', $application->findNamespace('f:sub'));
631 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
632 * @expectedExceptionMessage Command "foo::bar" is not defined.
634 public function testFindWithDoubleColonInNameThrowsException()
636 $application = new Application();
637 $application->add(new \FooCommand());
638 $application->add(new \Foo4Command());
639 $application->find('foo::bar');
642 public function testSetCatchExceptions()
644 $application = new Application();
645 $application->setAutoExit(false);
646 putenv('COLUMNS=120');
647 $tester = new ApplicationTester($application);
649 $application->setCatchExceptions(true);
650 $this->assertTrue($application->areExceptionsCaught());
652 $tester->run(array('command' => 'foo'), array('decorated' => false));
653 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag');
655 $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
656 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag');
657 $this->assertSame('', $tester->getDisplay(true));
659 $application->setCatchExceptions(false);
661 $tester->run(array('command' => 'foo'), array('decorated' => false));
662 $this->fail('->setCatchExceptions() sets the catch exception flag');
663 } catch (\Exception $e) {
664 $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag');
665 $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag');
669 public function testAutoExitSetting()
671 $application = new Application();
672 $this->assertTrue($application->isAutoExitEnabled());
674 $application->setAutoExit(false);
675 $this->assertFalse($application->isAutoExitEnabled());
678 public function testRenderException()
680 $application = new Application();
681 $application->setAutoExit(false);
682 putenv('COLUMNS=120');
683 $tester = new ApplicationTester($application);
685 $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
686 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception');
688 $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true));
689 $this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose');
691 $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false, 'capture_stderr_separately' => true));
692 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command');
694 $application->add(new \Foo3Command());
695 $tester = new ApplicationTester($application);
696 $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'capture_stderr_separately' => true));
697 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
699 $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE));
700 $this->assertRegExp('/\[Exception\]\s*First exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is default and verbosity is verbose');
701 $this->assertRegExp('/\[Exception\]\s*Second exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is 0 and verbosity is verbose');
702 $this->assertRegExp('/\[Exception \(404\)\]\s*Third exception/', $tester->getDisplay(), '->renderException() renders a pretty exception with code exception when code exception is 404 and verbosity is verbose');
704 $tester->run(array('command' => 'foo3:bar'), array('decorated' => true));
705 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions');
707 $tester->run(array('command' => 'foo3:bar'), array('decorated' => true, 'capture_stderr_separately' => true));
708 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
710 $application = new Application();
711 $application->setAutoExit(false);
712 putenv('COLUMNS=32');
713 $tester = new ApplicationTester($application);
715 $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
716 $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
717 putenv('COLUMNS=120');
720 public function testRenderExceptionWithDoubleWidthCharacters()
722 $application = new Application();
723 $application->setAutoExit(false);
724 putenv('COLUMNS=120');
725 $application->register('foo')->setCode(function () {
726 throw new \Exception('エラーメッセージ');
728 $tester = new ApplicationTester($application);
730 $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
731 $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
733 $tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true));
734 $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
736 $application = new Application();
737 $application->setAutoExit(false);
738 putenv('COLUMNS=32');
739 $application->register('foo')->setCode(function () {
740 throw new \Exception('コマンドの実行中にエラーが発生しました。');
742 $tester = new ApplicationTester($application);
743 $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
744 $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
745 putenv('COLUMNS=120');
748 public function testRenderExceptionEscapesLines()
750 $application = new Application();
751 $application->setAutoExit(false);
752 putenv('COLUMNS=22');
753 $application->register('foo')->setCode(function () {
754 throw new \Exception('dont break here <info>!</info>');
756 $tester = new ApplicationTester($application);
758 $tester->run(array('command' => 'foo'), array('decorated' => false));
759 $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_escapeslines.txt', $tester->getDisplay(true), '->renderException() escapes lines containing formatting');
760 putenv('COLUMNS=120');
763 public function testRenderExceptionLineBreaks()
765 $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('getTerminalWidth'))->getMock();
766 $application->setAutoExit(false);
767 $application->expects($this->any())
768 ->method('getTerminalWidth')
769 ->will($this->returnValue(120));
770 $application->register('foo')->setCode(function () {
771 throw new \InvalidArgumentException("\n\nline 1 with extra spaces \nline 2\n\nline 4\n");
773 $tester = new ApplicationTester($application);
775 $tester->run(array('command' => 'foo'), array('decorated' => false));
776 $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_linebreaks.txt', $tester->getDisplay(true), '->renderException() keep multiple line breaks');
779 public function testRun()
781 $application = new Application();
782 $application->setAutoExit(false);
783 $application->setCatchExceptions(false);
784 $application->add($command = new \Foo1Command());
785 $_SERVER['argv'] = array('cli.php', 'foo:bar1');
791 $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given');
792 $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given');
794 $application = new Application();
795 $application->setAutoExit(false);
796 $application->setCatchExceptions(false);
798 $this->ensureStaticCommandHelp($application);
799 $tester = new ApplicationTester($application);
801 $tester->run(array(), array('decorated' => false));
802 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed');
804 $tester->run(array('--help' => true), array('decorated' => false));
805 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed');
807 $tester->run(array('-h' => true), array('decorated' => false));
808 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed');
810 $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false));
811 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed');
813 $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false));
814 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed');
816 $tester->run(array('--ansi' => true));
817 $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed');
819 $tester->run(array('--no-ansi' => true));
820 $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed');
822 $tester->run(array('--version' => true), array('decorated' => false));
823 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed');
825 $tester->run(array('-V' => true), array('decorated' => false));
826 $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed');
828 $tester->run(array('command' => 'list', '--quiet' => true));
829 $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed');
830 $this->assertFalse($tester->getInput()->isInteractive(), '->run() sets off the interactive mode if --quiet is passed');
832 $tester->run(array('command' => 'list', '-q' => true));
833 $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed');
834 $this->assertFalse($tester->getInput()->isInteractive(), '->run() sets off the interactive mode if -q is passed');
836 $tester->run(array('command' => 'list', '--verbose' => true));
837 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed');
839 $tester->run(array('command' => 'list', '--verbose' => 1));
840 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed');
842 $tester->run(array('command' => 'list', '--verbose' => 2));
843 $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed');
845 $tester->run(array('command' => 'list', '--verbose' => 3));
846 $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed');
848 $tester->run(array('command' => 'list', '--verbose' => 4));
849 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed');
851 $tester->run(array('command' => 'list', '-v' => true));
852 $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed');
854 $tester->run(array('command' => 'list', '-vv' => true));
855 $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed');
857 $tester->run(array('command' => 'list', '-vvv' => true));
858 $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed');
860 $application = new Application();
861 $application->setAutoExit(false);
862 $application->setCatchExceptions(false);
863 $application->add(new \FooCommand());
864 $tester = new ApplicationTester($application);
866 $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false));
867 $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed');
869 $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false));
870 $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed');
876 * If the "verbose" option is just before an argument in ArgvInput,
877 * an argument value should not be treated as verbosity value.
878 * This test will fail with "Not enough arguments." if broken
880 public function testVerboseValueNotBreakArguments()
882 $application = new Application();
883 $application->setAutoExit(false);
884 $application->setCatchExceptions(false);
885 $application->add(new \FooCommand());
887 $output = new StreamOutput(fopen('php://memory', 'w', false));
889 $input = new ArgvInput(array('cli.php', '-v', 'foo:bar'));
890 $application->run($input, $output);
892 $this->addToAssertionCount(1);
894 $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar'));
895 $application->run($input, $output);
897 $this->addToAssertionCount(1);
900 public function testRunReturnsIntegerExitCode()
902 $exception = new \Exception('', 4);
904 $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('doRun'))->getMock();
905 $application->setAutoExit(false);
906 $application->expects($this->once())
908 ->will($this->throwException($exception));
910 $exitCode = $application->run(new ArrayInput(array()), new NullOutput());
912 $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception');
915 public function testRunDispatchesIntegerExitCode()
917 $passedRightValue = false;
919 // We can assume here that some other test asserts that the event is dispatched at all
920 $dispatcher = new EventDispatcher();
921 $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use (&$passedRightValue) {
922 $passedRightValue = (4 === $event->getExitCode());
925 $application = new Application();
926 $application->setDispatcher($dispatcher);
927 $application->setAutoExit(false);
929 $application->register('test')->setCode(function (InputInterface $input, OutputInterface $output) {
930 throw new \Exception('', 4);
933 $tester = new ApplicationTester($application);
934 $tester->run(array('command' => 'test'));
936 $this->assertTrue($passedRightValue, '-> exit code 4 was passed in the console.terminate event');
939 public function testRunReturnsExitCodeOneForExceptionCodeZero()
941 $exception = new \Exception('', 0);
943 $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('doRun'))->getMock();
944 $application->setAutoExit(false);
945 $application->expects($this->once())
947 ->will($this->throwException($exception));
949 $exitCode = $application->run(new ArrayInput(array()), new NullOutput());
951 $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0');
954 public function testRunDispatchesExitCodeOneForExceptionCodeZero()
956 $passedRightValue = false;
958 // We can assume here that some other test asserts that the event is dispatched at all
959 $dispatcher = new EventDispatcher();
960 $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use (&$passedRightValue) {
961 $passedRightValue = (1 === $event->getExitCode());
964 $application = new Application();
965 $application->setDispatcher($dispatcher);
966 $application->setAutoExit(false);
968 $application->register('test')->setCode(function (InputInterface $input, OutputInterface $output) {
969 throw new \Exception();
972 $tester = new ApplicationTester($application);
973 $tester->run(array('command' => 'test'));
975 $this->assertTrue($passedRightValue, '-> exit code 1 was passed in the console.terminate event');
979 * @expectedException \LogicException
980 * @expectedExceptionMessage An option with shortcut "e" already exists.
982 public function testAddingOptionWithDuplicateShortcut()
984 $dispatcher = new EventDispatcher();
985 $application = new Application();
986 $application->setAutoExit(false);
987 $application->setCatchExceptions(false);
988 $application->setDispatcher($dispatcher);
990 $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment'));
994 ->setAliases(array('f'))
995 ->setDefinition(array(new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.')))
996 ->setCode(function (InputInterface $input, OutputInterface $output) {})
999 $input = new ArrayInput(array('command' => 'foo'));
1000 $output = new NullOutput();
1002 $application->run($input, $output);
1006 * @expectedException \LogicException
1007 * @dataProvider getAddingAlreadySetDefinitionElementData
1009 public function testAddingAlreadySetDefinitionElementData($def)
1011 $application = new Application();
1012 $application->setAutoExit(false);
1013 $application->setCatchExceptions(false);
1016 ->setDefinition(array($def))
1017 ->setCode(function (InputInterface $input, OutputInterface $output) {})
1020 $input = new ArrayInput(array('command' => 'foo'));
1021 $output = new NullOutput();
1022 $application->run($input, $output);
1025 public function getAddingAlreadySetDefinitionElementData()
1028 array(new InputArgument('command', InputArgument::REQUIRED)),
1029 array(new InputOption('quiet', '', InputOption::VALUE_NONE)),
1030 array(new InputOption('query', 'q', InputOption::VALUE_NONE)),
1034 public function testGetDefaultHelperSetReturnsDefaultValues()
1036 $application = new Application();
1037 $application->setAutoExit(false);
1038 $application->setCatchExceptions(false);
1040 $helperSet = $application->getHelperSet();
1042 $this->assertTrue($helperSet->has('formatter'));
1045 public function testAddingSingleHelperSetOverwritesDefaultValues()
1047 $application = new Application();
1048 $application->setAutoExit(false);
1049 $application->setCatchExceptions(false);
1051 $application->setHelperSet(new HelperSet(array(new FormatterHelper())));
1053 $helperSet = $application->getHelperSet();
1055 $this->assertTrue($helperSet->has('formatter'));
1057 // no other default helper set should be returned
1058 $this->assertFalse($helperSet->has('dialog'));
1059 $this->assertFalse($helperSet->has('progress'));
1062 public function testOverwritingDefaultHelperSetOverwritesDefaultValues()
1064 $application = new CustomApplication();
1065 $application->setAutoExit(false);
1066 $application->setCatchExceptions(false);
1068 $application->setHelperSet(new HelperSet(array(new FormatterHelper())));
1070 $helperSet = $application->getHelperSet();
1072 $this->assertTrue($helperSet->has('formatter'));
1074 // no other default helper set should be returned
1075 $this->assertFalse($helperSet->has('dialog'));
1076 $this->assertFalse($helperSet->has('progress'));
1079 public function testGetDefaultInputDefinitionReturnsDefaultValues()
1081 $application = new Application();
1082 $application->setAutoExit(false);
1083 $application->setCatchExceptions(false);
1085 $inputDefinition = $application->getDefinition();
1087 $this->assertTrue($inputDefinition->hasArgument('command'));
1089 $this->assertTrue($inputDefinition->hasOption('help'));
1090 $this->assertTrue($inputDefinition->hasOption('quiet'));
1091 $this->assertTrue($inputDefinition->hasOption('verbose'));
1092 $this->assertTrue($inputDefinition->hasOption('version'));
1093 $this->assertTrue($inputDefinition->hasOption('ansi'));
1094 $this->assertTrue($inputDefinition->hasOption('no-ansi'));
1095 $this->assertTrue($inputDefinition->hasOption('no-interaction'));
1098 public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues()
1100 $application = new CustomApplication();
1101 $application->setAutoExit(false);
1102 $application->setCatchExceptions(false);
1104 $inputDefinition = $application->getDefinition();
1106 // check whether the default arguments and options are not returned any more
1107 $this->assertFalse($inputDefinition->hasArgument('command'));
1109 $this->assertFalse($inputDefinition->hasOption('help'));
1110 $this->assertFalse($inputDefinition->hasOption('quiet'));
1111 $this->assertFalse($inputDefinition->hasOption('verbose'));
1112 $this->assertFalse($inputDefinition->hasOption('version'));
1113 $this->assertFalse($inputDefinition->hasOption('ansi'));
1114 $this->assertFalse($inputDefinition->hasOption('no-ansi'));
1115 $this->assertFalse($inputDefinition->hasOption('no-interaction'));
1117 $this->assertTrue($inputDefinition->hasOption('custom'));
1120 public function testSettingCustomInputDefinitionOverwritesDefaultValues()
1122 $application = new Application();
1123 $application->setAutoExit(false);
1124 $application->setCatchExceptions(false);
1126 $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))));
1128 $inputDefinition = $application->getDefinition();
1130 // check whether the default arguments and options are not returned any more
1131 $this->assertFalse($inputDefinition->hasArgument('command'));
1133 $this->assertFalse($inputDefinition->hasOption('help'));
1134 $this->assertFalse($inputDefinition->hasOption('quiet'));
1135 $this->assertFalse($inputDefinition->hasOption('verbose'));
1136 $this->assertFalse($inputDefinition->hasOption('version'));
1137 $this->assertFalse($inputDefinition->hasOption('ansi'));
1138 $this->assertFalse($inputDefinition->hasOption('no-ansi'));
1139 $this->assertFalse($inputDefinition->hasOption('no-interaction'));
1141 $this->assertTrue($inputDefinition->hasOption('custom'));
1144 public function testRunWithDispatcher()
1146 $application = new Application();
1147 $application->setAutoExit(false);
1148 $application->setDispatcher($this->getDispatcher());
1150 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1151 $output->write('foo.');
1154 $tester = new ApplicationTester($application);
1155 $tester->run(array('command' => 'foo'));
1156 $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay());
1160 * @expectedException \LogicException
1161 * @expectedExceptionMessage error
1163 public function testRunWithExceptionAndDispatcher()
1165 $application = new Application();
1166 $application->setDispatcher($this->getDispatcher());
1167 $application->setAutoExit(false);
1168 $application->setCatchExceptions(false);
1170 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1171 throw new \RuntimeException('foo');
1174 $tester = new ApplicationTester($application);
1175 $tester->run(array('command' => 'foo'));
1178 public function testRunDispatchesAllEventsWithException()
1180 $application = new Application();
1181 $application->setDispatcher($this->getDispatcher());
1182 $application->setAutoExit(false);
1184 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1185 $output->write('foo.');
1187 throw new \RuntimeException('foo');
1190 $tester = new ApplicationTester($application);
1191 $tester->run(array('command' => 'foo'));
1192 $this->assertContains('before.foo.error.after.', $tester->getDisplay());
1195 public function testRunDispatchesAllEventsWithExceptionInListener()
1197 $dispatcher = $this->getDispatcher();
1198 $dispatcher->addListener('console.command', function () {
1199 throw new \RuntimeException('foo');
1202 $application = new Application();
1203 $application->setDispatcher($dispatcher);
1204 $application->setAutoExit(false);
1206 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1207 $output->write('foo.');
1210 $tester = new ApplicationTester($application);
1211 $tester->run(array('command' => 'foo'));
1212 $this->assertContains('before.error.after.', $tester->getDisplay());
1218 public function testRunWithError()
1220 $application = new Application();
1221 $application->setAutoExit(false);
1222 $application->setCatchExceptions(false);
1224 $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
1225 $output->write('dym.');
1227 throw new \Error('dymerr');
1230 $tester = new ApplicationTester($application);
1233 $tester->run(array('command' => 'dym'));
1234 $this->fail('Error expected.');
1235 } catch (\Error $e) {
1236 $this->assertSame('dymerr', $e->getMessage());
1240 public function testRunAllowsErrorListenersToSilenceTheException()
1242 $dispatcher = $this->getDispatcher();
1243 $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
1244 $event->getOutput()->write('silenced.');
1246 $event->setExitCode(0);
1249 $dispatcher->addListener('console.command', function () {
1250 throw new \RuntimeException('foo');
1253 $application = new Application();
1254 $application->setDispatcher($dispatcher);
1255 $application->setAutoExit(false);
1257 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1258 $output->write('foo.');
1261 $tester = new ApplicationTester($application);
1262 $tester->run(array('command' => 'foo'));
1263 $this->assertContains('before.error.silenced.after.', $tester->getDisplay());
1264 $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $tester->getStatusCode());
1267 public function testConsoleErrorEventIsTriggeredOnCommandNotFound()
1269 $dispatcher = new EventDispatcher();
1270 $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
1271 $this->assertNull($event->getCommand());
1272 $this->assertInstanceOf(CommandNotFoundException::class, $event->getError());
1273 $event->getOutput()->write('silenced command not found');
1276 $application = new Application();
1277 $application->setDispatcher($dispatcher);
1278 $application->setAutoExit(false);
1280 $tester = new ApplicationTester($application);
1281 $tester->run(array('command' => 'unknown'));
1282 $this->assertContains('silenced command not found', $tester->getDisplay());
1283 $this->assertEquals(1, $tester->getStatusCode());
1288 * @expectedDeprecation The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.
1290 public function testLegacyExceptionListenersAreStillTriggered()
1292 $dispatcher = $this->getDispatcher();
1293 $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) {
1294 $event->getOutput()->write('caught.');
1296 $event->setException(new \RuntimeException('replaced in caught.'));
1299 $application = new Application();
1300 $application->setDispatcher($dispatcher);
1301 $application->setAutoExit(false);
1303 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1304 throw new \RuntimeException('foo');
1307 $tester = new ApplicationTester($application);
1308 $tester->run(array('command' => 'foo'));
1309 $this->assertContains('before.caught.error.after.', $tester->getDisplay());
1310 $this->assertContains('replaced in caught.', $tester->getDisplay());
1316 public function testErrorIsRethrownIfNotHandledByConsoleErrorEvent()
1318 $application = new Application();
1319 $application->setAutoExit(false);
1320 $application->setCatchExceptions(false);
1321 $application->setDispatcher(new EventDispatcher());
1323 $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
1324 new \UnknownClass();
1327 $tester = new ApplicationTester($application);
1330 $tester->run(array('command' => 'dym'));
1331 $this->fail('->run() should rethrow PHP errors if not handled via ConsoleErrorEvent.');
1332 } catch (\Error $e) {
1333 $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found');
1339 * @expectedException \LogicException
1340 * @expectedExceptionMessage error
1342 public function testRunWithErrorAndDispatcher()
1344 $application = new Application();
1345 $application->setDispatcher($this->getDispatcher());
1346 $application->setAutoExit(false);
1347 $application->setCatchExceptions(false);
1349 $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
1350 $output->write('dym.');
1352 throw new \Error('dymerr');
1355 $tester = new ApplicationTester($application);
1356 $tester->run(array('command' => 'dym'));
1357 $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
1363 public function testRunDispatchesAllEventsWithError()
1365 $application = new Application();
1366 $application->setDispatcher($this->getDispatcher());
1367 $application->setAutoExit(false);
1369 $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
1370 $output->write('dym.');
1372 throw new \Error('dymerr');
1375 $tester = new ApplicationTester($application);
1376 $tester->run(array('command' => 'dym'));
1377 $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
1383 public function testRunWithErrorFailingStatusCode()
1385 $application = new Application();
1386 $application->setDispatcher($this->getDispatcher());
1387 $application->setAutoExit(false);
1389 $application->register('dus')->setCode(function (InputInterface $input, OutputInterface $output) {
1390 $output->write('dus.');
1392 throw new \Error('duserr');
1395 $tester = new ApplicationTester($application);
1396 $tester->run(array('command' => 'dus'));
1397 $this->assertSame(1, $tester->getStatusCode(), 'Status code should be 1');
1400 public function testRunWithDispatcherSkippingCommand()
1402 $application = new Application();
1403 $application->setDispatcher($this->getDispatcher(true));
1404 $application->setAutoExit(false);
1406 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1407 $output->write('foo.');
1410 $tester = new ApplicationTester($application);
1411 $exitCode = $tester->run(array('command' => 'foo'));
1412 $this->assertContains('before.after.', $tester->getDisplay());
1413 $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode);
1416 public function testRunWithDispatcherAccessingInputOptions()
1418 $noInteractionValue = null;
1421 $dispatcher = $this->getDispatcher();
1422 $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) {
1423 $input = $event->getInput();
1425 $noInteractionValue = $input->getOption('no-interaction');
1426 $quietValue = $input->getOption('quiet');
1429 $application = new Application();
1430 $application->setDispatcher($dispatcher);
1431 $application->setAutoExit(false);
1433 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1434 $output->write('foo.');
1437 $tester = new ApplicationTester($application);
1438 $tester->run(array('command' => 'foo', '--no-interaction' => true));
1440 $this->assertTrue($noInteractionValue);
1441 $this->assertFalse($quietValue);
1444 public function testRunWithDispatcherAddingInputOptions()
1448 $dispatcher = $this->getDispatcher();
1449 $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) {
1450 $definition = $event->getCommand()->getDefinition();
1451 $input = $event->getInput();
1453 $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED));
1454 $input->bind($definition);
1456 $extraValue = $input->getOption('extra');
1459 $application = new Application();
1460 $application->setDispatcher($dispatcher);
1461 $application->setAutoExit(false);
1463 $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
1464 $output->write('foo.');
1467 $tester = new ApplicationTester($application);
1468 $tester->run(array('command' => 'foo', '--extra' => 'some test value'));
1470 $this->assertEquals('some test value', $extraValue);
1476 public function testTerminalDimensions()
1478 $application = new Application();
1479 $originalDimensions = $application->getTerminalDimensions();
1480 $this->assertCount(2, $originalDimensions);
1483 if ($originalDimensions[0] == $width) {
1487 $application->setTerminalDimensions($width, 80);
1488 $this->assertSame(array($width, 80), $application->getTerminalDimensions());
1491 public function testSetRunCustomDefaultCommand()
1493 $command = new \FooCommand();
1495 $application = new Application();
1496 $application->setAutoExit(false);
1497 $application->add($command);
1498 $application->setDefaultCommand($command->getName());
1500 $tester = new ApplicationTester($application);
1501 $tester->run(array(), array('interactive' => false));
1502 $this->assertEquals('called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command');
1504 $application = new CustomDefaultCommandApplication();
1505 $application->setAutoExit(false);
1507 $tester = new ApplicationTester($application);
1508 $tester->run(array(), array('interactive' => false));
1510 $this->assertEquals('called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command');
1513 public function testSetRunCustomDefaultCommandWithOption()
1515 $command = new \FooOptCommand();
1517 $application = new Application();
1518 $application->setAutoExit(false);
1519 $application->add($command);
1520 $application->setDefaultCommand($command->getName());
1522 $tester = new ApplicationTester($application);
1523 $tester->run(array('--fooopt' => 'opt'), array('interactive' => false));
1525 $this->assertEquals('called'.PHP_EOL.'opt'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command');
1528 public function testSetRunCustomSingleCommand()
1530 $command = new \FooCommand();
1532 $application = new Application();
1533 $application->setAutoExit(false);
1534 $application->add($command);
1535 $application->setDefaultCommand($command->getName(), true);
1537 $tester = new ApplicationTester($application);
1539 $tester->run(array());
1540 $this->assertContains('called', $tester->getDisplay());
1542 $tester->run(array('--help' => true));
1543 $this->assertContains('The foo:bar command', $tester->getDisplay());
1547 * @requires function posix_isatty
1549 public function testCanCheckIfTerminalIsInteractive()
1551 $application = new CustomDefaultCommandApplication();
1552 $application->setAutoExit(false);
1554 $tester = new ApplicationTester($application);
1555 $tester->run(array('command' => 'help'));
1557 $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n')));
1559 $inputStream = $tester->getInput()->getStream();
1560 $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream));
1563 public function testRunLazyCommandService()
1565 $container = new ContainerBuilder();
1566 $container->addCompilerPass(new AddConsoleCommandPass());
1568 ->register('lazy-command', LazyCommand::class)
1569 ->addTag('console.command', array('command' => 'lazy:command'))
1570 ->addTag('console.command', array('command' => 'lazy:alias'))
1571 ->addTag('console.command', array('command' => 'lazy:alias2'));
1572 $container->compile();
1574 $application = new Application();
1575 $application->setCommandLoader($container->get('console.command_loader'));
1576 $application->setAutoExit(false);
1578 $tester = new ApplicationTester($application);
1580 $tester->run(array('command' => 'lazy:command'));
1581 $this->assertSame("lazy-command called\n", $tester->getDisplay(true));
1583 $tester->run(array('command' => 'lazy:alias'));
1584 $this->assertSame("lazy-command called\n", $tester->getDisplay(true));
1586 $tester->run(array('command' => 'lazy:alias2'));
1587 $this->assertSame("lazy-command called\n", $tester->getDisplay(true));
1589 $command = $application->get('lazy:command');
1590 $this->assertSame(array('lazy:alias', 'lazy:alias2'), $command->getAliases());
1594 * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
1596 public function testGetDisabledLazyCommand()
1598 $application = new Application();
1599 $application->setCommandLoader(new FactoryCommandLoader(array('disabled' => function () { return new DisabledCommand(); })));
1600 $application->get('disabled');
1603 public function testHasReturnsFalseForDisabledLazyCommand()
1605 $application = new Application();
1606 $application->setCommandLoader(new FactoryCommandLoader(array('disabled' => function () { return new DisabledCommand(); })));
1607 $this->assertFalse($application->has('disabled'));
1610 public function testAllExcludesDisabledLazyCommand()
1612 $application = new Application();
1613 $application->setCommandLoader(new FactoryCommandLoader(array('disabled' => function () { return new DisabledCommand(); })));
1614 $this->assertArrayNotHasKey('disabled', $application->all());
1617 protected function getDispatcher($skipCommand = false)
1619 $dispatcher = new EventDispatcher();
1620 $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) {
1621 $event->getOutput()->write('before.');
1624 $event->disableCommand();
1627 $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) {
1628 $event->getOutput()->writeln('after.');
1630 if (!$skipCommand) {
1631 $event->setExitCode(ConsoleCommandEvent::RETURN_CODE_DISABLED);
1634 $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
1635 $event->getOutput()->write('error.');
1637 $event->setError(new \LogicException('error.', $event->getExitCode(), $event->getError()));
1646 public function testErrorIsRethrownIfNotHandledByConsoleErrorEventWithCatchingEnabled()
1648 $application = new Application();
1649 $application->setAutoExit(false);
1650 $application->setDispatcher(new EventDispatcher());
1652 $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
1653 new \UnknownClass();
1656 $tester = new ApplicationTester($application);
1659 $tester->run(array('command' => 'dym'));
1660 $this->fail('->run() should rethrow PHP errors if not handled via ConsoleErrorEvent.');
1661 } catch (\Error $e) {
1662 $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found');
1666 protected function tearDown()
1668 putenv('SHELL_VERBOSITY');
1669 unset($_ENV['SHELL_VERBOSITY']);
1670 unset($_SERVER['SHELL_VERBOSITY']);
1674 class CustomApplication extends Application
1677 * Overwrites the default input definition.
1679 * @return InputDefinition An InputDefinition instance
1681 protected function getDefaultInputDefinition()
1683 return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')));
1687 * Gets the default helper set with the helpers that should always be available.
1689 * @return HelperSet A HelperSet instance
1691 protected function getDefaultHelperSet()
1693 return new HelperSet(array(new FormatterHelper()));
1697 class CustomDefaultCommandApplication extends Application
1700 * Overwrites the constructor in order to set a different default command.
1702 public function __construct()
1704 parent::__construct();
1706 $command = new \FooCommand();
1707 $this->add($command);
1708 $this->setDefaultCommand($command->getName());
1712 class LazyCommand extends Command
1714 public function execute(InputInterface $input, OutputInterface $output)
1716 $output->writeln('lazy-command called');
1720 class DisabledCommand extends Command
1722 public function isEnabled()