2 namespace Consolidation\AnnotatedCommand;
4 use Consolidation\AnnotatedCommand\AnnotationData;
5 use Consolidation\AnnotatedCommand\CommandData;
6 use Consolidation\AnnotatedCommand\Hooks\HookManager;
7 use Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent;
8 use Consolidation\AnnotatedCommand\Parser\CommandInfo;
9 use Consolidation\TestUtils\ExampleCommandInfoAlterer;
10 use Symfony\Component\Console\Application;
11 use Symfony\Component\Console\Command\Command;
12 use Symfony\Component\Console\Input\InputInterface;
13 use Symfony\Component\Console\Input\ArgvInput;
14 use Symfony\Component\Console\Input\StringInput;
15 use Symfony\Component\Console\Output\BufferedOutput;
16 use Symfony\Component\Console\Output\OutputInterface;
18 class AnnotatedCommandFactoryTests extends \PHPUnit_Framework_TestCase
20 protected $commandFileInstance;
21 protected $commandFactory;
23 function testOptionDefaultValue()
25 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
26 $this->commandFactory = new AnnotatedCommandFactory();
27 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaultOptionOne');
29 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
30 $this->assertEquals('default:option-one', $command->getName());
31 $this->assertEquals('default:option-one [--foo [FOO]]', $command->getSynopsis());
33 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
35 $input = new StringInput('default:option-one');
36 $this->assertRunCommandViaApplicationEquals($command, $input, 'Foo is 1');
38 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaultOptionTwo');
40 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
42 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
43 $this->assertEquals('default:option-two', $command->getName());
44 $this->assertEquals('default:option-two [--foo [FOO]]', $command->getSynopsis());
46 $input = new StringInput('default:option-two');
47 $this->assertRunCommandViaApplicationEquals($command, $input, 'Foo is 2');
49 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaultOptionNone');
51 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
53 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
54 $this->assertEquals('default:option-none', $command->getName());
55 $this->assertEquals('default:option-none [--foo FOO]', $command->getSynopsis());
57 // Skip failing test until Symfony is fixed.
58 $this->markTestSkipped('Symfony Console 3.2.5 and 3.2.6 do not handle default options with required values correctly.');
60 $input = new StringInput('default:option-none --foo');
61 $this->assertRunCommandViaApplicationContains($command, $input, ['The "--foo" option requires a value.'], 1);
65 * Test CommandInfo command caching.
68 * - Create all of the command info objects from one class, caching them.
69 * - Change the method name of one of the items in the cache to a non-existent method
70 * - Restore all of the cached commandinfo objects
71 * - Ensure that the non-existent method cached commandinfo was not created
72 * - Ensure that the now-missing cached commandinfo was still created
74 * This tests both save/restore, plus adding a new command method to
75 * a class, and removing a command method from a class.
77 function testAnnotatedCommandCache()
79 $testCacheStore = new \Consolidation\TestUtils\InMemoryCacheStore();
81 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
82 $this->commandFactory = new AnnotatedCommandFactory();
83 $this->commandFactory->setDataStore($testCacheStore);
85 // Make commandInfo objects for every command in the test commandfile.
86 // These will also be stored in our cache.
87 $commandInfoList = $this->commandFactory->getCommandInfoListFromClass($this->commandFileInstance);
89 $cachedClassName = get_class($this->commandFileInstance);
91 $this->assertTrue($testCacheStore->has($cachedClassName));
93 $cachedData = $testCacheStore->get($cachedClassName);
94 $this->assertFalse(empty($cachedData));
95 $this->assertTrue(array_key_exists('testArithmatic', $cachedData));
97 $alterCommandInfoCache = $cachedData['testArithmatic'];
98 unset($cachedData['testArithmatic']);
99 $alterCommandInfoCache['method_name'] = 'nonExistentMethod';
100 $cachedData[$alterCommandInfoCache['method_name']] = $alterCommandInfoCache;
102 $testCacheStore->set($cachedClassName, $cachedData);
104 $restoredCommandInfoList = $this->commandFactory->getCommandInfoListFromClass($this->commandFileInstance);
106 $rebuiltCachedData = $testCacheStore->get($cachedClassName);
108 $this->assertFalse(empty($rebuiltCachedData));
109 $this->assertTrue(array_key_exists('testArithmatic', $rebuiltCachedData));
110 $this->assertFalse(array_key_exists('nonExistentMethod', $rebuiltCachedData));
114 * Test CommandInfo command annotation parsing.
116 function testAnnotatedCommandCreation()
118 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
119 $this->commandFactory = new AnnotatedCommandFactory();
120 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testArithmatic');
122 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
124 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
125 $this->assertEquals('test:arithmatic', $command->getName());
126 $this->assertEquals('This is the test:arithmatic command', $command->getDescription());
127 $this->assertEquals("This command will add one and two. If the --negate flag\nis provided, then the result is negated.", $command->getHelp());
128 $this->assertEquals('arithmatic', implode(',', $command->getAliases()));
129 $this->assertEquals('test:arithmatic [--negate] [--unused [UNUSED]] [--] <one> [<two>]', $command->getSynopsis());
130 $this->assertEquals('test:arithmatic 2 2 --negate', implode(',', $command->getUsages()));
132 $input = new StringInput('arithmatic 2 3 --negate');
133 $this->assertRunCommandViaApplicationEquals($command, $input, '-5');
137 * Test CommandInfo command annotation altering.
139 function testAnnotatedCommandInfoAlteration()
141 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
142 $this->commandFactory = new AnnotatedCommandFactory();
143 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myCat');
145 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
147 $annotationData = $command->getAnnotationData();
148 $this->assertTrue($annotationData->has('arbitrary'));
149 $this->assertFalse($annotationData->has('dynamic'));
151 $this->commandFactory->addCommandInfoAlterer(new ExampleCommandInfoAlterer());
153 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
155 $annotationData = $command->getAnnotationData();
156 $this->assertTrue($annotationData->has('arbitrary'));
157 $this->assertTrue($annotationData->has('dynamic'));
160 function testMyCatCommand()
162 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
163 $this->commandFactory = new AnnotatedCommandFactory();
164 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myCat');
166 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
168 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
169 $this->assertEquals('my:cat', $command->getName());
170 $this->assertEquals('This is the my:cat command', $command->getDescription());
171 $this->assertEquals("This command will concatenate two parameters. If the --flip flag\nis provided, then the result is the concatenation of two and one.", $command->getHelp());
172 $this->assertEquals('c', implode(',', $command->getAliases()));
173 $this->assertEquals('my:cat [--flip] [--] <one> [<two>]', $command->getSynopsis());
174 $this->assertEquals('my:cat bet alpha --flip', implode(',', $command->getUsages()));
176 $input = new StringInput('my:cat bet alpha --flip');
177 $this->assertRunCommandViaApplicationEquals($command, $input, 'alphabet');
180 function testJoinCommandHelp()
182 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
183 $this->commandFactory = new AnnotatedCommandFactory();
184 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myJoin');
186 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
188 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
189 $this->assertEquals('my:join', $command->getName());
190 $this->assertEquals('This is the my:join command', $command->getDescription());
191 $this->assertEquals("This command will join its parameters together. It can also reverse and repeat its arguments.", $command->getHelp());
192 $this->assertEquals('my:join [--flip] [--repeat [REPEAT]] [--] [<args>]...', $command->getSynopsis());
194 // Bug in parser: @usage with no parameters or options not passed to us correctly.
195 $actualUsages = implode(',', $command->getUsages());
196 if ($actualUsages == 'my:join a b,my:join Example with no parameters or options') {
197 $this->markTestSkipped();
199 $this->assertEquals('my:join a b,my:join', $actualUsages);
201 $input = new StringInput('my:join bet alpha --flip --repeat=2');
202 $this->assertRunCommandViaApplicationEquals($command, $input, 'alphabetalphabet');
205 function testDefaultsCommand()
207 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
208 $this->commandFactory = new AnnotatedCommandFactory();
209 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaults');
211 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
213 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
214 $this->assertEquals('defaults', $command->getName());
215 $this->assertEquals('Test default values in arguments', $command->getDescription());
217 $input = new StringInput('defaults');
218 $this->assertRunCommandViaApplicationEquals($command, $input, 'nothing provided');
220 $input = new StringInput('defaults ichi');
221 $this->assertRunCommandViaApplicationEquals($command, $input, 'only ichi');
223 $input = new StringInput('defaults I II');
224 $this->assertRunCommandViaApplicationEquals($command, $input, 'I and II');
227 function testCommandWithNoOptions()
229 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
230 $this->commandFactory = new AnnotatedCommandFactory();
231 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'commandWithNoOptions');
233 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
235 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
236 $this->assertEquals('command:with-no-options', $command->getName());
237 $this->assertEquals('This is a command with no options', $command->getDescription());
238 $this->assertEquals("This command will concatenate two parameters.", $command->getHelp());
239 $this->assertEquals('nope', implode(',', $command->getAliases()));
240 $this->assertEquals('command:with-no-options <one> [<two>]', $command->getSynopsis());
241 $this->assertEquals('command:with-no-options alpha bet', implode(',', $command->getUsages()));
243 $input = new StringInput('command:with-no-options something');
244 $this->assertRunCommandViaApplicationEquals($command, $input, 'somethingdefault');
246 $input = new StringInput('help command:with-no-options something');
247 $this->assertRunCommandViaApplicationContains(
251 'The first parameter.',
252 'The other parameter.',
257 function testCommandWithNoArguments()
259 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
260 $this->commandFactory = new AnnotatedCommandFactory();
261 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'commandWithNoArguments');
263 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
265 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
266 $this->assertEquals('command:with-no-arguments', $command->getName());
267 $this->assertEquals('This command has no arguments--only options', $command->getDescription());
268 $this->assertEquals("Return a result only if not silent.", $command->getHelp());
269 $this->assertEquals('command:with-no-arguments [-s|--silent]', $command->getSynopsis());
271 $input = new StringInput('command:with-no-arguments');
272 $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, world');
273 $input = new StringInput('command:with-no-arguments -s');
274 $this->assertRunCommandViaApplicationEquals($command, $input, '');
275 $input = new StringInput('command:with-no-arguments --silent');
276 $this->assertRunCommandViaApplicationEquals($command, $input, '');
279 function testCommandWithShortcutOnAnnotation()
281 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
282 $this->commandFactory = new AnnotatedCommandFactory();
283 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'shortcutOnAnnotation');
285 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
287 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
288 $this->assertEquals('shortcut:on-annotation', $command->getName());
289 $this->assertEquals('Shortcut on annotation', $command->getDescription());
290 $this->assertEquals("This command defines the option shortcut on the annotation instead of in the options array.", $command->getHelp());
291 $this->assertEquals('shortcut:on-annotation [-s|--silent]', $command->getSynopsis());
293 $input = new StringInput('shortcut:on-annotation');
294 $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, world');
295 $input = new StringInput('shortcut:on-annotation -s');
296 $this->assertRunCommandViaApplicationEquals($command, $input, '');
297 $input = new StringInput('shortcut:on-annotation --silent');
298 $this->assertRunCommandViaApplicationEquals($command, $input, '');
303 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile('secret secret');
304 $this->commandFactory = new AnnotatedCommandFactory();
305 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testState');
307 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
309 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
310 $this->assertEquals('test:state', $command->getName());
312 $input = new StringInput('test:state');
313 $this->assertRunCommandViaApplicationEquals($command, $input, 'secret secret');
316 function testPassthroughArray()
318 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
319 $this->commandFactory = new AnnotatedCommandFactory();
320 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testPassthrough');
322 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
324 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
325 $this->assertEquals('test:passthrough', $command->getName());
327 $input = new StringInput('test:passthrough a b c -- x y z');
328 $this->assertRunCommandViaApplicationEquals($command, $input, 'a,b,c,x,y,z');
331 function testPassThroughNonArray()
333 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
334 $this->commandFactory = new AnnotatedCommandFactory();
335 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myJoin');
337 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
339 $input = new StringInput('my:join bet --flip -- x y z');
340 $this->assertRunCommandViaApplicationEquals($command, $input, 'zyxbet');
341 // Can't look at 'hasOption' until after the command initializes the
342 // option, because Symfony.
343 $this->assertTrue($input->hasOption('flip'));
346 function testPassThroughWithInputManipulation()
348 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
349 $this->commandFactory = new AnnotatedCommandFactory();
350 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myJoin');
352 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
354 $input = new StringInput('my:join bet --repeat=2 -- x y z');
355 $this->assertRunCommandViaApplicationEquals($command, $input, 'betxyzbetxyz');
356 // Symfony does not allow us to manipulate the options via setOption until
357 // the definition from the command object has been set up.
358 $input->setOption('repeat', 3);
359 $this->assertEquals(3, $input->getOption('repeat'));
360 $input->setArgument(0, 'q');
361 // Manipulating $input does not work -- the changes are not effective.
362 // The end result here should be 'qx y yqx y yqx y y'
363 $this->assertRunCommandViaApplicationEquals($command, $input, 'betxyzbetxyz');
366 function testRequiredArrayOption()
368 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
369 $this->commandFactory = new AnnotatedCommandFactory();
370 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testRequiredArrayOption');
372 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
373 $this->assertEquals('test:required-array-option [-a|--arr ARR]', $command->getSynopsis());
375 $input = new StringInput('test:required-array-option --arr=1 --arr=2 --arr=3');
376 $this->assertRunCommandViaApplicationEquals($command, $input, '1 2 3');
378 $input = new StringInput('test:required-array-option -a 1 -a 2 -a 3');
379 $this->assertRunCommandViaApplicationEquals($command, $input, '1 2 3');
382 function testArrayOption()
384 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
385 $this->commandFactory = new AnnotatedCommandFactory();
386 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testArrayOption');
388 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
389 $this->assertEquals('test:array-option [-a|--arr [ARR]]', $command->getSynopsis());
391 $input = new StringInput('test:array-option');
392 $this->assertRunCommandViaApplicationEquals($command, $input, '1 2 3');
394 $input = new StringInput('test:array-option --arr=a --arr=b --arr=c');
395 $this->assertRunCommandViaApplicationEquals($command, $input, 'a b c');
397 $input = new StringInput('test:array-option -a a');
398 $this->assertRunCommandViaApplicationEquals($command, $input, 'a');
401 function testHookedCommand()
403 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
404 $this->commandFactory = new AnnotatedCommandFactory();
406 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestHook');
408 $this->assertTrue($hookInfo->hasAnnotation('hook'));
409 $this->assertEquals('alter test:hook', $hookInfo->getAnnotation('hook'));
411 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
413 $hookCallback = $this->commandFactory->hookManager()->get('test:hook', [HookManager::ALTER_RESULT]);
414 $this->assertTrue($hookCallback != null);
415 $this->assertEquals(1, count($hookCallback));
416 $this->assertEquals(2, count($hookCallback[0]));
417 $this->assertTrue(is_callable($hookCallback[0]));
418 $this->assertEquals('hookTestHook', $hookCallback[0][1]);
420 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHook');
421 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
423 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
424 $this->assertEquals('test:hook', $command->getName());
426 $input = new StringInput('test:hook bar');
427 $this->assertRunCommandViaApplicationEquals($command, $input, '<[bar]>');
429 $input = new StringInput('list --raw');
430 $this->assertRunCommandViaApplicationContains($command, $input, ['This command wraps its parameter in []; its alter hook then wraps the result in .']);
433 function testReplaceCommandHook(){
434 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
435 $this->commandFactory = new AnnotatedCommandFactory();
437 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestReplaceCommandHook');
439 $this->assertTrue($hookInfo->hasAnnotation('hook'));
440 $this->assertEquals('replace-command test:replace-command', $hookInfo->getAnnotation('hook'));
442 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
444 $hookCallback = $this->commandFactory->hookManager()->get('test:replace-command', [HookManager::REPLACE_COMMAND_HOOK]);
445 $this->assertTrue($hookCallback != null);
446 $this->assertEquals(1, count($hookCallback));
447 $this->assertEquals(2, count($hookCallback[0]));
448 $this->assertTrue(is_callable($hookCallback[0]));
449 $this->assertEquals('hookTestReplaceCommandHook', $hookCallback[0][1]);
451 $input = new StringInput('test:replace-command foo');
452 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testReplaceCommand');
453 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
454 $this->assertRunCommandViaApplicationEquals($command, $input, "bar", 0);
457 function testPostCommandCalledAfterCommand()
459 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
460 $this->commandFactory = new AnnotatedCommandFactory();
462 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestPostCommandHook');
464 $this->assertTrue($hookInfo->hasAnnotation('hook'));
465 $this->assertEquals('post-command test:post-command', $hookInfo->getAnnotation('hook'));
467 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
469 $hookCallback = $this->commandFactory->hookManager()->get('test:post-command', [HookManager::POST_COMMAND_HOOK]);
470 $this->assertTrue($hookCallback != null);
471 $this->assertEquals(1, count($hookCallback));
472 $this->assertEquals(2, count($hookCallback[0]));
473 $this->assertTrue(is_callable($hookCallback[0]));
474 $this->assertEquals('hookTestPostCommandHook', $hookCallback[0][1]);
476 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestPreCommandHook');
478 $this->assertTrue($hookInfo->hasAnnotation('hook'));
479 $this->assertEquals('pre-command test:post-command', $hookInfo->getAnnotation('hook'));
481 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
483 $hookCallback = $this->commandFactory->hookManager()->get('test:post-command', [HookManager::PRE_COMMAND_HOOK]);
484 $this->assertTrue($hookCallback != null);
485 $this->assertEquals(1, count($hookCallback));
486 $this->assertEquals(2, count($hookCallback[0]));
487 $this->assertTrue(is_callable($hookCallback[0]));
488 $this->assertEquals('hookTestPreCommandHook', $hookCallback[0][1]);
490 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testPostCommand');
491 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
493 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
494 $this->assertEquals('test:post-command', $command->getName());
496 $input = new StringInput('test:post-command bar');
497 $this->assertRunCommandViaApplicationEquals($command, $input, "foo\nbar\nbaz", 0, $this->commandFileInstance);
500 function testHookAllCommands()
502 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleHookAllCommandFile();
503 $this->commandFactory = new AnnotatedCommandFactory();
505 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'alterAllCommands');
507 $this->assertTrue($hookInfo->hasAnnotation('hook'));
508 $this->assertEquals('alter', $hookInfo->getAnnotation('hook'));
510 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
512 $hookCallback = $this->commandFactory->hookManager()->get('Consolidation\TestUtils\ExampleHookAllCommandFile', [HookManager::ALTER_RESULT]);
513 $this->assertTrue($hookCallback != null);
514 $this->assertEquals(1, count($hookCallback));
515 $this->assertEquals(2, count($hookCallback[0]));
516 $this->assertTrue(is_callable($hookCallback[0]));
517 $this->assertEquals('alterAllCommands', $hookCallback[0][1]);
519 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'doCat');
520 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
522 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
523 $this->assertEquals('do:cat', $command->getName());
525 $input = new StringInput('do:cat bar');
526 $this->assertRunCommandViaApplicationEquals($command, $input, '*** bar ***');
529 function testDoubleDashWithVersion()
531 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleHookAllCommandFile();
532 $this->commandFactory = new AnnotatedCommandFactory();
533 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'doCat');
534 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
536 $input = new ArgvInput(['placeholder', 'do:cat', 'one', '--', '--version']);
537 list($statusCode, $commandOutput) = $this->runCommandViaApplication($command, $input);
539 if ($commandOutput == 'TestApplication version 0.0.0') {
540 $this->markTestSkipped('Symfony/Console 2.x does not respect -- with --version');
542 $this->assertEquals('one--version', $commandOutput);
545 function testAnnotatedHookedCommand()
547 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
548 $this->commandFactory = new AnnotatedCommandFactory();
550 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestAnnotatedHook');
552 $this->assertTrue($hookInfo->hasAnnotation('hook'));
553 $this->assertEquals('alter @hookme', $hookInfo->getAnnotation('hook'));
555 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
556 $hookCallback = $this->commandFactory->hookManager()->get('@hookme', [HookManager::ALTER_RESULT]);
557 $this->assertTrue($hookCallback != null);
558 $this->assertEquals(1, count($hookCallback));
559 $this->assertEquals(2, count($hookCallback[0]));
560 $this->assertTrue(is_callable($hookCallback[0]));
561 $this->assertEquals('hookTestAnnotatedHook', $hookCallback[0][1]);
563 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testAnnotationHook');
564 $annotationData = $commandInfo->getRawAnnotations();
565 $this->assertEquals('hookme,before,after', implode(',', $annotationData->keys()));
566 $this->assertEquals('@hookme,@before,@after', implode(',', array_map(function ($item) { return "@$item"; }, $annotationData->keys())));
568 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
570 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
571 $this->assertEquals('test:annotation-hook', $command->getName());
573 $input = new StringInput('test:annotation-hook baz');
574 $this->assertRunCommandViaApplicationEquals($command, $input, '>(baz)<');
577 function testHookHasCommandAnnotation()
579 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
580 $this->commandFactory = new AnnotatedCommandFactory();
582 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookAddCommandName');
584 $this->assertTrue($hookInfo->hasAnnotation('hook'));
585 $this->assertEquals('alter @addmycommandname', $hookInfo->getAnnotation('hook'));
587 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
588 $hookCallback = $this->commandFactory->hookManager()->get('@addmycommandname', [HookManager::ALTER_RESULT]);
589 $this->assertTrue($hookCallback != null);
590 $this->assertEquals(1, count($hookCallback));
591 $this->assertEquals(2, count($hookCallback[0]));
592 $this->assertTrue(is_callable($hookCallback[0]));
593 $this->assertEquals('hookAddCommandName', $hookCallback[0][1]);
595 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'alterMe');
596 $annotationData = $commandInfo->getRawAnnotations();
597 $this->assertEquals('command,addmycommandname', implode(',', $annotationData->keys()));
599 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
601 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
602 $this->assertEquals('alter-me', $command->getName());
604 $input = new StringInput('alter-me');
605 $this->assertRunCommandViaApplicationEquals($command, $input, 'splendiferous from alter-me');
607 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'alterMeToo');
608 $annotationData = $commandInfo->getRawAnnotations();
609 $this->assertEquals('addmycommandname', implode(',', $annotationData->keys()));
610 $annotationData = $commandInfo->getAnnotations();
611 $this->assertEquals('addmycommandname,command,_path,_classname', implode(',', $annotationData->keys()));
613 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
615 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
616 $this->assertEquals('alter:me-too', $command->getName());
618 $input = new StringInput('alter:me-too');
619 $this->assertRunCommandViaApplicationEquals($command, $input, 'fantabulous from alter:me-too');
622 function testHookedCommandWithHookAddedLater()
624 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
625 $this->commandFactory = new AnnotatedCommandFactory();
626 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHook');
628 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
630 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
631 $this->assertEquals('test:hook', $command->getName());
633 // Run the command once without the hook
634 $input = new StringInput('test:hook foo');
635 $this->assertRunCommandViaApplicationEquals($command, $input, '[foo]');
637 // Register the hook and run the command again
638 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestHook');
640 $this->assertTrue($hookInfo->hasAnnotation('hook'));
641 $this->assertEquals('alter test:hook', $hookInfo->getAnnotation('hook'));
643 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
644 $hookCallback = $this->commandFactory->hookManager()->get('test:hook', [HookManager::ALTER_RESULT]);;
645 $this->assertTrue($hookCallback != null);
646 $this->assertEquals(1, count($hookCallback));
647 $this->assertEquals(2, count($hookCallback[0]));
648 $this->assertTrue(is_callable($hookCallback[0]));
649 $this->assertEquals('hookTestHook', $hookCallback[0][1]);
651 $input = new StringInput('test:hook bar');
652 $this->assertRunCommandViaApplicationEquals($command, $input, '<[bar]>');
655 function testInitializeHook()
657 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
658 $this->commandFactory = new AnnotatedCommandFactory();
660 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'initializeTestHello');
662 $this->assertTrue($hookInfo->hasAnnotation('hook'));
663 $this->assertEquals($hookInfo->getAnnotation('hook'), 'init test:hello');
665 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
667 $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::INITIALIZE]);
668 $this->assertTrue($hookCallback != null);
669 $this->assertEquals(1, count($hookCallback));
670 $this->assertEquals(2, count($hookCallback[0]));
671 $this->assertTrue(is_callable($hookCallback[0]));
672 $this->assertEquals('initializeTestHello', $hookCallback[0][1]);
674 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHello');
675 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
677 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
678 $this->assertEquals('test:hello', $command->getName());
679 $commandGetNames = $this->callProtected($command, 'getNames');
680 $this->assertEquals('test:hello,Consolidation\TestUtils\ExampleCommandFile', implode(',', $commandGetNames));
682 $hookCallback = $command->commandProcessor()->hookManager()->get('test:hello', 'init');
683 $this->assertTrue($hookCallback != null);
684 $this->assertEquals('initializeTestHello', $hookCallback[0][1]);
686 $input = new StringInput('test:hello');
687 $this->assertRunCommandViaApplicationEquals($command, $input, "Hello, Huey.");
690 function testCommandEventHook()
692 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
693 $this->commandFactory = new AnnotatedCommandFactory();
695 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'commandEventTestHello');
697 $this->assertTrue($hookInfo->hasAnnotation('hook'));
698 $this->assertEquals($hookInfo->getAnnotation('hook'), 'command-event test:hello');
700 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
702 $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::COMMAND_EVENT]);
703 $this->assertTrue($hookCallback != null);
704 $this->assertEquals(1, count($hookCallback));
705 $this->assertEquals(2, count($hookCallback[0]));
706 $this->assertTrue(is_callable($hookCallback[0]));
707 $this->assertEquals('commandEventTestHello', $hookCallback[0][1]);
709 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHello');
710 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
712 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
713 $this->assertEquals('test:hello', $command->getName());
714 $commandGetNames = $this->callProtected($command, 'getNames');
715 $this->assertEquals('test:hello,Consolidation\TestUtils\ExampleCommandFile', implode(',', $commandGetNames));
717 $hookCallback = $command->commandProcessor()->hookManager()->get('test:hello', 'command-event');
718 $this->assertTrue($hookCallback != null);
719 $this->assertEquals('commandEventTestHello', $hookCallback[0][1]);
721 $input = new StringInput('test:hello Pluto');
722 $this->assertRunCommandViaApplicationEquals($command, $input, "Here comes Pluto!\nHello, Pluto.");
726 function testInteractAndValidate()
728 $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
729 $this->commandFactory = new AnnotatedCommandFactory();
731 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'interactTestHello');
733 $this->assertTrue($hookInfo->hasAnnotation('hook'));
734 $this->assertEquals($hookInfo->getAnnotation('hook'), 'interact test:hello');
736 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
738 $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'validateTestHello');
740 $this->assertTrue($hookInfo->hasAnnotation('hook'));
741 $this->assertEquals($hookInfo->getAnnotation('hook'), 'validate test:hello');
743 $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
745 $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::ARGUMENT_VALIDATOR]);
746 $this->assertTrue($hookCallback != null);
747 $this->assertEquals(1, count($hookCallback));
748 $this->assertEquals(2, count($hookCallback[0]));
749 $this->assertTrue(is_callable($hookCallback[0]));
750 $this->assertEquals('validateTestHello', $hookCallback[0][1]);
752 $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::INTERACT]);
753 $this->assertTrue($hookCallback != null);
754 $this->assertEquals(1, count($hookCallback));
755 $this->assertEquals(2, count($hookCallback[0]));
756 $this->assertTrue(is_callable($hookCallback[0]));
757 $this->assertEquals('interactTestHello', $hookCallback[0][1]);
759 $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHello');
760 $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
762 $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
763 $this->assertEquals('test:hello', $command->getName());
764 $commandGetNames = $this->callProtected($command, 'getNames');
765 $this->assertEquals('test:hello,Consolidation\TestUtils\ExampleCommandFile', implode(',', $commandGetNames));
767 $testInteractInput = new StringInput('test:hello');
768 $definition = new \Symfony\Component\Console\Input\InputDefinition(
770 new \Symfony\Component\Console\Input\InputArgument('application', \Symfony\Component\Console\Input\InputArgument::REQUIRED),
771 new \Symfony\Component\Console\Input\InputArgument('who', \Symfony\Component\Console\Input\InputArgument::REQUIRED),
774 $testInteractInput->bind($definition);
775 $testInteractOutput = new BufferedOutput();
776 $command->commandProcessor()->interact(
780 $command->getAnnotationData()
782 $this->assertEquals('Goofey', $testInteractInput->getArgument('who'));
784 $hookCallback = $command->commandProcessor()->hookManager()->get('test:hello', 'interact');
785 $this->assertTrue($hookCallback != null);
786 $this->assertEquals('interactTestHello', $hookCallback[0][1]);
788 $input = new StringInput('test:hello "Mickey Mouse"');
789 $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, Mickey Mouse.');
791 $input = new StringInput('test:hello');
792 $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, Goofey.');
794 $input = new StringInput('test:hello "Donald Duck"');
795 $this->assertRunCommandViaApplicationEquals($command, $input, "I won't say hello to Donald Duck.", 1);
797 $input = new StringInput('test:hello "Drumph"');
798 $this->assertRunCommandViaApplicationEquals($command, $input, "Irrational value error.", 1);
800 // Try the last test again with a display error function installed.
801 $this->commandFactory->commandProcessor()->setDisplayErrorFunction(
802 function ($output, $message) {
803 $output->writeln("*** $message ****");
807 $input = new StringInput('test:hello "Drumph"');
808 $this->assertRunCommandViaApplicationEquals($command, $input, "*** Irrational value error. ****", 1);
811 function callProtected($object, $method, $args = [])
813 $r = new \ReflectionMethod($object, $method);
814 $r->setAccessible(true);
815 return $r->invokeArgs($object, $args);
818 function assertRunCommandViaApplicationContains($command, $input, $containsList, $expectedStatusCode = 0)
820 list($statusCode, $commandOutput) = $this->runCommandViaApplication($command, $input);
822 foreach ($containsList as $contains) {
823 $this->assertContains($contains, $commandOutput);
825 $this->assertEquals($expectedStatusCode, $statusCode);
828 function assertRunCommandViaApplicationEquals($command, $input, $expectedOutput, $expectedStatusCode = 0)
830 list($statusCode, $commandOutput) = $this->runCommandViaApplication($command, $input);
832 $this->assertEquals($expectedOutput, $commandOutput);
833 $this->assertEquals($expectedStatusCode, $statusCode);
836 function runCommandViaApplication($command, $input)
838 $output = new BufferedOutput();
839 if ($this->commandFileInstance && method_exists($this->commandFileInstance, 'setOutput')) {
840 $this->commandFileInstance->setOutput($output);
843 $application = new Application('TestApplication', '0.0.0');
844 $alterOptionsEventManager = new AlterOptionsCommandEvent($application);
846 $eventDispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
847 $eventDispatcher->addSubscriber($this->commandFactory->commandProcessor()->hookManager());
848 $eventDispatcher->addSubscriber($alterOptionsEventManager);
849 $application->setDispatcher($eventDispatcher);
851 $application->setAutoExit(false);
852 $application->add($command);
854 $statusCode = $application->run($input, $output);
855 $commandOutput = trim($output->fetch());
857 return [$statusCode, $commandOutput];