X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fsymfony%2Fconsole%2FTests%2FApplicationTest.php;fp=vendor%2Fsymfony%2Fconsole%2FTests%2FApplicationTest.php;h=38389b1b4b14cd1ce66d49ea1c2b6b08d84b8b74;hp=3f682465b329a317899ea4cb5ad98d6f8728e1cb;hb=af6d1fb995500ae68849458ee10d66abbdcfb252;hpb=680c79a86e3ed402f263faeac92e89fb6d9edcc0 diff --git a/vendor/symfony/console/Tests/ApplicationTest.php b/vendor/symfony/console/Tests/ApplicationTest.php index 3f682465b..38389b1b4 100644 --- a/vendor/symfony/console/Tests/ApplicationTest.php +++ b/vendor/symfony/console/Tests/ApplicationTest.php @@ -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; + } +}