Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / symfony / console / Tests / ApplicationTest.php
index 3f682465b329a317899ea4cb5ad98d6f8728e1cb..38389b1b4b14cd1ce66d49ea1c2b6b08d84b8b74 100644 (file)
@@ -13,6 +13,9 @@ namespace Symfony\Component\Console\Tests;
 
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
+use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
 use Symfony\Component\Console\Helper\HelperSet;
 use Symfony\Component\Console\Helper\FormatterHelper;
 use Symfony\Component\Console\Input\ArgvInput;
@@ -27,8 +30,11 @@ use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Output\StreamOutput;
 use Symfony\Component\Console\Tester\ApplicationTester;
 use Symfony\Component\Console\Event\ConsoleCommandEvent;
+use Symfony\Component\Console\Event\ConsoleErrorEvent;
 use Symfony\Component\Console\Event\ConsoleExceptionEvent;
 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
+use Symfony\Component\Console\Exception\CommandNotFoundException;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 
 class ApplicationTest extends TestCase
@@ -45,10 +51,14 @@ class ApplicationTest extends TestCase
         require_once self::$fixturesPath.'/Foo3Command.php';
         require_once self::$fixturesPath.'/Foo4Command.php';
         require_once self::$fixturesPath.'/Foo5Command.php';
+        require_once self::$fixturesPath.'/FooSameCaseUppercaseCommand.php';
+        require_once self::$fixturesPath.'/FooSameCaseLowercaseCommand.php';
         require_once self::$fixturesPath.'/FoobarCommand.php';
         require_once self::$fixturesPath.'/BarBucCommand.php';
         require_once self::$fixturesPath.'/FooSubnamespaced1Command.php';
         require_once self::$fixturesPath.'/FooSubnamespaced2Command.php';
+        require_once self::$fixturesPath.'/TestTiti.php';
+        require_once self::$fixturesPath.'/TestToto.php';
     }
 
     protected function normalizeLineBreaks($text)
@@ -113,6 +123,25 @@ class ApplicationTest extends TestCase
         $this->assertCount(1, $commands, '->all() takes a namespace as its first argument');
     }
 
+    public function testAllWithCommandLoader()
+    {
+        $application = new Application();
+        $commands = $application->all();
+        $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands');
+
+        $application->add(new \FooCommand());
+        $commands = $application->all('foo');
+        $this->assertCount(1, $commands, '->all() takes a namespace as its first argument');
+
+        $application->setCommandLoader(new FactoryCommandLoader(array(
+            'foo:bar1' => function () { return new \Foo1Command(); },
+        )));
+        $commands = $application->all('foo');
+        $this->assertCount(2, $commands, '->all() takes a namespace as its first argument');
+        $this->assertInstanceOf(\FooCommand::class, $commands['foo:bar'], '->all() returns the registered commands');
+        $this->assertInstanceOf(\Foo1Command::class, $commands['foo:bar1'], '->all() returns the registered commands');
+    }
+
     public function testRegister()
     {
         $application = new Application();
@@ -165,6 +194,30 @@ class ApplicationTest extends TestCase
         $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input');
     }
 
+    public function testHasGetWithCommandLoader()
+    {
+        $application = new Application();
+        $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered');
+        $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered');
+
+        $application->add($foo = new \FooCommand());
+        $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered');
+        $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name');
+        $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias');
+
+        $application->setCommandLoader(new FactoryCommandLoader(array(
+            'foo:bar1' => function () { return new \Foo1Command(); },
+        )));
+
+        $this->assertTrue($application->has('afoobar'), '->has() returns true if an instance is registered for an alias even with command loader');
+        $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns an instance by name even with command loader');
+        $this->assertEquals($foo, $application->get('afoobar'), '->get() returns an instance by alias even with command loader');
+        $this->assertTrue($application->has('foo:bar1'), '->has() returns true for commands registered in the loader');
+        $this->assertInstanceOf(\Foo1Command::class, $foo1 = $application->get('foo:bar1'), '->get() returns a command by name from the command loader');
+        $this->assertTrue($application->has('afoobar1'), '->has() returns true for commands registered in the loader');
+        $this->assertEquals($foo1, $application->get('afoobar1'), '->get() returns a command by name from the command loader');
+    }
+
     public function testSilentHelp()
     {
         $application = new Application();
@@ -213,19 +266,33 @@ class ApplicationTest extends TestCase
         $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces');
     }
 
-    /**
-     * @expectedException        \Symfony\Component\Console\Exception\CommandNotFoundException
-     * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1).
-     */
     public function testFindAmbiguousNamespace()
     {
         $application = new Application();
         $application->add(new \BarBucCommand());
         $application->add(new \FooCommand());
         $application->add(new \Foo2Command());
+
+        $expectedMsg = "The namespace \"f\" is ambiguous.\nDid you mean one of these?\n    foo\n    foo1";
+
+        if (method_exists($this, 'expectException')) {
+            $this->expectException(CommandNotFoundException::class);
+            $this->expectExceptionMessage($expectedMsg);
+        } else {
+            $this->setExpectedException(CommandNotFoundException::class, $expectedMsg);
+        }
+
         $application->findNamespace('f');
     }
 
+    public function testFindNonAmbiguous()
+    {
+        $application = new Application();
+        $application->add(new \TestTiti());
+        $application->add(new \TestToto());
+        $this->assertEquals('test-toto', $application->find('test')->getName());
+    }
+
     /**
      * @expectedException        \Symfony\Component\Console\Exception\CommandNotFoundException
      * @expectedExceptionMessage There are no commands defined in the "bar" namespace.
@@ -262,6 +329,55 @@ class ApplicationTest extends TestCase
         $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias');
     }
 
+    public function testFindCaseSensitiveFirst()
+    {
+        $application = new Application();
+        $application->add(new \FooSameCaseUppercaseCommand());
+        $application->add(new \FooSameCaseLowercaseCommand());
+
+        $this->assertInstanceOf('FooSameCaseUppercaseCommand', $application->find('f:B'), '->find() returns a command if the abbreviation is the correct case');
+        $this->assertInstanceOf('FooSameCaseUppercaseCommand', $application->find('f:BAR'), '->find() returns a command if the abbreviation is the correct case');
+        $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:b'), '->find() returns a command if the abbreviation is the correct case');
+        $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation is the correct case');
+    }
+
+    public function testFindCaseInsensitiveAsFallback()
+    {
+        $application = new Application();
+        $application->add(new \FooSameCaseLowercaseCommand());
+
+        $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:b'), '->find() returns a command if the abbreviation is the correct case');
+        $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:B'), '->find() will fallback to case insensitivity');
+        $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('FoO:BaR'), '->find() will fallback to case insensitivity');
+    }
+
+    /**
+     * @expectedException        \Symfony\Component\Console\Exception\CommandNotFoundException
+     * @expectedExceptionMessage Command "FoO:BaR" is ambiguous
+     */
+    public function testFindCaseInsensitiveSuggestions()
+    {
+        $application = new Application();
+        $application->add(new \FooSameCaseLowercaseCommand());
+        $application->add(new \FooSameCaseUppercaseCommand());
+
+        $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('FoO:BaR'), '->find() will find two suggestions with case insensitivity');
+    }
+
+    public function testFindWithCommandLoader()
+    {
+        $application = new Application();
+        $application->setCommandLoader(new FactoryCommandLoader(array(
+            'foo:bar' => $f = function () { return new \FooCommand(); },
+        )));
+
+        $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists');
+        $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists');
+        $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists');
+        $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist');
+        $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias');
+    }
+
     /**
      * @dataProvider provideAmbiguousAbbreviations
      */
@@ -286,8 +402,20 @@ class ApplicationTest extends TestCase
     {
         return array(
             array('f', 'Command "f" is not defined.'),
-            array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'),
-            array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'),
+            array(
+                'a',
+                "Command \"a\" is ambiguous.\nDid you mean one of these?\n".
+                "    afoobar  The foo:bar command\n".
+                "    afoobar1 The foo:bar1 command\n".
+                '    afoobar2 The foo1:bar command',
+            ),
+            array(
+                'foo:b',
+                "Command \"foo:b\" is ambiguous.\nDid you mean one of these?\n".
+                "    foo:bar  The foo:bar command\n".
+                "    foo:bar1 The foo:bar1 command\n".
+                '    foo1:bar The foo1:bar command',
+            ),
         );
     }
 
@@ -333,8 +461,8 @@ class ApplicationTest extends TestCase
     public function provideInvalidCommandNamesSingle()
     {
         return array(
-            array('foo3:baR'),
-            array('foO3:bar'),
+            array('foo3:barr'),
+            array('fooo3:bar'),
         );
     }
 
@@ -459,6 +587,36 @@ class ApplicationTest extends TestCase
         }
     }
 
+    public function testFindAlternativesOutput()
+    {
+        $application = new Application();
+
+        $application->add(new \FooCommand());
+        $application->add(new \Foo1Command());
+        $application->add(new \Foo2Command());
+        $application->add(new \Foo3Command());
+
+        $expectedAlternatives = array(
+            'afoobar',
+            'afoobar1',
+            'afoobar2',
+            'foo1:bar',
+            'foo3:bar',
+            'foo:bar',
+            'foo:bar1',
+        );
+
+        try {
+            $application->find('foo');
+            $this->fail('->find() throws a CommandNotFoundException if command is not defined');
+        } catch (\Exception $e) {
+            $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command is not defined');
+            $this->assertSame($expectedAlternatives, $e->getAlternatives());
+
+            $this->assertRegExp('/Command "foo" is not defined\..*Did you mean one of these\?.*/Ums', $e->getMessage());
+        }
+    }
+
     public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces()
     {
         $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('getNamespaces'))->getMock();
@@ -570,10 +728,10 @@ class ApplicationTest extends TestCase
         $tester = new ApplicationTester($application);
 
         $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
-        $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
+        $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
 
         $tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true));
-        $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
+        $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions');
 
         $application = new Application();
         $application->setAutoExit(false);
@@ -583,7 +741,7 @@ class ApplicationTest extends TestCase
         });
         $tester = new ApplicationTester($application);
         $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true));
-        $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
+        $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal');
         putenv('COLUMNS=120');
     }
 
@@ -598,10 +756,26 @@ class ApplicationTest extends TestCase
         $tester = new ApplicationTester($application);
 
         $tester->run(array('command' => 'foo'), array('decorated' => false));
-        $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_escapeslines.txt', $tester->getDisplay(true), '->renderException() escapes lines containing formatting');
+        $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_escapeslines.txt', $tester->getDisplay(true), '->renderException() escapes lines containing formatting');
         putenv('COLUMNS=120');
     }
 
+    public function testRenderExceptionLineBreaks()
+    {
+        $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('getTerminalWidth'))->getMock();
+        $application->setAutoExit(false);
+        $application->expects($this->any())
+            ->method('getTerminalWidth')
+            ->will($this->returnValue(120));
+        $application->register('foo')->setCode(function () {
+            throw new \InvalidArgumentException("\n\nline 1 with extra spaces        \nline 2\n\nline 4\n");
+        });
+        $tester = new ApplicationTester($application);
+
+        $tester->run(array('command' => 'foo'), array('decorated' => false));
+        $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_linebreaks.txt', $tester->getDisplay(true), '->renderException() keep multiple line breaks');
+    }
+
     public function testRun()
     {
         $application = new Application();
@@ -936,7 +1110,7 @@ class ApplicationTest extends TestCase
 
     /**
      * @expectedException        \LogicException
-     * @expectedExceptionMessage caught
+     * @expectedExceptionMessage error
      */
     public function testRunWithExceptionAndDispatcher()
     {
@@ -967,7 +1141,7 @@ class ApplicationTest extends TestCase
 
         $tester = new ApplicationTester($application);
         $tester->run(array('command' => 'foo'));
-        $this->assertContains('before.foo.caught.after.', $tester->getDisplay());
+        $this->assertContains('before.foo.error.after.', $tester->getDisplay());
     }
 
     public function testRunDispatchesAllEventsWithExceptionInListener()
@@ -987,9 +1161,12 @@ class ApplicationTest extends TestCase
 
         $tester = new ApplicationTester($application);
         $tester->run(array('command' => 'foo'));
-        $this->assertContains('before.caught.after.', $tester->getDisplay());
+        $this->assertContains('before.error.after.', $tester->getDisplay());
     }
 
+    /**
+     * @requires PHP 7
+     */
     public function testRunWithError()
     {
         $application = new Application();
@@ -1012,9 +1189,107 @@ class ApplicationTest extends TestCase
         }
     }
 
+    public function testRunAllowsErrorListenersToSilenceTheException()
+    {
+        $dispatcher = $this->getDispatcher();
+        $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
+            $event->getOutput()->write('silenced.');
+
+            $event->setExitCode(0);
+        });
+
+        $dispatcher->addListener('console.command', function () {
+            throw new \RuntimeException('foo');
+        });
+
+        $application = new Application();
+        $application->setDispatcher($dispatcher);
+        $application->setAutoExit(false);
+
+        $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
+            $output->write('foo.');
+        });
+
+        $tester = new ApplicationTester($application);
+        $tester->run(array('command' => 'foo'));
+        $this->assertContains('before.error.silenced.after.', $tester->getDisplay());
+        $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $tester->getStatusCode());
+    }
+
+    public function testConsoleErrorEventIsTriggeredOnCommandNotFound()
+    {
+        $dispatcher = new EventDispatcher();
+        $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
+            $this->assertNull($event->getCommand());
+            $this->assertInstanceOf(CommandNotFoundException::class, $event->getError());
+            $event->getOutput()->write('silenced command not found');
+        });
+
+        $application = new Application();
+        $application->setDispatcher($dispatcher);
+        $application->setAutoExit(false);
+
+        $tester = new ApplicationTester($application);
+        $tester->run(array('command' => 'unknown'));
+        $this->assertContains('silenced command not found', $tester->getDisplay());
+        $this->assertEquals(1, $tester->getStatusCode());
+    }
+
+    /**
+     * @group legacy
+     * @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.
+     */
+    public function testLegacyExceptionListenersAreStillTriggered()
+    {
+        $dispatcher = $this->getDispatcher();
+        $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) {
+            $event->getOutput()->write('caught.');
+
+            $event->setException(new \RuntimeException('replaced in caught.'));
+        });
+
+        $application = new Application();
+        $application->setDispatcher($dispatcher);
+        $application->setAutoExit(false);
+
+        $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
+            throw new \RuntimeException('foo');
+        });
+
+        $tester = new ApplicationTester($application);
+        $tester->run(array('command' => 'foo'));
+        $this->assertContains('before.caught.error.after.', $tester->getDisplay());
+        $this->assertContains('replaced in caught.', $tester->getDisplay());
+    }
+
+    /**
+     * @requires PHP 7
+     */
+    public function testErrorIsRethrownIfNotHandledByConsoleErrorEvent()
+    {
+        $application = new Application();
+        $application->setAutoExit(false);
+        $application->setCatchExceptions(false);
+        $application->setDispatcher(new EventDispatcher());
+
+        $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) {
+            new \UnknownClass();
+        });
+
+        $tester = new ApplicationTester($application);
+
+        try {
+            $tester->run(array('command' => 'dym'));
+            $this->fail('->run() should rethrow PHP errors if not handled via ConsoleErrorEvent.');
+        } catch (\Error $e) {
+            $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found');
+        }
+    }
+
     /**
+     * @requires PHP 7
      * @expectedException        \LogicException
-     * @expectedExceptionMessage caught
+     * @expectedExceptionMessage error
      */
     public function testRunWithErrorAndDispatcher()
     {
@@ -1031,9 +1306,12 @@ class ApplicationTest extends TestCase
 
         $tester = new ApplicationTester($application);
         $tester->run(array('command' => 'dym'));
-        $this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
+        $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
     }
 
+    /**
+     * @requires PHP 7
+     */
     public function testRunDispatchesAllEventsWithError()
     {
         $application = new Application();
@@ -1048,9 +1326,12 @@ class ApplicationTest extends TestCase
 
         $tester = new ApplicationTester($application);
         $tester->run(array('command' => 'dym'));
-        $this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
+        $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
     }
 
+    /**
+     * @requires PHP 7
+     */
     public function testRunWithErrorFailingStatusCode()
     {
         $application = new Application();
@@ -1231,6 +1512,60 @@ class ApplicationTest extends TestCase
         $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream));
     }
 
+    public function testRunLazyCommandService()
+    {
+        $container = new ContainerBuilder();
+        $container->addCompilerPass(new AddConsoleCommandPass());
+        $container
+            ->register('lazy-command', LazyCommand::class)
+            ->addTag('console.command', array('command' => 'lazy:command'))
+            ->addTag('console.command', array('command' => 'lazy:alias'))
+            ->addTag('console.command', array('command' => 'lazy:alias2'));
+        $container->compile();
+
+        $application = new Application();
+        $application->setCommandLoader($container->get('console.command_loader'));
+        $application->setAutoExit(false);
+
+        $tester = new ApplicationTester($application);
+
+        $tester->run(array('command' => 'lazy:command'));
+        $this->assertSame("lazy-command called\n", $tester->getDisplay(true));
+
+        $tester->run(array('command' => 'lazy:alias'));
+        $this->assertSame("lazy-command called\n", $tester->getDisplay(true));
+
+        $tester->run(array('command' => 'lazy:alias2'));
+        $this->assertSame("lazy-command called\n", $tester->getDisplay(true));
+
+        $command = $application->get('lazy:command');
+        $this->assertSame(array('lazy:alias', 'lazy:alias2'), $command->getAliases());
+    }
+
+    /**
+     * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
+     */
+    public function testGetDisabledLazyCommand()
+    {
+        $application = new Application();
+        $application->setCommandLoader(new FactoryCommandLoader(array('disabled' => function () { return new DisabledCommand(); })));
+        $application->get('disabled');
+    }
+
+    public function testHasReturnsFalseForDisabledLazyCommand()
+    {
+        $application = new Application();
+        $application->setCommandLoader(new FactoryCommandLoader(array('disabled' => function () { return new DisabledCommand(); })));
+        $this->assertFalse($application->has('disabled'));
+    }
+
+    public function testAllExcludesDisabledLazyCommand()
+    {
+        $application = new Application();
+        $application->setCommandLoader(new FactoryCommandLoader(array('disabled' => function () { return new DisabledCommand(); })));
+        $this->assertArrayNotHasKey('disabled', $application->all());
+    }
+
     protected function getDispatcher($skipCommand = false)
     {
         $dispatcher = new EventDispatcher();
@@ -1248,10 +1583,10 @@ class ApplicationTest extends TestCase
                 $event->setExitCode(ConsoleCommandEvent::RETURN_CODE_DISABLED);
             }
         });
-        $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) {
-            $event->getOutput()->write('caught.');
+        $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
+            $event->getOutput()->write('error.');
 
-            $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException()));
+            $event->setError(new \LogicException('error.', $event->getExitCode(), $event->getError()));
         });
 
         return $dispatcher;
@@ -1279,6 +1614,13 @@ class ApplicationTest extends TestCase
             $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found');
         }
     }
+
+    protected function tearDown()
+    {
+        putenv('SHELL_VERBOSITY');
+        unset($_ENV['SHELL_VERBOSITY']);
+        unset($_SERVER['SHELL_VERBOSITY']);
+    }
 }
 
 class CustomApplication extends Application
@@ -1318,3 +1660,19 @@ class CustomDefaultCommandApplication extends Application
         $this->setDefaultCommand($command->getName());
     }
 }
+
+class LazyCommand extends Command
+{
+    public function execute(InputInterface $input, OutputInterface $output)
+    {
+        $output->writeln('lazy-command called');
+    }
+}
+
+class DisabledCommand extends Command
+{
+    public function isEnabled()
+    {
+        return false;
+    }
+}