ShellTest.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440
  1. <?php
  2. /**
  3. * CakePHP : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP Project
  12. * @since 1.2.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Console;
  16. use Cake\Console\ConsoleIo;
  17. use Cake\Console\ConsoleOptionParser;
  18. use Cake\Console\Shell;
  19. use Cake\Core\Plugin;
  20. use Cake\Filesystem\Folder;
  21. use Cake\TestSuite\TestCase;
  22. use TestApp\Shell\MergeShell;
  23. use TestApp\Shell\ShellTestShell;
  24. use TestApp\Shell\TestingDispatchShell;
  25. /**
  26. * TestAppleTask class
  27. */
  28. class TestAppleTask extends Shell
  29. {
  30. }
  31. /**
  32. * TestBananaTask class
  33. */
  34. class TestBananaTask extends Shell
  35. {
  36. }
  37. class_alias(__NAMESPACE__ . '\TestAppleTask', 'Cake\Shell\Task\TestAppleTask');
  38. class_alias(__NAMESPACE__ . '\TestBananaTask', 'Cake\Shell\Task\TestBananaTask');
  39. /**
  40. * ShellTest class
  41. */
  42. class ShellTest extends TestCase
  43. {
  44. /**
  45. * Fixtures used in this test case
  46. *
  47. * @var array
  48. */
  49. public $fixtures = [
  50. 'core.articles',
  51. 'core.articles_tags',
  52. 'core.attachments',
  53. 'core.comments',
  54. 'core.posts',
  55. 'core.tags',
  56. 'core.users'
  57. ];
  58. /** @var \Cake\Console\Shell */
  59. protected $Shell;
  60. /**
  61. * @var \Cake\Console\ConsoleIo|\PHPUnit\Framework\MockObject\MockObject
  62. */
  63. protected $io;
  64. /**
  65. * setUp method
  66. *
  67. * @return void
  68. */
  69. public function setUp()
  70. {
  71. parent::setUp();
  72. $this->io = $this->getMockBuilder('Cake\Console\ConsoleIo')
  73. ->disableOriginalConstructor()
  74. ->getMock();
  75. $this->Shell = new ShellTestShell($this->io);
  76. if (is_dir(TMP . 'shell_test')) {
  77. $Folder = new Folder(TMP . 'shell_test');
  78. $Folder->delete();
  79. }
  80. }
  81. /**
  82. * testConstruct method
  83. *
  84. * @return void
  85. */
  86. public function testConstruct()
  87. {
  88. $this->assertEquals('ShellTestShell', $this->Shell->name);
  89. $this->assertInstanceOf(ConsoleIo::class, $this->Shell->getIo());
  90. }
  91. /**
  92. * test io method
  93. *
  94. * @group deprecated
  95. * @return void
  96. */
  97. public function testIo()
  98. {
  99. $this->deprecated(function () {
  100. $this->assertInstanceOf(ConsoleIo::class, $this->Shell->io());
  101. $io = $this->getMockBuilder(ConsoleIo::class)
  102. ->disableOriginalConstructor()
  103. ->getMock();
  104. $this->assertSame($io, $this->Shell->io($io));
  105. $this->assertSame($io, $this->Shell->io());
  106. });
  107. }
  108. /**
  109. * testInitialize method
  110. *
  111. * @return void
  112. */
  113. public function testInitialize()
  114. {
  115. static::setAppNamespace();
  116. Plugin::load('TestPlugin');
  117. $this->Shell->tasks = ['Extract' => ['one', 'two']];
  118. $this->Shell->plugin = 'TestPlugin';
  119. $this->Shell->modelClass = 'TestPlugin.TestPluginComments';
  120. $this->Shell->initialize();
  121. $this->Shell->loadModel();
  122. $this->assertTrue(isset($this->Shell->TestPluginComments));
  123. $this->assertInstanceOf(
  124. 'TestPlugin\Model\Table\TestPluginCommentsTable',
  125. $this->Shell->TestPluginComments
  126. );
  127. }
  128. /**
  129. * test LoadModel method
  130. *
  131. * @return void
  132. */
  133. public function testLoadModel()
  134. {
  135. static::setAppNamespace();
  136. $Shell = new MergeShell();
  137. $this->assertInstanceOf(
  138. 'TestApp\Model\Table\ArticlesTable',
  139. $Shell->Articles
  140. );
  141. $this->assertEquals('Articles', $Shell->modelClass);
  142. Plugin::load('TestPlugin');
  143. $result = $this->Shell->loadModel('TestPlugin.TestPluginComments');
  144. $this->assertInstanceOf(
  145. 'TestPlugin\Model\Table\TestPluginCommentsTable',
  146. $result
  147. );
  148. $this->assertInstanceOf(
  149. 'TestPlugin\Model\Table\TestPluginCommentsTable',
  150. $this->Shell->TestPluginComments
  151. );
  152. }
  153. /**
  154. * testIn method
  155. *
  156. * @return void
  157. */
  158. public function testIn()
  159. {
  160. $this->io->expects($this->at(0))
  161. ->method('askChoice')
  162. ->with('Just a test?', ['y', 'n'], 'n')
  163. ->will($this->returnValue('n'));
  164. $this->io->expects($this->at(1))
  165. ->method('ask')
  166. ->with('Just a test?', 'n')
  167. ->will($this->returnValue('n'));
  168. $result = $this->Shell->in('Just a test?', ['y', 'n'], 'n');
  169. $this->assertEquals('n', $result);
  170. $result = $this->Shell->in('Just a test?', null, 'n');
  171. $this->assertEquals('n', $result);
  172. }
  173. /**
  174. * Test in() when not interactive.
  175. *
  176. * @return void
  177. */
  178. public function testInNonInteractive()
  179. {
  180. $this->io->expects($this->never())
  181. ->method('askChoice');
  182. $this->io->expects($this->never())
  183. ->method('ask');
  184. $this->Shell->interactive = false;
  185. $result = $this->Shell->in('Just a test?', 'y/n', 'n');
  186. $this->assertEquals('n', $result);
  187. }
  188. /**
  189. * testVerbose method
  190. *
  191. * @return void
  192. */
  193. public function testVerbose()
  194. {
  195. $this->io->expects($this->once())
  196. ->method('verbose')
  197. ->with('Just a test', 1);
  198. $this->Shell->verbose('Just a test');
  199. }
  200. /**
  201. * testQuiet method
  202. *
  203. * @return void
  204. */
  205. public function testQuiet()
  206. {
  207. $this->io->expects($this->once())
  208. ->method('quiet')
  209. ->with('Just a test', 1);
  210. $this->Shell->quiet('Just a test');
  211. }
  212. /**
  213. * testOut method
  214. *
  215. * @return void
  216. */
  217. public function testOut()
  218. {
  219. $this->io->expects($this->once())
  220. ->method('out')
  221. ->with('Just a test', 1);
  222. $this->Shell->out('Just a test');
  223. }
  224. /**
  225. * testErr method
  226. *
  227. * @return void
  228. */
  229. public function testErr()
  230. {
  231. $this->io->expects($this->once())
  232. ->method('error')
  233. ->with('Just a test', 1);
  234. $this->Shell->err('Just a test');
  235. }
  236. /**
  237. * testErr method with array
  238. *
  239. * @return void
  240. */
  241. public function testErrArray()
  242. {
  243. $this->io->expects($this->once())
  244. ->method('error')
  245. ->with(['Just', 'a', 'test'], 1);
  246. $this->Shell->err(['Just', 'a', 'test']);
  247. }
  248. /**
  249. * testInfo method
  250. *
  251. * @return void
  252. */
  253. public function testInfo()
  254. {
  255. $this->io->expects($this->once())
  256. ->method('info')
  257. ->with('Just a test', 1);
  258. $this->Shell->info('Just a test');
  259. }
  260. /**
  261. * testInfo method with array
  262. *
  263. * @return void
  264. */
  265. public function testInfoArray()
  266. {
  267. $this->io->expects($this->once())
  268. ->method('info')
  269. ->with(['Just', 'a', 'test'], 1);
  270. $this->Shell->info(['Just', 'a', 'test']);
  271. }
  272. /**
  273. * testWarn method
  274. *
  275. * @return void
  276. */
  277. public function testWarn()
  278. {
  279. $this->io->expects($this->once())
  280. ->method('warning')
  281. ->with('Just a test', 1);
  282. $this->Shell->warn('Just a test');
  283. }
  284. /**
  285. * testWarn method with array
  286. *
  287. * @return void
  288. */
  289. public function testWarnArray()
  290. {
  291. $this->io->expects($this->once())
  292. ->method('warning')
  293. ->with(['Just', 'a', 'test'], 1);
  294. $this->Shell->warn(['Just', 'a', 'test']);
  295. }
  296. /**
  297. * testSuccess method
  298. *
  299. * @return void
  300. */
  301. public function testSuccess()
  302. {
  303. $this->io->expects($this->once())
  304. ->method('success')
  305. ->with('Just a test', 1);
  306. $this->Shell->success('Just a test');
  307. }
  308. /**
  309. * testSuccess method with array
  310. *
  311. * @return void
  312. */
  313. public function testSuccessArray()
  314. {
  315. $this->io->expects($this->once())
  316. ->method('success')
  317. ->with(['Just', 'a', 'test'], 1);
  318. $this->Shell->success(['Just', 'a', 'test']);
  319. }
  320. /**
  321. * testNl
  322. *
  323. * @return void
  324. */
  325. public function testNl()
  326. {
  327. $this->io->expects($this->once())
  328. ->method('nl')
  329. ->with(2);
  330. $this->Shell->nl(2);
  331. }
  332. /**
  333. * testHr
  334. *
  335. * @return void
  336. */
  337. public function testHr()
  338. {
  339. $this->io->expects($this->once())
  340. ->method('hr')
  341. ->with(2);
  342. $this->Shell->hr(2);
  343. }
  344. /**
  345. * testError
  346. *
  347. * @group deprecated
  348. * @return void
  349. */
  350. public function testError()
  351. {
  352. $this->io->expects($this->at(0))
  353. ->method('err')
  354. ->with('<error>Error:</error> Foo Not Found');
  355. $this->io->expects($this->at(1))
  356. ->method('err')
  357. ->with('Searched all...');
  358. $this->deprecated(function () {
  359. $this->Shell->error('Foo Not Found', 'Searched all...');
  360. $this->assertSame($this->Shell->stopped, 1);
  361. });
  362. }
  363. /**
  364. * testLoadTasks method
  365. *
  366. * @return void
  367. */
  368. public function testLoadTasks()
  369. {
  370. $this->assertTrue($this->Shell->loadTasks());
  371. $this->Shell->tasks = null;
  372. $this->assertTrue($this->Shell->loadTasks());
  373. $this->Shell->tasks = false;
  374. $this->assertTrue($this->Shell->loadTasks());
  375. $this->Shell->tasks = true;
  376. $this->assertTrue($this->Shell->loadTasks());
  377. $this->Shell->tasks = [];
  378. $this->assertTrue($this->Shell->loadTasks());
  379. $this->Shell->tasks = ['TestApple'];
  380. $this->assertTrue($this->Shell->loadTasks());
  381. $this->assertInstanceOf('Cake\Shell\Task\TestAppleTask', $this->Shell->TestApple);
  382. $this->Shell->tasks = 'TestBanana';
  383. $this->assertTrue($this->Shell->loadTasks());
  384. $this->assertInstanceOf('Cake\Shell\Task\TestAppleTask', $this->Shell->TestApple);
  385. $this->assertInstanceOf('Cake\Shell\Task\TestBananaTask', $this->Shell->TestBanana);
  386. unset($this->Shell->ShellTestApple, $this->Shell->TestBanana);
  387. $this->Shell->tasks = ['TestApple', 'TestBanana'];
  388. $this->assertTrue($this->Shell->loadTasks());
  389. $this->assertInstanceOf('Cake\Shell\Task\TestAppleTask', $this->Shell->TestApple);
  390. $this->assertInstanceOf('Cake\Shell\Task\TestBananaTask', $this->Shell->TestBanana);
  391. }
  392. /**
  393. * test that __get() makes args and params references
  394. *
  395. * @return void
  396. */
  397. public function testMagicGetArgAndParamReferences()
  398. {
  399. $this->Shell->tasks = ['TestApple'];
  400. $this->Shell->args = ['one'];
  401. $this->Shell->params = ['help' => false];
  402. $this->Shell->loadTasks();
  403. $result = $this->Shell->TestApple;
  404. $this->Shell->args = ['one', 'two'];
  405. $this->assertSame($this->Shell->args, $result->args);
  406. $this->assertSame($this->Shell->params, $result->params);
  407. }
  408. /**
  409. * testShortPath method
  410. *
  411. * @return void
  412. */
  413. public function testShortPath()
  414. {
  415. $path = $expected = DS . 'tmp/ab/cd';
  416. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  417. $path = $expected = DS . 'tmp/ab/cd/';
  418. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  419. $path = $expected = DS . 'tmp/ab/index.php';
  420. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  421. $path = DS . 'tmp/ab/' . DS . 'cd';
  422. $expected = DS . 'tmp/ab/cd';
  423. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  424. $path = 'tmp/ab';
  425. $expected = 'tmp/ab';
  426. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  427. $path = 'tmp/ab';
  428. $expected = 'tmp/ab';
  429. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  430. $path = APP;
  431. $result = $this->Shell->shortPath($path);
  432. $this->assertNotContains(ROOT, $result, 'Short paths should not contain ROOT');
  433. }
  434. /**
  435. * testCreateFile method
  436. *
  437. * @return void
  438. */
  439. public function testCreateFileNonInteractive()
  440. {
  441. $eol = PHP_EOL;
  442. $path = TMP . 'shell_test';
  443. $file = $path . DS . 'file1.php';
  444. new Folder($path, true);
  445. $contents = "<?php{$eol}echo 'test';${eol}\$te = 'st';{$eol}";
  446. $this->Shell->interactive = false;
  447. $result = $this->Shell->createFile($file, $contents);
  448. $this->assertTrue($result);
  449. $this->assertFileExists($file);
  450. $this->assertStringEqualsFile($file, $contents);
  451. }
  452. /**
  453. * Test that while in non interactive mode it will not overwrite files by default.
  454. *
  455. * @return void
  456. */
  457. public function testCreateFileNonInteractiveFileExists()
  458. {
  459. $eol = PHP_EOL;
  460. $path = TMP . 'shell_test';
  461. $file = $path . DS . 'file1.php';
  462. if (!is_dir($path)) {
  463. mkdir($path, 0770, true);
  464. }
  465. touch($file);
  466. $this->assertFileExists($file);
  467. new Folder($path, true);
  468. $contents = "<?php{$eol}echo 'test';${eol}\$te = 'st';{$eol}";
  469. $this->Shell->interactive = false;
  470. $result = $this->Shell->createFile($file, $contents);
  471. $this->assertFalse($result);
  472. }
  473. /**
  474. * Test that files are not changed with a 'n' reply.
  475. *
  476. * @return void
  477. */
  478. public function testCreateFileNoReply()
  479. {
  480. $eol = PHP_EOL;
  481. $path = TMP . 'shell_test';
  482. $file = $path . DS . 'file1.php';
  483. new Folder($path, true);
  484. $this->io->expects($this->once())
  485. ->method('askChoice')
  486. ->will($this->returnValue('n'));
  487. touch($file);
  488. $this->assertFileExists($file);
  489. $contents = 'My content';
  490. $result = $this->Shell->createFile($file, $contents);
  491. $this->assertFileExists($file);
  492. $this->assertTextEquals('', file_get_contents($file));
  493. $this->assertFalse($result, 'Did not create file.');
  494. }
  495. /**
  496. * Test that files are changed with a 'y' reply.
  497. *
  498. * @return void
  499. */
  500. public function testCreateFileOverwrite()
  501. {
  502. $eol = PHP_EOL;
  503. $path = TMP . 'shell_test';
  504. $file = $path . DS . 'file1.php';
  505. new Folder($path, true);
  506. $this->io->expects($this->once())
  507. ->method('askChoice')
  508. ->will($this->returnValue('y'));
  509. touch($file);
  510. $this->assertFileExists($file);
  511. $contents = 'My content';
  512. $result = $this->Shell->createFile($file, $contents);
  513. $this->assertFileExists($file);
  514. $this->assertTextEquals($contents, file_get_contents($file));
  515. $this->assertTrue($result, 'Did create file.');
  516. }
  517. /**
  518. * Test that there is no user prompt in non-interactive mode while file already exists
  519. * and if force mode is explicitly enabled.
  520. *
  521. * @return void
  522. */
  523. public function testCreateFileOverwriteNonInteractive()
  524. {
  525. $path = TMP . 'shell_test';
  526. $file = $path . DS . 'file1.php';
  527. new Folder($path, true);
  528. touch($file);
  529. $this->assertFileExists($file);
  530. $this->io->expects($this->never())->method('askChoice');
  531. $this->Shell->params['force'] = true;
  532. $this->Shell->interactive = false;
  533. $result = $this->Shell->createFile($file, 'My content');
  534. $this->assertTrue($result);
  535. $this->assertStringEqualsFile($file, 'My content');
  536. }
  537. /**
  538. * Test that all files are changed with a 'a' reply.
  539. *
  540. * @return void
  541. */
  542. public function testCreateFileOverwriteAll()
  543. {
  544. $eol = PHP_EOL;
  545. $path = TMP . 'shell_test';
  546. $files = [
  547. $path . DS . 'file1.php' => 'My first content',
  548. $path . DS . 'file2.php' => 'My second content',
  549. $path . DS . 'file3.php' => 'My third content'
  550. ];
  551. new Folder($path, true);
  552. $this->io->expects($this->once())
  553. ->method('askChoice')
  554. ->will($this->returnValue('a'));
  555. foreach ($files as $file => $contents) {
  556. touch($file);
  557. $this->assertFileExists($file);
  558. $result = $this->Shell->createFile($file, $contents);
  559. $this->assertFileExists($file);
  560. $this->assertTextEquals($contents, file_get_contents($file));
  561. $this->assertTrue($result, 'Did create file.');
  562. }
  563. }
  564. /**
  565. * Test that you can't create files that aren't writable.
  566. *
  567. * @return void
  568. */
  569. public function testCreateFileNoPermissions()
  570. {
  571. $this->skipIf(DS === '\\', 'Cant perform operations using permissions on windows.');
  572. $path = TMP . 'shell_test';
  573. $file = $path . DS . 'no_perms';
  574. if (!is_dir($path)) {
  575. mkdir($path);
  576. }
  577. chmod($path, 0444);
  578. $this->Shell->createFile($file, 'testing');
  579. $this->assertFileNotExists($file);
  580. chmod($path, 0744);
  581. rmdir($path);
  582. }
  583. /**
  584. * test hasTask method
  585. *
  586. * @return void
  587. */
  588. public function testHasTask()
  589. {
  590. $this->Shell->tasks = ['Extract', 'Assets'];
  591. $this->Shell->loadTasks();
  592. $this->assertTrue($this->Shell->hasTask('extract'));
  593. $this->assertTrue($this->Shell->hasTask('Extract'));
  594. $this->assertFalse($this->Shell->hasTask('random'));
  595. $this->assertTrue($this->Shell->hasTask('assets'));
  596. $this->assertTrue($this->Shell->hasTask('Assets'));
  597. }
  598. /**
  599. * test task loading exception
  600. *
  601. * @expectedException \RuntimeException
  602. * @expectedExceptionMessage Task `DoesNotExist` not found. Maybe you made a typo or a plugin is missing or not loaded?
  603. * @return void
  604. */
  605. public function testMissingTaskException()
  606. {
  607. $this->Shell->tasks = ['DoesNotExist'];
  608. $this->Shell->loadTasks();
  609. }
  610. /**
  611. * test the hasMethod
  612. *
  613. * @return void
  614. */
  615. public function testHasMethod()
  616. {
  617. $this->assertTrue($this->Shell->hasMethod('doSomething'));
  618. $this->assertFalse($this->Shell->hasMethod('hr'), 'hr is callable');
  619. $this->assertFalse($this->Shell->hasMethod('_secret'), '_secret is callable');
  620. $this->assertFalse($this->Shell->hasMethod('no_access'), 'no_access is callable');
  621. }
  622. /**
  623. * test run command calling main.
  624. *
  625. * @return void
  626. */
  627. public function testRunCommandMain()
  628. {
  629. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  630. $shell = $this->getMockBuilder('Cake\Console\Shell')
  631. ->setMethods(['main', 'startup'])
  632. ->setConstructorArgs([$io])
  633. ->getMock();
  634. $shell->expects($this->once())->method('startup');
  635. $shell->expects($this->once())->method('main')
  636. ->with('cakes')
  637. ->will($this->returnValue(true));
  638. $result = $shell->runCommand(['cakes', '--verbose']);
  639. $this->assertTrue($result);
  640. $this->assertEquals('main', $shell->command);
  641. }
  642. /**
  643. * test run command calling a real method with no subcommands defined.
  644. *
  645. * @return void
  646. */
  647. public function testRunCommandWithMethod()
  648. {
  649. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  650. $shell = $this->getMockBuilder('Cake\Console\Shell')
  651. ->setMethods(['hitMe', 'startup'])
  652. ->setConstructorArgs([$io])
  653. ->getMock();
  654. $shell->expects($this->once())->method('startup');
  655. $shell->expects($this->once())->method('hitMe')
  656. ->with('cakes')
  657. ->will($this->returnValue(true));
  658. $result = $shell->runCommand(['hit_me', 'cakes', '--verbose'], true);
  659. $this->assertTrue($result);
  660. $this->assertEquals('hit_me', $shell->command);
  661. }
  662. /**
  663. * test that a command called with an extra parameter passed merges the extra parameters
  664. * to the shell's one
  665. * Also tests that if an extra `requested` parameter prevents the welcome message from
  666. * being displayed
  667. *
  668. * @return void
  669. */
  670. public function testRunCommandWithExtra()
  671. {
  672. $Parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  673. ->setMethods(['help'])
  674. ->setConstructorArgs(['knife'])
  675. ->getMock();
  676. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  677. $Shell = $this->getMockBuilder('Cake\Console\Shell')
  678. ->setMethods(['getOptionParser', 'slice', '_welcome', 'param'])
  679. ->setConstructorArgs([$io])
  680. ->getMock();
  681. $Parser->addSubCommand('slice');
  682. $Shell->expects($this->once())
  683. ->method('getOptionParser')
  684. ->will($this->returnValue($Parser));
  685. $Shell->expects($this->once())
  686. ->method('slice')
  687. ->with('cakes');
  688. $Shell->expects($this->never())->method('_welcome');
  689. $Shell->expects($this->once())->method('param')
  690. ->with('requested')
  691. ->will($this->returnValue(true));
  692. $Shell->runCommand(['slice', 'cakes'], false, ['requested' => true]);
  693. }
  694. /**
  695. * Test the dispatchShell() arguments parser
  696. *
  697. * @return void
  698. */
  699. public function testDispatchShellArgsParser()
  700. {
  701. $Shell = new Shell();
  702. $expected = [['schema', 'create', 'DbAcl'], []];
  703. // Shell::dispatchShell('schema create DbAcl');
  704. $result = $Shell->parseDispatchArguments(['schema create DbAcl']);
  705. $this->assertEquals($expected, $result);
  706. // Shell::dispatchShell('schema', 'create', 'DbAcl');
  707. $result = $Shell->parseDispatchArguments(['schema', 'create', 'DbAcl']);
  708. $this->assertEquals($expected, $result);
  709. // Shell::dispatchShell(['command' => 'schema create DbAcl']);
  710. $result = $Shell->parseDispatchArguments([[
  711. 'command' => 'schema create DbAcl'
  712. ]]);
  713. $this->assertEquals($expected, $result);
  714. // Shell::dispatchShell(['command' => ['schema', 'create', 'DbAcl']]);
  715. $result = $Shell->parseDispatchArguments([[
  716. 'command' => ['schema', 'create', 'DbAcl']
  717. ]]);
  718. $this->assertEquals($expected, $result);
  719. $expected[1] = ['param' => 'value'];
  720. // Shell::dispatchShell(['command' => 'schema create DbAcl', 'extra' => ['param' => 'value']]);
  721. $result = $Shell->parseDispatchArguments([[
  722. 'command' => 'schema create DbAcl',
  723. 'extra' => ['param' => 'value']
  724. ]]);
  725. $this->assertEquals($expected, $result);
  726. // Shell::dispatchShell(['command' => ['schema', 'create', 'DbAcl'], 'extra' => ['param' => 'value']]);
  727. $result = $Shell->parseDispatchArguments([[
  728. 'command' => ['schema', 'create', 'DbAcl'],
  729. 'extra' => ['param' => 'value']
  730. ]]);
  731. $this->assertEquals($expected, $result);
  732. }
  733. /**
  734. * test calling a shell that dispatch another one
  735. *
  736. * @return void
  737. */
  738. public function testDispatchShell()
  739. {
  740. $Shell = new TestingDispatchShell();
  741. ob_start();
  742. $Shell->runCommand(['test_task'], true);
  743. $result = ob_get_clean();
  744. $expected = <<<TEXT
  745. <info>Welcome to CakePHP Console</info>
  746. I am a test task, I dispatch another Shell
  747. I am a dispatched Shell
  748. TEXT;
  749. $this->assertEquals($expected, $result);
  750. ob_start();
  751. $Shell->runCommand(['test_task_dispatch_array'], true);
  752. $result = ob_get_clean();
  753. $this->assertEquals($expected, $result);
  754. ob_start();
  755. $Shell->runCommand(['test_task_dispatch_command_string'], true);
  756. $result = ob_get_clean();
  757. $this->assertEquals($expected, $result);
  758. ob_start();
  759. $Shell->runCommand(['test_task_dispatch_command_array'], true);
  760. $result = ob_get_clean();
  761. $this->assertEquals($expected, $result);
  762. $expected = <<<TEXT
  763. <info>Welcome to CakePHP Console</info>
  764. I am a test task, I dispatch another Shell
  765. I am a dispatched Shell. My param `foo` has the value `bar`
  766. TEXT;
  767. ob_start();
  768. $Shell->runCommand(['test_task_dispatch_with_param'], true);
  769. $result = ob_get_clean();
  770. $this->assertEquals($expected, $result);
  771. $expected = <<<TEXT
  772. <info>Welcome to CakePHP Console</info>
  773. I am a test task, I dispatch another Shell
  774. I am a dispatched Shell. My param `foo` has the value `bar`
  775. My param `fooz` has the value `baz`
  776. TEXT;
  777. ob_start();
  778. $Shell->runCommand(['test_task_dispatch_with_multiple_params'], true);
  779. $result = ob_get_clean();
  780. $this->assertEquals($expected, $result);
  781. $expected = <<<TEXT
  782. <info>Welcome to CakePHP Console</info>
  783. I am a test task, I dispatch another Shell
  784. <info>Welcome to CakePHP Console</info>
  785. I am a dispatched Shell
  786. TEXT;
  787. ob_start();
  788. $Shell->runCommand(['test_task_dispatch_with_requested_off'], true);
  789. $result = ob_get_clean();
  790. $this->assertEquals($expected, $result);
  791. }
  792. /**
  793. * Test that runCommand() doesn't call public methods when the second arg is false.
  794. *
  795. * @return void
  796. */
  797. public function testRunCommandAutoMethodOff()
  798. {
  799. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  800. $shell = $this->getMockBuilder('Cake\Console\Shell')
  801. ->setMethods(['hit_me', 'startup'])
  802. ->setConstructorArgs([$io])
  803. ->getMock();
  804. $shell->expects($this->never())->method('startup');
  805. $shell->expects($this->never())->method('hit_me');
  806. $result = $shell->runCommand(['hit_me', 'baseball'], false);
  807. $this->assertFalse($result);
  808. $result = $shell->runCommand(['hit_me', 'baseball']);
  809. $this->assertFalse($result, 'Default value of runCommand() should be false');
  810. }
  811. /**
  812. * test run command calling a real method with mismatching subcommands defined.
  813. *
  814. * @return void
  815. */
  816. public function testRunCommandWithMethodNotInSubcommands()
  817. {
  818. $parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  819. ->setMethods(['help'])
  820. ->setConstructorArgs(['knife'])
  821. ->getMock();
  822. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  823. $shell = $this->getMockBuilder('Cake\Console\Shell')
  824. ->setMethods(['getOptionParser', 'roll', 'startup'])
  825. ->setConstructorArgs([$io])
  826. ->getMock();
  827. $parser->addSubCommand('slice');
  828. $shell->expects($this->any())
  829. ->method('getOptionParser')
  830. ->will($this->returnValue($parser));
  831. $parser->expects($this->once())
  832. ->method('help');
  833. $shell->expects($this->never())->method('startup');
  834. $shell->expects($this->never())->method('roll');
  835. $result = $shell->runCommand(['roll', 'cakes', '--verbose']);
  836. $this->assertFalse($result);
  837. }
  838. /**
  839. * test run command calling a real method with subcommands defined.
  840. *
  841. * @return void
  842. */
  843. public function testRunCommandWithMethodInSubcommands()
  844. {
  845. $parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  846. ->setMethods(['help'])
  847. ->setConstructorArgs(['knife'])
  848. ->getMock();
  849. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  850. $shell = $this->getMockBuilder('Cake\Console\Shell')
  851. ->setMethods(['getOptionParser', 'slice', 'startup'])
  852. ->setConstructorArgs([$io])
  853. ->getMock();
  854. $parser->addSubCommand('slice');
  855. $shell->expects($this->any())
  856. ->method('getOptionParser')
  857. ->will($this->returnValue($parser));
  858. $shell->expects($this->once())->method('startup');
  859. $shell->expects($this->once())
  860. ->method('slice')
  861. ->with('cakes');
  862. $shell->runCommand(['slice', 'cakes', '--verbose']);
  863. }
  864. /**
  865. * test run command calling a missing method with subcommands defined.
  866. *
  867. * @return void
  868. */
  869. public function testRunCommandWithMissingMethodInSubcommands()
  870. {
  871. /** @var \Cake\Console\ConsoleOptionParser|\PHPUnit\Framework\MockObject\MockObject $parser */
  872. $parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  873. ->setMethods(['help'])
  874. ->setConstructorArgs(['knife'])
  875. ->getMock();
  876. $parser->addSubCommand('slice');
  877. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  878. /** @var \Cake\Console\Shell|\PHPUnit\Framework\MockObject\MockObject $shell */
  879. $shell = $this->getMockBuilder('Cake\Console\Shell')
  880. ->setMethods(['getOptionParser', 'startup'])
  881. ->setConstructorArgs([$io])
  882. ->getMock();
  883. $shell->expects($this->any())
  884. ->method('getOptionParser')
  885. ->will($this->returnValue($parser));
  886. $shell->expects($this->never())
  887. ->method('startup');
  888. $parser->expects($this->once())
  889. ->method('help');
  890. $shell->runCommand(['slice', 'cakes', '--verbose']);
  891. }
  892. /**
  893. * test run command causing exception on Shell method.
  894. *
  895. * @return void
  896. */
  897. public function testRunCommandBaseClassMethod()
  898. {
  899. $shell = $this->getMockBuilder('Cake\Console\Shell')
  900. ->setMethods(['startup', 'getOptionParser', 'hr'])
  901. ->disableOriginalConstructor()
  902. ->getMock();
  903. $shell->setIo(
  904. $this->getMockBuilder('Cake\Console\ConsoleIo')
  905. ->setMethods(['err'])
  906. ->getMock()
  907. );
  908. $parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  909. ->disableOriginalConstructor()
  910. ->getMock();
  911. $parser->expects($this->once())->method('help');
  912. $shell->expects($this->once())->method('getOptionParser')
  913. ->will($this->returnValue($parser));
  914. $shell->expects($this->never())->method('hr');
  915. $shell->_io->expects($this->exactly(2))->method('err');
  916. $shell->runCommand(['hr']);
  917. }
  918. /**
  919. * test run command causing exception on Shell method.
  920. *
  921. * @return void
  922. */
  923. public function testRunCommandMissingMethod()
  924. {
  925. $shell = $this->getMockBuilder('Cake\Console\Shell')
  926. ->setMethods(['startup', 'getOptionParser', 'hr'])
  927. ->disableOriginalConstructor()
  928. ->getMock();
  929. $shell->setIo(
  930. $this->getMockBuilder('Cake\Console\ConsoleIo')
  931. ->setMethods(['err'])
  932. ->getMock()
  933. );
  934. $parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  935. ->disableOriginalConstructor()
  936. ->getMock();
  937. $parser->expects($this->once())->method('help');
  938. $shell->expects($this->once())->method('getOptionParser')
  939. ->will($this->returnValue($parser));
  940. $shell->_io->expects($this->exactly(2))->method('err');
  941. $result = $shell->runCommand(['idontexist']);
  942. $this->assertFalse($result);
  943. }
  944. /**
  945. * test that a --help causes help to show.
  946. *
  947. * @return void
  948. */
  949. public function testRunCommandTriggeringHelp()
  950. {
  951. $parser = $this->getMockBuilder('Cake\Console\ConsoleOptionParser')
  952. ->disableOriginalConstructor()
  953. ->getMock();
  954. $parser->expects($this->once())->method('parse')
  955. ->with(['--help'])
  956. ->will($this->returnValue([['help' => true], []]));
  957. $parser->expects($this->once())->method('help');
  958. $shell = $this->getMockBuilder('Cake\Console\Shell')
  959. ->setMethods(['getOptionParser', 'out', 'startup', '_welcome'])
  960. ->disableOriginalConstructor()
  961. ->getMock();
  962. $shell->setIo($this->getMockBuilder('Cake\Console\ConsoleIo')->getMock());
  963. $shell->expects($this->once())->method('getOptionParser')
  964. ->will($this->returnValue($parser));
  965. $shell->expects($this->once())->method('out');
  966. $shell->runCommand(['--help']);
  967. }
  968. /**
  969. * test that runCommand will not call runCommand on tasks that are not subcommands.
  970. *
  971. * @return void
  972. */
  973. public function testRunCommandNotCallUnexposedTask()
  974. {
  975. $shell = $this->getMockBuilder('Cake\Console\Shell')
  976. ->setMethods(['startup', 'hasTask'])
  977. ->disableOriginalConstructor()
  978. ->getMock();
  979. $shell->setIo(
  980. $this->getMockBuilder('Cake\Console\ConsoleIo')
  981. ->setMethods(['err'])
  982. ->getMock()
  983. );
  984. $task = $this->getMockBuilder('Cake\Console\Shell')
  985. ->setMethods(['runCommand'])
  986. ->disableOriginalConstructor()
  987. ->getMock();
  988. $task->expects($this->never())
  989. ->method('runCommand');
  990. $shell->expects($this->any())
  991. ->method('hasTask')
  992. ->will($this->returnValue(true));
  993. $shell->expects($this->never())->method('startup');
  994. $shell->_io->expects($this->exactly(2))->method('err');
  995. $shell->RunCommand = $task;
  996. $result = $shell->runCommand(['run_command', 'one']);
  997. $this->assertFalse($result);
  998. }
  999. /**
  1000. * test that runCommand will call runCommand on the task.
  1001. *
  1002. * @return void
  1003. */
  1004. public function testRunCommandHittingTaskInSubcommand()
  1005. {
  1006. $parser = new ConsoleOptionParser('knife');
  1007. $parser->addSubcommand('slice');
  1008. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  1009. $shell = $this->getMockBuilder('Cake\Console\Shell')
  1010. ->setMethods(['hasTask', 'startup', 'getOptionParser'])
  1011. ->disableOriginalConstructor()
  1012. ->getMock();
  1013. $shell->setIo($io);
  1014. $task = $this->getMockBuilder('Cake\Console\Shell')
  1015. ->setMethods(['main', 'runCommand'])
  1016. ->disableOriginalConstructor()
  1017. ->getMock();
  1018. $task->setIo($io);
  1019. $task->expects($this->once())
  1020. ->method('runCommand')
  1021. ->with(['one'], false, ['requested' => true]);
  1022. $shell->expects($this->once())->method('getOptionParser')
  1023. ->will($this->returnValue($parser));
  1024. $shell->expects($this->once())->method('startup');
  1025. $shell->expects($this->any())
  1026. ->method('hasTask')
  1027. ->will($this->returnValue(true));
  1028. $shell->Slice = $task;
  1029. $shell->runCommand(['slice', 'one']);
  1030. }
  1031. /**
  1032. * test that runCommand will invoke a task
  1033. *
  1034. * @return void
  1035. */
  1036. public function testRunCommandInvokeTask()
  1037. {
  1038. $parser = new ConsoleOptionParser('knife');
  1039. $parser->addSubcommand('slice');
  1040. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  1041. $shell = $this->getMockBuilder('Cake\Console\Shell')
  1042. ->setMethods(['hasTask', 'getOptionParser'])
  1043. ->setConstructorArgs([$io])
  1044. ->getMock();
  1045. $task = $this->getMockBuilder('Cake\Console\Shell')
  1046. ->setMethods(['main', '_welcome'])
  1047. ->setConstructorArgs([$io])
  1048. ->getMock();
  1049. $shell->expects($this->once())
  1050. ->method('getOptionParser')
  1051. ->will($this->returnValue($parser));
  1052. $shell->expects($this->any())
  1053. ->method('hasTask')
  1054. ->will($this->returnValue(true));
  1055. $task->expects($this->never())
  1056. ->method('_welcome');
  1057. $shell->Slice = $task;
  1058. $shell->runCommand(['slice', 'one']);
  1059. $this->assertTrue($task->params['requested'], 'Task is requested, no welcome.');
  1060. }
  1061. /**
  1062. * test run command missing parameters
  1063. *
  1064. * @return void
  1065. */
  1066. public function testRunCommandMainMissingArgument()
  1067. {
  1068. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')->getMock();
  1069. $shell = $this->getMockBuilder('Cake\Console\Shell')
  1070. ->setMethods(['main', 'startup', 'getOptionParser'])
  1071. ->setConstructorArgs([$io])
  1072. ->getMock();
  1073. $parser = new ConsoleOptionParser('test');
  1074. $parser->addArgument('filename', [
  1075. 'required' => true,
  1076. 'help' => 'a file',
  1077. ]);
  1078. $shell->expects($this->once())
  1079. ->method('getOptionParser')
  1080. ->will($this->returnValue($parser));
  1081. $shell->expects($this->never())->method('main');
  1082. $io->expects($this->once())
  1083. ->method('error')
  1084. ->with('Error: Missing required arguments. filename is required.');
  1085. $result = $shell->runCommand([]);
  1086. $this->assertFalse($result, 'Shell should fail');
  1087. }
  1088. /**
  1089. * test wrapBlock wrapping text.
  1090. *
  1091. * @return void
  1092. */
  1093. public function testWrapText()
  1094. {
  1095. $text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
  1096. $result = $this->Shell->wrapText($text, ['width' => 33]);
  1097. $expected = <<<TEXT
  1098. This is the song that never ends.
  1099. This is the song that never ends.
  1100. This is the song that never ends.
  1101. TEXT;
  1102. $this->assertTextEquals($expected, $result, 'Text not wrapped.');
  1103. $result = $this->Shell->wrapText($text, ['indent' => ' ', 'width' => 33]);
  1104. $expected = <<<TEXT
  1105. This is the song that never ends.
  1106. This is the song that never ends.
  1107. This is the song that never ends.
  1108. TEXT;
  1109. $this->assertTextEquals($expected, $result, 'Text not wrapped.');
  1110. }
  1111. /**
  1112. * Testing camel cased naming of tasks
  1113. *
  1114. * @return void
  1115. */
  1116. public function testShellNaming()
  1117. {
  1118. $this->Shell->tasks = ['TestApple'];
  1119. $this->Shell->loadTasks();
  1120. $expected = 'TestApple';
  1121. $this->assertEquals($expected, $this->Shell->TestApple->name);
  1122. }
  1123. /**
  1124. * Test reading params
  1125. *
  1126. * @dataProvider paramReadingDataProvider
  1127. */
  1128. public function testParamReading($toRead, $expected)
  1129. {
  1130. $this->Shell->params = [
  1131. 'key' => 'value',
  1132. 'help' => false,
  1133. 'emptykey' => '',
  1134. 'truthy' => true
  1135. ];
  1136. $this->assertSame($expected, $this->Shell->param($toRead));
  1137. }
  1138. /**
  1139. * Data provider for testing reading values with Shell::param()
  1140. *
  1141. * @return array
  1142. */
  1143. public function paramReadingDataProvider()
  1144. {
  1145. return [
  1146. [
  1147. 'key',
  1148. 'value',
  1149. ],
  1150. [
  1151. 'help',
  1152. false,
  1153. ],
  1154. [
  1155. 'emptykey',
  1156. '',
  1157. ],
  1158. [
  1159. 'truthy',
  1160. true,
  1161. ],
  1162. [
  1163. 'does_not_exist',
  1164. null,
  1165. ]
  1166. ];
  1167. }
  1168. /**
  1169. * Test that option parsers are created with the correct name/command.
  1170. *
  1171. * @return void
  1172. */
  1173. public function testGetOptionParser()
  1174. {
  1175. $this->Shell->name = 'test';
  1176. $this->Shell->plugin = 'plugin';
  1177. $parser = $this->Shell->getOptionParser();
  1178. $this->assertEquals('plugin.test', $parser->getCommand());
  1179. }
  1180. /**
  1181. * Test file and console and logging quiet output
  1182. *
  1183. * @return void
  1184. */
  1185. public function testQuietLog()
  1186. {
  1187. $io = $this->getMockBuilder('Cake\Console\ConsoleIo')
  1188. ->disableOriginalConstructor()
  1189. ->getMock();
  1190. $io->expects($this->once())
  1191. ->method('level')
  1192. ->with(Shell::QUIET);
  1193. $io->expects($this->at(0))
  1194. ->method('setLoggers')
  1195. ->with(true);
  1196. $io->expects($this->at(2))
  1197. ->method('setLoggers')
  1198. ->with(ConsoleIo::QUIET);
  1199. $this->Shell = $this->getMockBuilder(ShellTestShell::class)
  1200. ->setMethods(['welcome'])
  1201. ->setConstructorArgs([$io])
  1202. ->getMock();
  1203. $this->Shell->runCommand(['foo', '--quiet']);
  1204. }
  1205. /**
  1206. * Test getIo() and setIo() methods
  1207. *
  1208. * @return void
  1209. */
  1210. public function testGetSetIo()
  1211. {
  1212. $this->Shell->setIo($this->io);
  1213. $this->assertSame($this->Shell->getIo(), $this->io);
  1214. }
  1215. /**
  1216. * Test setRootName filters into the option parser help text.
  1217. *
  1218. * @return void
  1219. */
  1220. public function testSetRootNamePropagatesToHelpText()
  1221. {
  1222. $this->assertSame($this->Shell, $this->Shell->setRootName('tool'), 'is chainable');
  1223. $this->assertContains('tool shell_test_shell [-h]', $this->Shell->getOptionParser()->help());
  1224. }
  1225. /**
  1226. * Tests __debugInfo
  1227. *
  1228. * @return void
  1229. */
  1230. public function testDebugInfo()
  1231. {
  1232. $expected = [
  1233. 'name' => 'ShellTestShell',
  1234. 'plugin' => null,
  1235. 'command' => null,
  1236. 'tasks' => [],
  1237. 'params' => [],
  1238. 'args' => [],
  1239. 'interactive' => true
  1240. ];
  1241. $result = $this->Shell->__debugInfo();
  1242. $this->assertEquals($expected, $result);
  1243. }
  1244. }