Drupal investigation

testAnnotatedCommandFactory.php 43KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. <?php
  2. namespace Consolidation\AnnotatedCommand;
  3. use Consolidation\AnnotatedCommand\AnnotationData;
  4. use Consolidation\AnnotatedCommand\CommandData;
  5. use Consolidation\AnnotatedCommand\Hooks\HookManager;
  6. use Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent;
  7. use Consolidation\AnnotatedCommand\Parser\CommandInfo;
  8. use Consolidation\TestUtils\ExampleCommandInfoAlterer;
  9. use Symfony\Component\Console\Application;
  10. use Symfony\Component\Console\Command\Command;
  11. use Symfony\Component\Console\Input\InputInterface;
  12. use Symfony\Component\Console\Input\ArgvInput;
  13. use Symfony\Component\Console\Input\StringInput;
  14. use Symfony\Component\Console\Output\BufferedOutput;
  15. use Symfony\Component\Console\Output\OutputInterface;
  16. class AnnotatedCommandFactoryTests extends \PHPUnit_Framework_TestCase
  17. {
  18. protected $commandFileInstance;
  19. protected $commandFactory;
  20. function testOptionDefaultValue()
  21. {
  22. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  23. $this->commandFactory = new AnnotatedCommandFactory();
  24. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaultOptionOne');
  25. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  26. $this->assertEquals('default:option-one', $command->getName());
  27. $this->assertEquals('default:option-one [--foo [FOO]]', $command->getSynopsis());
  28. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  29. $input = new StringInput('default:option-one');
  30. $this->assertRunCommandViaApplicationEquals($command, $input, 'Foo is 1');
  31. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaultOptionTwo');
  32. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  33. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  34. $this->assertEquals('default:option-two', $command->getName());
  35. $this->assertEquals('default:option-two [--foo [FOO]]', $command->getSynopsis());
  36. $input = new StringInput('default:option-two');
  37. $this->assertRunCommandViaApplicationEquals($command, $input, 'Foo is 2');
  38. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaultOptionNone');
  39. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  40. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  41. $this->assertEquals('default:option-none', $command->getName());
  42. $this->assertEquals('default:option-none [--foo FOO]', $command->getSynopsis());
  43. // Skip failing test until Symfony is fixed.
  44. $this->markTestSkipped('Symfony Console 3.2.5 and 3.2.6 do not handle default options with required values correctly.');
  45. $input = new StringInput('default:option-none --foo');
  46. $this->assertRunCommandViaApplicationContains($command, $input, ['The "--foo" option requires a value.'], 1);
  47. }
  48. /**
  49. * Test CommandInfo command caching.
  50. *
  51. * Sequence:
  52. * - Create all of the command info objects from one class, caching them.
  53. * - Change the method name of one of the items in the cache to a non-existent method
  54. * - Restore all of the cached commandinfo objects
  55. * - Ensure that the non-existent method cached commandinfo was not created
  56. * - Ensure that the now-missing cached commandinfo was still created
  57. *
  58. * This tests both save/restore, plus adding a new command method to
  59. * a class, and removing a command method from a class.
  60. */
  61. function testAnnotatedCommandCache()
  62. {
  63. $testCacheStore = new \Consolidation\TestUtils\InMemoryCacheStore();
  64. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  65. $this->commandFactory = new AnnotatedCommandFactory();
  66. $this->commandFactory->setDataStore($testCacheStore);
  67. // Make commandInfo objects for every command in the test commandfile.
  68. // These will also be stored in our cache.
  69. $commandInfoList = $this->commandFactory->getCommandInfoListFromClass($this->commandFileInstance);
  70. $cachedClassName = get_class($this->commandFileInstance);
  71. $this->assertTrue($testCacheStore->has($cachedClassName));
  72. $cachedData = $testCacheStore->get($cachedClassName);
  73. $this->assertFalse(empty($cachedData));
  74. $this->assertTrue(array_key_exists('testArithmatic', $cachedData));
  75. $alterCommandInfoCache = $cachedData['testArithmatic'];
  76. unset($cachedData['testArithmatic']);
  77. $alterCommandInfoCache['method_name'] = 'nonExistentMethod';
  78. $cachedData[$alterCommandInfoCache['method_name']] = $alterCommandInfoCache;
  79. $testCacheStore->set($cachedClassName, $cachedData);
  80. $restoredCommandInfoList = $this->commandFactory->getCommandInfoListFromClass($this->commandFileInstance);
  81. $rebuiltCachedData = $testCacheStore->get($cachedClassName);
  82. $this->assertFalse(empty($rebuiltCachedData));
  83. $this->assertTrue(array_key_exists('testArithmatic', $rebuiltCachedData));
  84. $this->assertFalse(array_key_exists('nonExistentMethod', $rebuiltCachedData));
  85. }
  86. /**
  87. * Test CommandInfo command annotation parsing.
  88. */
  89. function testAnnotatedCommandCreation()
  90. {
  91. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  92. $this->commandFactory = new AnnotatedCommandFactory();
  93. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testArithmatic');
  94. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  95. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  96. $this->assertEquals('test:arithmatic', $command->getName());
  97. $this->assertEquals('This is the test:arithmatic command', $command->getDescription());
  98. $this->assertEquals("This command will add one and two. If the --negate flag\nis provided, then the result is negated.", $command->getHelp());
  99. $this->assertEquals('arithmatic', implode(',', $command->getAliases()));
  100. $this->assertEquals('test:arithmatic [--negate] [--unused [UNUSED]] [--] <one> [<two>]', $command->getSynopsis());
  101. $this->assertEquals('test:arithmatic 2 2 --negate', implode(',', $command->getUsages()));
  102. $input = new StringInput('arithmatic 2 3 --negate');
  103. $this->assertRunCommandViaApplicationEquals($command, $input, '-5');
  104. }
  105. /**
  106. * Test CommandInfo command annotation altering.
  107. */
  108. function testAnnotatedCommandInfoAlteration()
  109. {
  110. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  111. $this->commandFactory = new AnnotatedCommandFactory();
  112. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myCat');
  113. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  114. $annotationData = $command->getAnnotationData();
  115. $this->assertTrue($annotationData->has('arbitrary'));
  116. $this->assertFalse($annotationData->has('dynamic'));
  117. $this->commandFactory->addCommandInfoAlterer(new ExampleCommandInfoAlterer());
  118. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  119. $annotationData = $command->getAnnotationData();
  120. $this->assertTrue($annotationData->has('arbitrary'));
  121. $this->assertTrue($annotationData->has('dynamic'));
  122. }
  123. function testMyCatCommand()
  124. {
  125. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  126. $this->commandFactory = new AnnotatedCommandFactory();
  127. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myCat');
  128. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  129. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  130. $this->assertEquals('my:cat', $command->getName());
  131. $this->assertEquals('This is the my:cat command', $command->getDescription());
  132. $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());
  133. $this->assertEquals('c', implode(',', $command->getAliases()));
  134. $this->assertEquals('my:cat [--flip] [--] <one> [<two>]', $command->getSynopsis());
  135. $this->assertEquals('my:cat bet alpha --flip', implode(',', $command->getUsages()));
  136. $input = new StringInput('my:cat bet alpha --flip');
  137. $this->assertRunCommandViaApplicationEquals($command, $input, 'alphabet');
  138. }
  139. function testJoinCommandHelp()
  140. {
  141. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  142. $this->commandFactory = new AnnotatedCommandFactory();
  143. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myJoin');
  144. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  145. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  146. $this->assertEquals('my:join', $command->getName());
  147. $this->assertEquals('This is the my:join command', $command->getDescription());
  148. $this->assertEquals("This command will join its parameters together. It can also reverse and repeat its arguments.", $command->getHelp());
  149. $this->assertEquals('my:join [--flip] [--repeat [REPEAT]] [--] [<args>]...', $command->getSynopsis());
  150. // Bug in parser: @usage with no parameters or options not passed to us correctly.
  151. $actualUsages = implode(',', $command->getUsages());
  152. if ($actualUsages == 'my:join a b,my:join Example with no parameters or options') {
  153. $this->markTestSkipped();
  154. }
  155. $this->assertEquals('my:join a b,my:join', $actualUsages);
  156. $input = new StringInput('my:join bet alpha --flip --repeat=2');
  157. $this->assertRunCommandViaApplicationEquals($command, $input, 'alphabetalphabet');
  158. }
  159. function testDefaultsCommand()
  160. {
  161. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  162. $this->commandFactory = new AnnotatedCommandFactory();
  163. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'defaults');
  164. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  165. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  166. $this->assertEquals('defaults', $command->getName());
  167. $this->assertEquals('Test default values in arguments', $command->getDescription());
  168. $input = new StringInput('defaults');
  169. $this->assertRunCommandViaApplicationEquals($command, $input, 'nothing provided');
  170. $input = new StringInput('defaults ichi');
  171. $this->assertRunCommandViaApplicationEquals($command, $input, 'only ichi');
  172. $input = new StringInput('defaults I II');
  173. $this->assertRunCommandViaApplicationEquals($command, $input, 'I and II');
  174. }
  175. function testCommandWithNoOptions()
  176. {
  177. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  178. $this->commandFactory = new AnnotatedCommandFactory();
  179. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'commandWithNoOptions');
  180. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  181. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  182. $this->assertEquals('command:with-no-options', $command->getName());
  183. $this->assertEquals('This is a command with no options', $command->getDescription());
  184. $this->assertEquals("This command will concatenate two parameters.", $command->getHelp());
  185. $this->assertEquals('nope', implode(',', $command->getAliases()));
  186. $this->assertEquals('command:with-no-options <one> [<two>]', $command->getSynopsis());
  187. $this->assertEquals('command:with-no-options alpha bet', implode(',', $command->getUsages()));
  188. $input = new StringInput('command:with-no-options something');
  189. $this->assertRunCommandViaApplicationEquals($command, $input, 'somethingdefault');
  190. $input = new StringInput('help command:with-no-options something');
  191. $this->assertRunCommandViaApplicationContains(
  192. $command,
  193. $input,
  194. [
  195. 'The first parameter.',
  196. 'The other parameter.',
  197. ]
  198. );
  199. }
  200. function testCommandWithNoArguments()
  201. {
  202. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  203. $this->commandFactory = new AnnotatedCommandFactory();
  204. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'commandWithNoArguments');
  205. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  206. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  207. $this->assertEquals('command:with-no-arguments', $command->getName());
  208. $this->assertEquals('This command has no arguments--only options', $command->getDescription());
  209. $this->assertEquals("Return a result only if not silent.", $command->getHelp());
  210. $this->assertEquals('command:with-no-arguments [-s|--silent]', $command->getSynopsis());
  211. $input = new StringInput('command:with-no-arguments');
  212. $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, world');
  213. $input = new StringInput('command:with-no-arguments -s');
  214. $this->assertRunCommandViaApplicationEquals($command, $input, '');
  215. $input = new StringInput('command:with-no-arguments --silent');
  216. $this->assertRunCommandViaApplicationEquals($command, $input, '');
  217. }
  218. function testCommandWithShortcutOnAnnotation()
  219. {
  220. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  221. $this->commandFactory = new AnnotatedCommandFactory();
  222. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'shortcutOnAnnotation');
  223. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  224. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  225. $this->assertEquals('shortcut:on-annotation', $command->getName());
  226. $this->assertEquals('Shortcut on annotation', $command->getDescription());
  227. $this->assertEquals("This command defines the option shortcut on the annotation instead of in the options array.", $command->getHelp());
  228. $this->assertEquals('shortcut:on-annotation [-s|--silent]', $command->getSynopsis());
  229. $input = new StringInput('shortcut:on-annotation');
  230. $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, world');
  231. $input = new StringInput('shortcut:on-annotation -s');
  232. $this->assertRunCommandViaApplicationEquals($command, $input, '');
  233. $input = new StringInput('shortcut:on-annotation --silent');
  234. $this->assertRunCommandViaApplicationEquals($command, $input, '');
  235. }
  236. function testState()
  237. {
  238. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile('secret secret');
  239. $this->commandFactory = new AnnotatedCommandFactory();
  240. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testState');
  241. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  242. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  243. $this->assertEquals('test:state', $command->getName());
  244. $input = new StringInput('test:state');
  245. $this->assertRunCommandViaApplicationEquals($command, $input, 'secret secret');
  246. }
  247. function testPassthroughArray()
  248. {
  249. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  250. $this->commandFactory = new AnnotatedCommandFactory();
  251. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testPassthrough');
  252. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  253. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  254. $this->assertEquals('test:passthrough', $command->getName());
  255. $input = new StringInput('test:passthrough a b c -- x y z');
  256. $this->assertRunCommandViaApplicationEquals($command, $input, 'a,b,c,x,y,z');
  257. }
  258. function testPassThroughNonArray()
  259. {
  260. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  261. $this->commandFactory = new AnnotatedCommandFactory();
  262. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myJoin');
  263. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  264. $input = new StringInput('my:join bet --flip -- x y z');
  265. $this->assertRunCommandViaApplicationEquals($command, $input, 'zyxbet');
  266. // Can't look at 'hasOption' until after the command initializes the
  267. // option, because Symfony.
  268. $this->assertTrue($input->hasOption('flip'));
  269. }
  270. function testPassThroughWithInputManipulation()
  271. {
  272. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  273. $this->commandFactory = new AnnotatedCommandFactory();
  274. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'myJoin');
  275. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  276. $input = new StringInput('my:join bet --repeat=2 -- x y z');
  277. $this->assertRunCommandViaApplicationEquals($command, $input, 'betxyzbetxyz');
  278. // Symfony does not allow us to manipulate the options via setOption until
  279. // the definition from the command object has been set up.
  280. $input->setOption('repeat', 3);
  281. $this->assertEquals(3, $input->getOption('repeat'));
  282. $input->setArgument(0, 'q');
  283. // Manipulating $input does not work -- the changes are not effective.
  284. // The end result here should be 'qx y yqx y yqx y y'
  285. $this->assertRunCommandViaApplicationEquals($command, $input, 'betxyzbetxyz');
  286. }
  287. function testRequiredArrayOption()
  288. {
  289. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  290. $this->commandFactory = new AnnotatedCommandFactory();
  291. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testRequiredArrayOption');
  292. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  293. $this->assertEquals('test:required-array-option [-a|--arr ARR]', $command->getSynopsis());
  294. $input = new StringInput('test:required-array-option --arr=1 --arr=2 --arr=3');
  295. $this->assertRunCommandViaApplicationEquals($command, $input, '1 2 3');
  296. $input = new StringInput('test:required-array-option -a 1 -a 2 -a 3');
  297. $this->assertRunCommandViaApplicationEquals($command, $input, '1 2 3');
  298. }
  299. function testArrayOption()
  300. {
  301. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile;
  302. $this->commandFactory = new AnnotatedCommandFactory();
  303. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testArrayOption');
  304. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  305. $this->assertEquals('test:array-option [-a|--arr [ARR]]', $command->getSynopsis());
  306. $input = new StringInput('test:array-option');
  307. $this->assertRunCommandViaApplicationEquals($command, $input, '1 2 3');
  308. $input = new StringInput('test:array-option --arr=a --arr=b --arr=c');
  309. $this->assertRunCommandViaApplicationEquals($command, $input, 'a b c');
  310. $input = new StringInput('test:array-option -a a');
  311. $this->assertRunCommandViaApplicationEquals($command, $input, 'a');
  312. }
  313. function testHookedCommand()
  314. {
  315. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  316. $this->commandFactory = new AnnotatedCommandFactory();
  317. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestHook');
  318. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  319. $this->assertEquals('alter test:hook', $hookInfo->getAnnotation('hook'));
  320. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  321. $hookCallback = $this->commandFactory->hookManager()->get('test:hook', [HookManager::ALTER_RESULT]);
  322. $this->assertTrue($hookCallback != null);
  323. $this->assertEquals(1, count($hookCallback));
  324. $this->assertEquals(2, count($hookCallback[0]));
  325. $this->assertTrue(is_callable($hookCallback[0]));
  326. $this->assertEquals('hookTestHook', $hookCallback[0][1]);
  327. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHook');
  328. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  329. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  330. $this->assertEquals('test:hook', $command->getName());
  331. $input = new StringInput('test:hook bar');
  332. $this->assertRunCommandViaApplicationEquals($command, $input, '<[bar]>');
  333. $input = new StringInput('list --raw');
  334. $this->assertRunCommandViaApplicationContains($command, $input, ['This command wraps its parameter in []; its alter hook then wraps the result in .']);
  335. }
  336. function testReplaceCommandHook(){
  337. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  338. $this->commandFactory = new AnnotatedCommandFactory();
  339. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestReplaceCommandHook');
  340. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  341. $this->assertEquals('replace-command test:replace-command', $hookInfo->getAnnotation('hook'));
  342. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  343. $hookCallback = $this->commandFactory->hookManager()->get('test:replace-command', [HookManager::REPLACE_COMMAND_HOOK]);
  344. $this->assertTrue($hookCallback != null);
  345. $this->assertEquals(1, count($hookCallback));
  346. $this->assertEquals(2, count($hookCallback[0]));
  347. $this->assertTrue(is_callable($hookCallback[0]));
  348. $this->assertEquals('hookTestReplaceCommandHook', $hookCallback[0][1]);
  349. $input = new StringInput('test:replace-command foo');
  350. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testReplaceCommand');
  351. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  352. $this->assertRunCommandViaApplicationEquals($command, $input, "bar", 0);
  353. }
  354. function testPostCommandCalledAfterCommand()
  355. {
  356. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  357. $this->commandFactory = new AnnotatedCommandFactory();
  358. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestPostCommandHook');
  359. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  360. $this->assertEquals('post-command test:post-command', $hookInfo->getAnnotation('hook'));
  361. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  362. $hookCallback = $this->commandFactory->hookManager()->get('test:post-command', [HookManager::POST_COMMAND_HOOK]);
  363. $this->assertTrue($hookCallback != null);
  364. $this->assertEquals(1, count($hookCallback));
  365. $this->assertEquals(2, count($hookCallback[0]));
  366. $this->assertTrue(is_callable($hookCallback[0]));
  367. $this->assertEquals('hookTestPostCommandHook', $hookCallback[0][1]);
  368. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestPreCommandHook');
  369. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  370. $this->assertEquals('pre-command test:post-command', $hookInfo->getAnnotation('hook'));
  371. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  372. $hookCallback = $this->commandFactory->hookManager()->get('test:post-command', [HookManager::PRE_COMMAND_HOOK]);
  373. $this->assertTrue($hookCallback != null);
  374. $this->assertEquals(1, count($hookCallback));
  375. $this->assertEquals(2, count($hookCallback[0]));
  376. $this->assertTrue(is_callable($hookCallback[0]));
  377. $this->assertEquals('hookTestPreCommandHook', $hookCallback[0][1]);
  378. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testPostCommand');
  379. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  380. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  381. $this->assertEquals('test:post-command', $command->getName());
  382. $input = new StringInput('test:post-command bar');
  383. $this->assertRunCommandViaApplicationEquals($command, $input, "foo\nbar\nbaz", 0, $this->commandFileInstance);
  384. }
  385. function testHookAllCommands()
  386. {
  387. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleHookAllCommandFile();
  388. $this->commandFactory = new AnnotatedCommandFactory();
  389. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'alterAllCommands');
  390. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  391. $this->assertEquals('alter', $hookInfo->getAnnotation('hook'));
  392. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  393. $hookCallback = $this->commandFactory->hookManager()->get('Consolidation\TestUtils\ExampleHookAllCommandFile', [HookManager::ALTER_RESULT]);
  394. $this->assertTrue($hookCallback != null);
  395. $this->assertEquals(1, count($hookCallback));
  396. $this->assertEquals(2, count($hookCallback[0]));
  397. $this->assertTrue(is_callable($hookCallback[0]));
  398. $this->assertEquals('alterAllCommands', $hookCallback[0][1]);
  399. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'doCat');
  400. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  401. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  402. $this->assertEquals('do:cat', $command->getName());
  403. $input = new StringInput('do:cat bar');
  404. $this->assertRunCommandViaApplicationEquals($command, $input, '*** bar ***');
  405. }
  406. function testDoubleDashWithVersion()
  407. {
  408. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleHookAllCommandFile();
  409. $this->commandFactory = new AnnotatedCommandFactory();
  410. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'doCat');
  411. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  412. $input = new ArgvInput(['placeholder', 'do:cat', 'one', '--', '--version']);
  413. list($statusCode, $commandOutput) = $this->runCommandViaApplication($command, $input);
  414. if ($commandOutput == 'TestApplication version 0.0.0') {
  415. $this->markTestSkipped('Symfony/Console 2.x does not respect -- with --version');
  416. }
  417. $this->assertEquals('one--version', $commandOutput);
  418. }
  419. function testAnnotatedHookedCommand()
  420. {
  421. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  422. $this->commandFactory = new AnnotatedCommandFactory();
  423. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestAnnotatedHook');
  424. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  425. $this->assertEquals('alter @hookme', $hookInfo->getAnnotation('hook'));
  426. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  427. $hookCallback = $this->commandFactory->hookManager()->get('@hookme', [HookManager::ALTER_RESULT]);
  428. $this->assertTrue($hookCallback != null);
  429. $this->assertEquals(1, count($hookCallback));
  430. $this->assertEquals(2, count($hookCallback[0]));
  431. $this->assertTrue(is_callable($hookCallback[0]));
  432. $this->assertEquals('hookTestAnnotatedHook', $hookCallback[0][1]);
  433. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testAnnotationHook');
  434. $annotationData = $commandInfo->getRawAnnotations();
  435. $this->assertEquals('hookme,before,after', implode(',', $annotationData->keys()));
  436. $this->assertEquals('@hookme,@before,@after', implode(',', array_map(function ($item) { return "@$item"; }, $annotationData->keys())));
  437. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  438. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  439. $this->assertEquals('test:annotation-hook', $command->getName());
  440. $input = new StringInput('test:annotation-hook baz');
  441. $this->assertRunCommandViaApplicationEquals($command, $input, '>(baz)<');
  442. }
  443. function testHookHasCommandAnnotation()
  444. {
  445. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  446. $this->commandFactory = new AnnotatedCommandFactory();
  447. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookAddCommandName');
  448. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  449. $this->assertEquals('alter @addmycommandname', $hookInfo->getAnnotation('hook'));
  450. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  451. $hookCallback = $this->commandFactory->hookManager()->get('@addmycommandname', [HookManager::ALTER_RESULT]);
  452. $this->assertTrue($hookCallback != null);
  453. $this->assertEquals(1, count($hookCallback));
  454. $this->assertEquals(2, count($hookCallback[0]));
  455. $this->assertTrue(is_callable($hookCallback[0]));
  456. $this->assertEquals('hookAddCommandName', $hookCallback[0][1]);
  457. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'alterMe');
  458. $annotationData = $commandInfo->getRawAnnotations();
  459. $this->assertEquals('command,addmycommandname', implode(',', $annotationData->keys()));
  460. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  461. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  462. $this->assertEquals('alter-me', $command->getName());
  463. $input = new StringInput('alter-me');
  464. $this->assertRunCommandViaApplicationEquals($command, $input, 'splendiferous from alter-me');
  465. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'alterMeToo');
  466. $annotationData = $commandInfo->getRawAnnotations();
  467. $this->assertEquals('addmycommandname', implode(',', $annotationData->keys()));
  468. $annotationData = $commandInfo->getAnnotations();
  469. $this->assertEquals('addmycommandname,command,_path,_classname', implode(',', $annotationData->keys()));
  470. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  471. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  472. $this->assertEquals('alter:me-too', $command->getName());
  473. $input = new StringInput('alter:me-too');
  474. $this->assertRunCommandViaApplicationEquals($command, $input, 'fantabulous from alter:me-too');
  475. }
  476. function testHookedCommandWithHookAddedLater()
  477. {
  478. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  479. $this->commandFactory = new AnnotatedCommandFactory();
  480. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHook');
  481. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  482. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  483. $this->assertEquals('test:hook', $command->getName());
  484. // Run the command once without the hook
  485. $input = new StringInput('test:hook foo');
  486. $this->assertRunCommandViaApplicationEquals($command, $input, '[foo]');
  487. // Register the hook and run the command again
  488. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'hookTestHook');
  489. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  490. $this->assertEquals('alter test:hook', $hookInfo->getAnnotation('hook'));
  491. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  492. $hookCallback = $this->commandFactory->hookManager()->get('test:hook', [HookManager::ALTER_RESULT]);;
  493. $this->assertTrue($hookCallback != null);
  494. $this->assertEquals(1, count($hookCallback));
  495. $this->assertEquals(2, count($hookCallback[0]));
  496. $this->assertTrue(is_callable($hookCallback[0]));
  497. $this->assertEquals('hookTestHook', $hookCallback[0][1]);
  498. $input = new StringInput('test:hook bar');
  499. $this->assertRunCommandViaApplicationEquals($command, $input, '<[bar]>');
  500. }
  501. function testInitializeHook()
  502. {
  503. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  504. $this->commandFactory = new AnnotatedCommandFactory();
  505. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'initializeTestHello');
  506. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  507. $this->assertEquals($hookInfo->getAnnotation('hook'), 'init test:hello');
  508. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  509. $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::INITIALIZE]);
  510. $this->assertTrue($hookCallback != null);
  511. $this->assertEquals(1, count($hookCallback));
  512. $this->assertEquals(2, count($hookCallback[0]));
  513. $this->assertTrue(is_callable($hookCallback[0]));
  514. $this->assertEquals('initializeTestHello', $hookCallback[0][1]);
  515. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHello');
  516. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  517. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  518. $this->assertEquals('test:hello', $command->getName());
  519. $commandGetNames = $this->callProtected($command, 'getNames');
  520. $this->assertEquals('test:hello,Consolidation\TestUtils\ExampleCommandFile', implode(',', $commandGetNames));
  521. $hookCallback = $command->commandProcessor()->hookManager()->get('test:hello', 'init');
  522. $this->assertTrue($hookCallback != null);
  523. $this->assertEquals('initializeTestHello', $hookCallback[0][1]);
  524. $input = new StringInput('test:hello');
  525. $this->assertRunCommandViaApplicationEquals($command, $input, "Hello, Huey.");
  526. }
  527. function testCommandEventHook()
  528. {
  529. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  530. $this->commandFactory = new AnnotatedCommandFactory();
  531. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'commandEventTestHello');
  532. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  533. $this->assertEquals($hookInfo->getAnnotation('hook'), 'command-event test:hello');
  534. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  535. $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::COMMAND_EVENT]);
  536. $this->assertTrue($hookCallback != null);
  537. $this->assertEquals(1, count($hookCallback));
  538. $this->assertEquals(2, count($hookCallback[0]));
  539. $this->assertTrue(is_callable($hookCallback[0]));
  540. $this->assertEquals('commandEventTestHello', $hookCallback[0][1]);
  541. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHello');
  542. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  543. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  544. $this->assertEquals('test:hello', $command->getName());
  545. $commandGetNames = $this->callProtected($command, 'getNames');
  546. $this->assertEquals('test:hello,Consolidation\TestUtils\ExampleCommandFile', implode(',', $commandGetNames));
  547. $hookCallback = $command->commandProcessor()->hookManager()->get('test:hello', 'command-event');
  548. $this->assertTrue($hookCallback != null);
  549. $this->assertEquals('commandEventTestHello', $hookCallback[0][1]);
  550. $input = new StringInput('test:hello Pluto');
  551. $this->assertRunCommandViaApplicationEquals($command, $input, "Here comes Pluto!\nHello, Pluto.");
  552. }
  553. function testInteractAndValidate()
  554. {
  555. $this->commandFileInstance = new \Consolidation\TestUtils\ExampleCommandFile();
  556. $this->commandFactory = new AnnotatedCommandFactory();
  557. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'interactTestHello');
  558. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  559. $this->assertEquals($hookInfo->getAnnotation('hook'), 'interact test:hello');
  560. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  561. $hookInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'validateTestHello');
  562. $this->assertTrue($hookInfo->hasAnnotation('hook'));
  563. $this->assertEquals($hookInfo->getAnnotation('hook'), 'validate test:hello');
  564. $this->commandFactory->registerCommandHook($hookInfo, $this->commandFileInstance);
  565. $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::ARGUMENT_VALIDATOR]);
  566. $this->assertTrue($hookCallback != null);
  567. $this->assertEquals(1, count($hookCallback));
  568. $this->assertEquals(2, count($hookCallback[0]));
  569. $this->assertTrue(is_callable($hookCallback[0]));
  570. $this->assertEquals('validateTestHello', $hookCallback[0][1]);
  571. $hookCallback = $this->commandFactory->hookManager()->get('test:hello', [HookManager::INTERACT]);
  572. $this->assertTrue($hookCallback != null);
  573. $this->assertEquals(1, count($hookCallback));
  574. $this->assertEquals(2, count($hookCallback[0]));
  575. $this->assertTrue(is_callable($hookCallback[0]));
  576. $this->assertEquals('interactTestHello', $hookCallback[0][1]);
  577. $commandInfo = $this->commandFactory->createCommandInfo($this->commandFileInstance, 'testHello');
  578. $command = $this->commandFactory->createCommand($commandInfo, $this->commandFileInstance);
  579. $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command);
  580. $this->assertEquals('test:hello', $command->getName());
  581. $commandGetNames = $this->callProtected($command, 'getNames');
  582. $this->assertEquals('test:hello,Consolidation\TestUtils\ExampleCommandFile', implode(',', $commandGetNames));
  583. $testInteractInput = new StringInput('test:hello');
  584. $definition = new \Symfony\Component\Console\Input\InputDefinition(
  585. [
  586. new \Symfony\Component\Console\Input\InputArgument('application', \Symfony\Component\Console\Input\InputArgument::REQUIRED),
  587. new \Symfony\Component\Console\Input\InputArgument('who', \Symfony\Component\Console\Input\InputArgument::REQUIRED),
  588. ]
  589. );
  590. $testInteractInput->bind($definition);
  591. $testInteractOutput = new BufferedOutput();
  592. $command->commandProcessor()->interact(
  593. $testInteractInput,
  594. $testInteractOutput,
  595. $commandGetNames,
  596. $command->getAnnotationData()
  597. );
  598. $this->assertEquals('Goofey', $testInteractInput->getArgument('who'));
  599. $hookCallback = $command->commandProcessor()->hookManager()->get('test:hello', 'interact');
  600. $this->assertTrue($hookCallback != null);
  601. $this->assertEquals('interactTestHello', $hookCallback[0][1]);
  602. $input = new StringInput('test:hello "Mickey Mouse"');
  603. $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, Mickey Mouse.');
  604. $input = new StringInput('test:hello');
  605. $this->assertRunCommandViaApplicationEquals($command, $input, 'Hello, Goofey.');
  606. $input = new StringInput('test:hello "Donald Duck"');
  607. $this->assertRunCommandViaApplicationEquals($command, $input, "I won't say hello to Donald Duck.", 1);
  608. $input = new StringInput('test:hello "Drumph"');
  609. $this->assertRunCommandViaApplicationEquals($command, $input, "Irrational value error.", 1);
  610. // Try the last test again with a display error function installed.
  611. $this->commandFactory->commandProcessor()->setDisplayErrorFunction(
  612. function ($output, $message) {
  613. $output->writeln("*** $message ****");
  614. }
  615. );
  616. $input = new StringInput('test:hello "Drumph"');
  617. $this->assertRunCommandViaApplicationEquals($command, $input, "*** Irrational value error. ****", 1);
  618. }
  619. function callProtected($object, $method, $args = [])
  620. {
  621. $r = new \ReflectionMethod($object, $method);
  622. $r->setAccessible(true);
  623. return $r->invokeArgs($object, $args);
  624. }
  625. function assertRunCommandViaApplicationContains($command, $input, $containsList, $expectedStatusCode = 0)
  626. {
  627. list($statusCode, $commandOutput) = $this->runCommandViaApplication($command, $input);
  628. foreach ($containsList as $contains) {
  629. $this->assertContains($contains, $commandOutput);
  630. }
  631. $this->assertEquals($expectedStatusCode, $statusCode);
  632. }
  633. function assertRunCommandViaApplicationEquals($command, $input, $expectedOutput, $expectedStatusCode = 0)
  634. {
  635. list($statusCode, $commandOutput) = $this->runCommandViaApplication($command, $input);
  636. $this->assertEquals($expectedOutput, $commandOutput);
  637. $this->assertEquals($expectedStatusCode, $statusCode);
  638. }
  639. function runCommandViaApplication($command, $input)
  640. {
  641. $output = new BufferedOutput();
  642. if ($this->commandFileInstance && method_exists($this->commandFileInstance, 'setOutput')) {
  643. $this->commandFileInstance->setOutput($output);
  644. }
  645. $application = new Application('TestApplication', '0.0.0');
  646. $alterOptionsEventManager = new AlterOptionsCommandEvent($application);
  647. $eventDispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
  648. $eventDispatcher->addSubscriber($this->commandFactory->commandProcessor()->hookManager());
  649. $eventDispatcher->addSubscriber($alterOptionsEventManager);
  650. $application->setDispatcher($eventDispatcher);
  651. $application->setAutoExit(false);
  652. $application->add($command);
  653. $statusCode = $application->run($input, $output);
  654. $commandOutput = trim($output->fetch());
  655. return [$statusCode, $commandOutput];
  656. }
  657. }