CommandRunnerTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.5.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\Console;
  16. use Cake\Console\CommandCollection;
  17. use Cake\Console\CommandFactoryInterface;
  18. use Cake\Console\CommandRunner;
  19. use Cake\Console\ConsoleIo;
  20. use Cake\Console\Shell;
  21. use Cake\Core\Configure;
  22. use Cake\Event\EventList;
  23. use Cake\Event\EventManager;
  24. use Cake\Http\BaseApplication;
  25. use Cake\TestSuite\Stub\ConsoleOutput;
  26. use Cake\TestSuite\TestCase;
  27. use TestApp\Command\DemoCommand;
  28. use TestApp\Http\EventApplication;
  29. use TestApp\Shell\SampleShell;
  30. /**
  31. * Test case for the CommandCollection
  32. */
  33. class CommandRunnerTest extends TestCase
  34. {
  35. /**
  36. * Tracking property for event triggering
  37. *
  38. * @var bool
  39. */
  40. protected $eventTriggered = false;
  41. /**
  42. * setup
  43. *
  44. * @return void
  45. */
  46. public function setUp()
  47. {
  48. parent::setUp();
  49. Configure::write('App.namespace', 'TestApp');
  50. $this->config = dirname(dirname(__DIR__));
  51. }
  52. /**
  53. * test set on the app
  54. *
  55. * @return void
  56. */
  57. public function testSetApp()
  58. {
  59. $app = $this->getMockBuilder(BaseApplication::class)
  60. ->setConstructorArgs([$this->config])
  61. ->getMock();
  62. $manager = new EventManager();
  63. $app->method('getEventManager')
  64. ->willReturn($manager);
  65. $runner = new CommandRunner($app);
  66. $this->assertSame($app->getEventManager(), $runner->getEventManager());
  67. }
  68. /**
  69. * Test that the console hook not returning a command collection
  70. * raises an error.
  71. *
  72. * @return void
  73. */
  74. public function testRunConsoleHookFailure()
  75. {
  76. $this->expectException(\RuntimeException::class);
  77. $this->expectExceptionMessage('The application\'s `console` method did not return a CommandCollection.');
  78. $app = $this->getMockBuilder(BaseApplication::class)
  79. ->setMethods(['console', 'middleware', 'bootstrap'])
  80. ->setConstructorArgs([$this->config])
  81. ->getMock();
  82. $runner = new CommandRunner($app);
  83. $runner->run(['cake', '-h']);
  84. }
  85. /**
  86. * Test that running with empty argv fails
  87. *
  88. * @return void
  89. */
  90. public function testRunMissingRootCommand()
  91. {
  92. $this->expectException(\RuntimeException::class);
  93. $this->expectExceptionMessage('Cannot run any commands. No arguments received.');
  94. $app = $this->getMockBuilder(BaseApplication::class)
  95. ->setMethods(['middleware', 'bootstrap'])
  96. ->setConstructorArgs([$this->config])
  97. ->getMock();
  98. $runner = new CommandRunner($app);
  99. $runner->run([]);
  100. }
  101. /**
  102. * Test that running an unknown command raises an error.
  103. *
  104. * @return void
  105. */
  106. public function testRunInvalidCommand()
  107. {
  108. $this->expectException(\RuntimeException::class);
  109. $this->expectExceptionMessage('Unknown command `cake nope`. Run `cake --help` to get the list of valid commands.');
  110. $app = $this->getMockBuilder(BaseApplication::class)
  111. ->setMethods(['middleware', 'bootstrap'])
  112. ->setConstructorArgs([$this->config])
  113. ->getMock();
  114. $runner = new CommandRunner($app);
  115. $runner->run(['cake', 'nope', 'nope', 'nope']);
  116. }
  117. /**
  118. * Test using `cake --help` invokes the help command
  119. *
  120. * @return void
  121. */
  122. public function testRunHelpLongOption()
  123. {
  124. $app = $this->getMockBuilder(BaseApplication::class)
  125. ->setMethods(['middleware', 'bootstrap'])
  126. ->setConstructorArgs([$this->config])
  127. ->getMock();
  128. $output = new ConsoleOutput();
  129. $runner = new CommandRunner($app, 'cake');
  130. $result = $runner->run(['cake', '--help'], $this->getMockIo($output));
  131. $this->assertSame(0, $result);
  132. $messages = implode("\n", $output->messages());
  133. $this->assertContains('Current Paths', $messages);
  134. $this->assertContains('- i18n', $messages);
  135. $this->assertContains('Available Commands', $messages);
  136. }
  137. /**
  138. * Test using `cake -h` invokes the help command
  139. *
  140. * @return void
  141. */
  142. public function testRunHelpShortOption()
  143. {
  144. $app = $this->getMockBuilder(BaseApplication::class)
  145. ->setMethods(['middleware', 'bootstrap'])
  146. ->setConstructorArgs([$this->config])
  147. ->getMock();
  148. $output = new ConsoleOutput();
  149. $runner = new CommandRunner($app, 'cake');
  150. $result = $runner->run(['cake', '-h'], $this->getMockIo($output));
  151. $this->assertSame(0, $result);
  152. $messages = implode("\n", $output->messages());
  153. $this->assertContains('- i18n', $messages);
  154. $this->assertContains('Available Commands', $messages);
  155. }
  156. /**
  157. * Test that no command outputs the command list
  158. *
  159. * @return void
  160. */
  161. public function testRunNoCommand()
  162. {
  163. $app = $this->getMockBuilder(BaseApplication::class)
  164. ->setMethods(['middleware', 'bootstrap'])
  165. ->setConstructorArgs([$this->config])
  166. ->getMock();
  167. $output = new ConsoleOutput();
  168. $runner = new CommandRunner($app);
  169. $result = $runner->run(['cake'], $this->getMockIo($output));
  170. $this->assertSame(0, $result, 'help output is success.');
  171. $messages = implode("\n", $output->messages());
  172. $this->assertContains('No command provided. Choose one of the available commands', $messages);
  173. $this->assertContains('- i18n', $messages);
  174. $this->assertContains('Available Commands', $messages);
  175. }
  176. /**
  177. * Test using `cake --verson` invokes the version command
  178. *
  179. * @return void
  180. */
  181. public function testRunVersionAlias()
  182. {
  183. $app = $this->getMockBuilder(BaseApplication::class)
  184. ->setMethods(['middleware', 'bootstrap'])
  185. ->setConstructorArgs([$this->config])
  186. ->getMock();
  187. $output = new ConsoleOutput();
  188. $runner = new CommandRunner($app, 'cake');
  189. $result = $runner->run(['cake', '--version'], $this->getMockIo($output));
  190. $this->assertContains(Configure::version(), $output->messages()[0]);
  191. }
  192. /**
  193. * Test running a valid command
  194. *
  195. * @return void
  196. */
  197. public function testRunValidCommand()
  198. {
  199. $app = $this->getMockBuilder(BaseApplication::class)
  200. ->setMethods(['middleware', 'bootstrap'])
  201. ->setConstructorArgs([$this->config])
  202. ->getMock();
  203. $output = new ConsoleOutput();
  204. $runner = new CommandRunner($app, 'cake');
  205. $result = $runner->run(['cake', 'routes'], $this->getMockIo($output));
  206. $this->assertSame(Shell::CODE_SUCCESS, $result);
  207. $contents = implode("\n", $output->messages());
  208. $this->assertContains('URI template', $contents);
  209. }
  210. /**
  211. * Test running a valid command and that backwards compatible
  212. * inflection is hooked up.
  213. *
  214. * @return void
  215. */
  216. public function testRunValidCommandInflection()
  217. {
  218. $app = $this->getMockBuilder(BaseApplication::class)
  219. ->setMethods(['middleware', 'bootstrap'])
  220. ->setConstructorArgs([$this->config])
  221. ->getMock();
  222. $output = new ConsoleOutput();
  223. $runner = new CommandRunner($app, 'cake');
  224. $result = $runner->run(['cake', 'OrmCache', 'build'], $this->getMockIo($output));
  225. $this->assertSame(Shell::CODE_SUCCESS, $result);
  226. $contents = implode("\n", $output->messages());
  227. $this->assertContains('Cache', $contents);
  228. }
  229. /**
  230. * Test running a valid raising an error
  231. *
  232. * @return void
  233. */
  234. public function testRunValidCommandWithAbort()
  235. {
  236. $app = $this->makeAppWithCommands(['failure' => SampleShell::class]);
  237. $output = new ConsoleOutput();
  238. $runner = new CommandRunner($app, 'cake');
  239. $result = $runner->run(['cake', 'failure', 'with_abort'], $this->getMockIo($output));
  240. $this->assertSame(Shell::CODE_ERROR, $result);
  241. }
  242. /**
  243. * Test returning a non-zero value
  244. *
  245. * @return void
  246. */
  247. public function testRunValidCommandReturnInteger()
  248. {
  249. $app = $this->makeAppWithCommands(['failure' => SampleShell::class]);
  250. $output = new ConsoleOutput();
  251. $runner = new CommandRunner($app, 'cake');
  252. $result = $runner->run(['cake', 'failure', 'returnValue'], $this->getMockIo($output));
  253. $this->assertSame(99, $result);
  254. }
  255. /**
  256. * Ensure that the root command name propagates to shell help
  257. *
  258. * @return void
  259. */
  260. public function testRunRootNamePropagates()
  261. {
  262. $app = $this->makeAppWithCommands(['sample' => SampleShell::class]);
  263. $output = new ConsoleOutput();
  264. $runner = new CommandRunner($app, 'widget');
  265. $runner->run(['widget', 'sample', '-h'], $this->getMockIo($output));
  266. $result = implode("\n", $output->messages());
  267. $this->assertContains('widget sample [-h]', $result);
  268. $this->assertNotContains('cake sample [-h]', $result);
  269. }
  270. /**
  271. * Test running a valid command
  272. *
  273. * @return void
  274. */
  275. public function testRunValidCommandClass()
  276. {
  277. $app = $this->makeAppWithCommands(['ex' => DemoCommand::class]);
  278. $output = new ConsoleOutput();
  279. $runner = new CommandRunner($app, 'cake');
  280. $result = $runner->run(['cake', 'ex'], $this->getMockIo($output));
  281. $this->assertSame(Shell::CODE_SUCCESS, $result);
  282. $messages = implode("\n", $output->messages());
  283. $this->assertContains('Demo Command!', $messages);
  284. }
  285. /**
  286. * Test using a custom factory
  287. *
  288. * @return void
  289. */
  290. public function testRunWithCustomFactory()
  291. {
  292. $output = new ConsoleOutput();
  293. $io = $this->getMockIo($output);
  294. $factory = $this->createMock(CommandFactoryInterface::class);
  295. $factory->expects($this->once())
  296. ->method('create')
  297. ->with(DemoCommand::class)
  298. ->willReturn(new DemoCommand());
  299. $app = $this->makeAppWithCommands(['ex' => DemoCommand::class]);
  300. $runner = new CommandRunner($app, 'cake', $factory);
  301. $result = $runner->run(['cake', 'ex'], $io);
  302. $this->assertSame(Shell::CODE_SUCCESS, $result);
  303. $messages = implode("\n", $output->messages());
  304. $this->assertContains('Demo Command!', $messages);
  305. }
  306. /**
  307. * Test running a valid command
  308. *
  309. * @return void
  310. */
  311. public function testRunEvents()
  312. {
  313. $app = new EventApplication($this->config);
  314. $output = new ConsoleOutput();
  315. $manager = $app->getEventManager();
  316. $manager->setEventList(new EventList());
  317. $runner = new CommandRunner($app, 'cake');
  318. $this->assertSame($manager, $runner->getEventManager());
  319. $result = $runner->run(['cake', 'ex'], $this->getMockIo($output));
  320. $this->assertSame(Shell::CODE_SUCCESS, $result);
  321. $messages = implode("\n", $output->messages());
  322. $this->assertContains('Demo Command!', $messages);
  323. $this->assertCount(1, $manager->listeners('My.event'));
  324. $this->assertEventFired('Console.buildCommands', $manager);
  325. }
  326. /**
  327. * Test running a command class' help
  328. *
  329. * @return void
  330. */
  331. public function testRunValidCommandClassHelp()
  332. {
  333. $app = $this->makeAppWithCommands(['ex' => DemoCommand::class]);
  334. $output = new ConsoleOutput();
  335. $runner = new CommandRunner($app, 'cake');
  336. $result = $runner->run(['cake', 'ex', '-h'], $this->getMockIo($output));
  337. $this->assertSame(Shell::CODE_SUCCESS, $result);
  338. $messages = implode("\n", $output->messages());
  339. $this->assertContains("\ncake ex [-h]", $messages);
  340. $this->assertNotContains('Demo Command!', $messages);
  341. }
  342. /**
  343. * Test that run() fires off the buildCommands event.
  344. *
  345. * @return void
  346. */
  347. public function testRunTriggersBuildCommandsEvent()
  348. {
  349. $app = $this->getMockBuilder(BaseApplication::class)
  350. ->setMethods(['middleware', 'bootstrap'])
  351. ->setConstructorArgs([$this->config])
  352. ->getMock();
  353. $output = new ConsoleOutput();
  354. $runner = new CommandRunner($app, 'cake');
  355. $runner->getEventManager()->on('Console.buildCommands', function ($event, $commands) {
  356. $this->assertInstanceOf(CommandCollection::class, $commands);
  357. $this->eventTriggered = true;
  358. });
  359. $result = $runner->run(['cake', '--version'], $this->getMockIo($output));
  360. $this->assertTrue($this->eventTriggered, 'Should have triggered event.');
  361. }
  362. protected function makeAppWithCommands($commands)
  363. {
  364. $app = $this->getMockBuilder(BaseApplication::class)
  365. ->setMethods(['middleware', 'bootstrap', 'console'])
  366. ->setConstructorArgs([$this->config])
  367. ->getMock();
  368. $collection = new CommandCollection($commands);
  369. $app->method('console')->will($this->returnValue($collection));
  370. return $app;
  371. }
  372. protected function getMockIo($output)
  373. {
  374. $io = $this->getMockBuilder(ConsoleIo::class)
  375. ->setConstructorArgs([$output, $output, null, null])
  376. ->setMethods(['in'])
  377. ->getMock();
  378. return $io;
  379. }
  380. }