ShellTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. <?php
  2. /**
  3. * CakePHP : 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 Project
  12. * @since 1.2.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Console;
  16. use Cake\Console\Shell;
  17. use Cake\Core\App;
  18. use Cake\Core\Configure;
  19. use Cake\Core\Plugin;
  20. use Cake\Log\Log;
  21. use Cake\TestSuite\TestCase;
  22. use Cake\Utility\Folder;
  23. use Cake\Utility\Hash;
  24. /**
  25. * Class for testing merging vars
  26. */
  27. class MergeShell extends Shell {
  28. public $tasks = array('DbConfig', 'Fixture');
  29. public $modelClass = 'Articles';
  30. }
  31. /**
  32. * ShellTestShell class
  33. *
  34. */
  35. class ShellTestShell extends Shell {
  36. /**
  37. * name property
  38. *
  39. * @var name
  40. */
  41. public $name = 'ShellTestShell';
  42. /**
  43. * stopped property
  44. *
  45. * @var integer
  46. */
  47. public $stopped;
  48. /**
  49. * testMessage property
  50. *
  51. * @var string
  52. */
  53. public $testMessage = 'all your base are belong to us';
  54. /**
  55. * stop method
  56. *
  57. * @param integer $status
  58. * @return void
  59. */
  60. protected function _stop($status = 0) {
  61. $this->stopped = $status;
  62. }
  63. protected function _secret() {
  64. }
  65. //@codingStandardsIgnoreStart
  66. public function do_something() {
  67. }
  68. protected function no_access() {
  69. }
  70. public function log_something() {
  71. $this->log($this->testMessage);
  72. }
  73. //@codingStandardsIgnoreEnd
  74. public function useLogger($enable = true) {
  75. $this->_useLogger($enable);
  76. }
  77. }
  78. /**
  79. * TestAppleTask class
  80. *
  81. */
  82. class TestAppleTask extends Shell {
  83. }
  84. /**
  85. * TestBananaTask class
  86. *
  87. */
  88. class TestBananaTask extends Shell {
  89. }
  90. class_alias(__NAMESPACE__ . '\TestAppleTask', 'Cake\Console\Command\Task\TestAppleTask');
  91. class_alias(__NAMESPACE__ . '\TestBananaTask', 'Cake\Console\Command\Task\TestBananaTask');
  92. /**
  93. * ShellTest class
  94. *
  95. */
  96. class ShellTest extends TestCase {
  97. /**
  98. * Fixtures used in this test case
  99. *
  100. * @var array
  101. */
  102. public $fixtures = array(
  103. 'core.post', 'core.comment', 'core.article', 'core.user',
  104. 'core.tag', 'core.articles_tag', 'core.attachment'
  105. );
  106. /**
  107. * setUp method
  108. *
  109. * @return void
  110. */
  111. public function setUp() {
  112. parent::setUp();
  113. $this->io = $this->getMock('Cake\Console\ConsoleIo', [], [], '', false);
  114. $this->Shell = new ShellTestShell($this->io);
  115. if (is_dir(TMP . 'shell_test')) {
  116. $Folder = new Folder(TMP . 'shell_test');
  117. $Folder->delete();
  118. }
  119. }
  120. /**
  121. * testConstruct method
  122. *
  123. * @return void
  124. */
  125. public function testConstruct() {
  126. $this->assertEquals('ShellTestShell', $this->Shell->name);
  127. $this->assertInstanceOf('Cake\Console\ConsoleIo', $this->Shell->io());
  128. }
  129. /**
  130. * testInitialize method
  131. *
  132. * @return void
  133. */
  134. public function testInitialize() {
  135. Configure::write('App.namespace', 'TestApp');
  136. Plugin::load('TestPlugin');
  137. $this->Shell->tasks = array('DbConfig' => array('one', 'two'));
  138. $this->Shell->plugin = 'TestPlugin';
  139. $this->Shell->modelClass = 'TestPluginComments';
  140. $this->Shell->initialize();
  141. $this->assertTrue(isset($this->Shell->TestPluginComments));
  142. $this->assertInstanceOf(
  143. 'TestPlugin\Model\Table\TestPluginCommentsTable',
  144. $this->Shell->TestPluginComments
  145. );
  146. }
  147. /**
  148. * test LoadModel method
  149. *
  150. * @return void
  151. */
  152. public function testLoadModel() {
  153. Configure::write('App.namespace', 'TestApp');
  154. $Shell = new MergeShell();
  155. $this->assertInstanceOf(
  156. 'TestApp\Model\Table\ArticlesTable',
  157. $Shell->Articles
  158. );
  159. $this->assertEquals('Articles', $Shell->modelClass);
  160. Plugin::load('TestPlugin');
  161. $this->Shell->loadModel('TestPlugin.TestPluginComments');
  162. $this->assertTrue(isset($this->Shell->TestPluginComments));
  163. $this->assertInstanceOf(
  164. 'TestPlugin\Model\Table\TestPluginCommentsTable',
  165. $this->Shell->TestPluginComments
  166. );
  167. }
  168. /**
  169. * testIn method
  170. *
  171. * @return void
  172. */
  173. public function testIn() {
  174. $this->io->expects($this->at(0))
  175. ->method('askChoice')
  176. ->with('Just a test?', ['y', 'n'], 'n')
  177. ->will($this->returnValue('n'));
  178. $this->io->expects($this->at(1))
  179. ->method('ask')
  180. ->with('Just a test?', 'n')
  181. ->will($this->returnValue('n'));
  182. $result = $this->Shell->in('Just a test?', array('y', 'n'), 'n');
  183. $this->assertEquals('n', $result);
  184. $result = $this->Shell->in('Just a test?', null, 'n');
  185. $this->assertEquals('n', $result);
  186. }
  187. /**
  188. * Test in() when not interactive.
  189. *
  190. * @return void
  191. */
  192. public function testInNonInteractive() {
  193. $this->io->expects($this->never())
  194. ->method('askChoice');
  195. $this->io->expects($this->never())
  196. ->method('ask');
  197. $this->Shell->interactive = false;
  198. $result = $this->Shell->in('Just a test?', 'y/n', 'n');
  199. $this->assertEquals('n', $result);
  200. }
  201. /**
  202. * testOut method
  203. *
  204. * @return void
  205. */
  206. public function testOut() {
  207. $this->io->expects($this->once())
  208. ->method('out')
  209. ->with('Just a test', 1);
  210. $this->Shell->out('Just a test');
  211. }
  212. /**
  213. * testErr method
  214. *
  215. * @return void
  216. */
  217. public function testErr() {
  218. $this->io->expects($this->once())
  219. ->method('err')
  220. ->with('Just a test', 1);
  221. $this->Shell->err('Just a test');
  222. }
  223. /**
  224. * testNl
  225. *
  226. * @return void
  227. */
  228. public function testNl() {
  229. $this->io->expects($this->once())
  230. ->method('nl')
  231. ->with(2);
  232. $this->Shell->nl(2);
  233. }
  234. /**
  235. * testHr
  236. *
  237. * @return void
  238. */
  239. public function testHr() {
  240. $this->io->expects($this->once())
  241. ->method('hr')
  242. ->with(2);
  243. $this->Shell->hr(2);
  244. }
  245. /**
  246. * testError
  247. *
  248. * @return void
  249. */
  250. public function testError() {
  251. $this->io->expects($this->at(0))
  252. ->method('err')
  253. ->with('<error>Error:</error> Foo Not Found');
  254. $this->io->expects($this->at(1))
  255. ->method('err')
  256. ->with("Searched all...");
  257. $this->Shell->error('Foo Not Found', 'Searched all...');
  258. $this->assertSame($this->Shell->stopped, 1);
  259. }
  260. /**
  261. * testLoadTasks method
  262. *
  263. * @return void
  264. */
  265. public function testLoadTasks() {
  266. $this->assertTrue($this->Shell->loadTasks());
  267. $this->Shell->tasks = null;
  268. $this->assertTrue($this->Shell->loadTasks());
  269. $this->Shell->tasks = false;
  270. $this->assertTrue($this->Shell->loadTasks());
  271. $this->Shell->tasks = true;
  272. $this->assertTrue($this->Shell->loadTasks());
  273. $this->Shell->tasks = array();
  274. $this->assertTrue($this->Shell->loadTasks());
  275. $this->Shell->tasks = array('TestApple');
  276. $this->assertTrue($this->Shell->loadTasks());
  277. $this->assertInstanceOf('Cake\Console\Command\Task\TestAppleTask', $this->Shell->TestApple);
  278. $this->Shell->tasks = 'TestBanana';
  279. $this->assertTrue($this->Shell->loadTasks());
  280. $this->assertInstanceOf('Cake\Console\Command\Task\TestAppleTask', $this->Shell->TestApple);
  281. $this->assertInstanceOf('Cake\Console\Command\Task\TestBananaTask', $this->Shell->TestBanana);
  282. unset($this->Shell->ShellTestApple, $this->Shell->TestBanana);
  283. $this->Shell->tasks = array('TestApple', 'TestBanana');
  284. $this->assertTrue($this->Shell->loadTasks());
  285. $this->assertInstanceOf('Cake\Console\Command\Task\TestAppleTask', $this->Shell->TestApple);
  286. $this->assertInstanceOf('Cake\Console\Command\Task\TestBananaTask', $this->Shell->TestBanana);
  287. }
  288. /**
  289. * test that __get() makes args and params references
  290. *
  291. * @return void
  292. */
  293. public function testMagicGetArgAndParamReferences() {
  294. $this->Shell->tasks = array('TestApple');
  295. $this->Shell->args = array('one');
  296. $this->Shell->params = array('help' => false);
  297. $this->Shell->loadTasks();
  298. $result = $this->Shell->TestApple;
  299. $this->Shell->args = array('one', 'two');
  300. $this->assertSame($this->Shell->args, $result->args);
  301. $this->assertSame($this->Shell->params, $result->params);
  302. }
  303. /**
  304. * testShortPath method
  305. *
  306. * @return void
  307. */
  308. public function testShortPath() {
  309. $path = $expected = DS . 'tmp/ab/cd';
  310. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  311. $path = $expected = DS . 'tmp/ab/cd/';
  312. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  313. $path = $expected = DS . 'tmp/ab/index.php';
  314. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  315. $path = DS . 'tmp/ab/' . DS . 'cd';
  316. $expected = DS . 'tmp/ab/cd';
  317. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  318. $path = 'tmp/ab';
  319. $expected = 'tmp/ab';
  320. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  321. $path = 'tmp/ab';
  322. $expected = 'tmp/ab';
  323. $this->assertPathEquals($expected, $this->Shell->shortPath($path));
  324. $path = APP;
  325. $result = $this->Shell->shortPath($path);
  326. $this->assertNotContains(ROOT, $result, 'Short paths should not contain ROOT');
  327. }
  328. /**
  329. * testCreateFile method
  330. *
  331. * @return void
  332. */
  333. public function testCreateFileNonInteractive() {
  334. $eol = PHP_EOL;
  335. $path = TMP . 'shell_test';
  336. $file = $path . DS . 'file1.php';
  337. new Folder($path, true);
  338. $contents = "<?php{$eol}echo 'test';${eol}\$te = 'st';{$eol}";
  339. $result = $this->Shell->createFile($file, $contents);
  340. $this->assertTrue($result);
  341. $this->assertTrue(file_exists($file));
  342. $this->assertEquals(file_get_contents($file), $contents);
  343. }
  344. /**
  345. * Test that files are not changed with a 'n' reply.
  346. *
  347. * @return void
  348. */
  349. public function testCreateFileNoReply() {
  350. $eol = PHP_EOL;
  351. $path = TMP . 'shell_test';
  352. $file = $path . DS . 'file1.php';
  353. new Folder($path, true);
  354. $this->io->expects($this->once())
  355. ->method('askChoice')
  356. ->will($this->returnValue('n'));
  357. touch($file);
  358. $this->assertTrue(file_exists($file));
  359. $contents = "My content";
  360. $result = $this->Shell->createFile($file, $contents);
  361. $this->assertTrue(file_exists($file));
  362. $this->assertTextEquals('', file_get_contents($file));
  363. $this->assertFalse($result, 'Did not create file.');
  364. }
  365. /**
  366. * Test that files are changed with a 'y' reply.
  367. *
  368. * @return void
  369. */
  370. public function testCreateFileOverwrite() {
  371. $eol = PHP_EOL;
  372. $path = TMP . 'shell_test';
  373. $file = $path . DS . 'file1.php';
  374. new Folder($path, true);
  375. $this->io->expects($this->once())
  376. ->method('askChoice')
  377. ->will($this->returnValue('y'));
  378. touch($file);
  379. $this->assertTrue(file_exists($file));
  380. $contents = "My content";
  381. $result = $this->Shell->createFile($file, $contents);
  382. $this->assertTrue(file_exists($file));
  383. $this->assertTextEquals($contents, file_get_contents($file));
  384. $this->assertTrue($result, 'Did create file.');
  385. }
  386. /**
  387. * Test that you can't create files that aren't writable.
  388. *
  389. * @return void
  390. */
  391. public function testCreateFileNoPermissions() {
  392. $this->skipIf(DIRECTORY_SEPARATOR === '\\', 'Cant perform operations using permissions on windows.');
  393. $path = TMP . 'shell_test';
  394. $file = $path . DS . 'no_perms';
  395. if (!is_dir($path)) {
  396. mkdir($path);
  397. }
  398. chmod($path, 0444);
  399. $this->Shell->createFile($file, 'testing');
  400. $this->assertFalse(file_exists($file));
  401. chmod($path, 0744);
  402. rmdir($path);
  403. }
  404. /**
  405. * test hasTask method
  406. *
  407. * @return void
  408. */
  409. public function testHasTask() {
  410. $this->Shell->tasks = array('Extract', 'DbConfig');
  411. $this->Shell->loadTasks();
  412. $this->assertTrue($this->Shell->hasTask('extract'));
  413. $this->assertTrue($this->Shell->hasTask('Extract'));
  414. $this->assertFalse($this->Shell->hasTask('random'));
  415. $this->assertTrue($this->Shell->hasTask('db_config'));
  416. $this->assertTrue($this->Shell->hasTask('DbConfig'));
  417. }
  418. /**
  419. * test the hasMethod
  420. *
  421. * @return void
  422. */
  423. public function testHasMethod() {
  424. $this->assertTrue($this->Shell->hasMethod('do_something'));
  425. $this->assertFalse($this->Shell->hasMethod('hr'), 'hr is callable');
  426. $this->assertFalse($this->Shell->hasMethod('_secret'), '_secret is callable');
  427. $this->assertFalse($this->Shell->hasMethod('no_access'), 'no_access is callable');
  428. }
  429. /**
  430. * test run command calling main.
  431. *
  432. * @return void
  433. */
  434. public function testRunCommandMain() {
  435. $Mock = $this->getMock('Cake\Console\Shell', array('main', 'startup'), array(), '', false);
  436. $Mock->expects($this->once())->method('main')->will($this->returnValue(true));
  437. $result = $Mock->runCommand(null, array());
  438. $this->assertTrue($result);
  439. }
  440. /**
  441. * test run command calling a legit method.
  442. *
  443. * @return void
  444. */
  445. public function testRunCommandWithMethod() {
  446. $Mock = $this->getMock('Cake\Console\Shell', array('hit_me', 'startup'), array(), '', false);
  447. $Mock->expects($this->once())->method('hit_me')->will($this->returnValue(true));
  448. $result = $Mock->runCommand('hit_me', array());
  449. $this->assertTrue($result);
  450. }
  451. /**
  452. * test run command causing exception on Shell method.
  453. *
  454. * @return void
  455. */
  456. public function testRunCommandBaseclassMethod() {
  457. $Mock = $this->getMock('Cake\Console\Shell', array('startup', 'getOptionParser', 'out'), array(), '', false);
  458. $Parser = $this->getMock('Cake\Console\ConsoleOptionParser', array(), array(), '', false);
  459. $Parser->expects($this->once())->method('help');
  460. $Mock->expects($this->once())->method('getOptionParser')
  461. ->will($this->returnValue($Parser));
  462. $Mock->expects($this->never())->method('hr');
  463. $Mock->expects($this->once())->method('out');
  464. $Mock->runCommand('hr', array());
  465. }
  466. /**
  467. * test run command causing exception on Shell method.
  468. *
  469. * @return void
  470. */
  471. public function testRunCommandMissingMethod() {
  472. $Mock = $this->getMock('Cake\Console\Shell', array('startup', 'getOptionParser', 'out'), array(), '', false);
  473. $Parser = $this->getMock('Cake\Console\ConsoleOptionParser', array(), array(), '', false);
  474. $Parser->expects($this->once())->method('help');
  475. $Mock->expects($this->never())->method('idontexist');
  476. $Mock->expects($this->once())->method('getOptionParser')
  477. ->will($this->returnValue($Parser));
  478. $Mock->expects($this->once())->method('out');
  479. $result = $Mock->runCommand('idontexist', array());
  480. $this->assertFalse($result);
  481. }
  482. /**
  483. * test that a --help causes help to show.
  484. *
  485. * @return void
  486. */
  487. public function testRunCommandTriggeringHelp() {
  488. $Parser = $this->getMock('Cake\Console\ConsoleOptionParser', array(), array(), '', false);
  489. $Parser->expects($this->once())->method('parse')
  490. ->with(array('--help'))
  491. ->will($this->returnValue(array(array('help' => true), array())));
  492. $Parser->expects($this->once())->method('help');
  493. $Shell = $this->getMock('Cake\Console\Shell', array('getOptionParser', 'out', 'startup', '_welcome'), array(), '', false);
  494. $Shell->expects($this->once())->method('getOptionParser')
  495. ->will($this->returnValue($Parser));
  496. $Shell->expects($this->once())->method('out');
  497. $Shell->runCommand(null, array('--help'));
  498. }
  499. /**
  500. * test that runCommand will call runCommand on the task.
  501. *
  502. * @return void
  503. */
  504. public function testRunCommandHittingTask() {
  505. $Shell = $this->getMock('Cake\Console\Shell', array('hasTask', 'startup'), array(), '', false);
  506. $task = $this->getMock('Cake\Console\Shell', array('execute', 'runCommand'), array(), '', false);
  507. $task->expects($this->any())
  508. ->method('runCommand')
  509. ->with('execute', array('one', 'value'));
  510. $Shell->expects($this->once())->method('startup');
  511. $Shell->expects($this->any())
  512. ->method('hasTask')
  513. ->will($this->returnValue(true));
  514. $Shell->RunCommand = $task;
  515. $Shell->runCommand('run_command', array('run_command', 'one', 'value'));
  516. }
  517. /**
  518. * test wrapBlock wrapping text.
  519. *
  520. * @return void
  521. */
  522. public function testWrapText() {
  523. $text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
  524. $result = $this->Shell->wrapText($text, 33);
  525. $expected = <<<TEXT
  526. This is the song that never ends.
  527. This is the song that never ends.
  528. This is the song that never ends.
  529. TEXT;
  530. $this->assertTextEquals($expected, $result, 'Text not wrapped.');
  531. $result = $this->Shell->wrapText($text, array('indent' => ' ', 'width' => 33));
  532. $expected = <<<TEXT
  533. This is the song that never ends.
  534. This is the song that never ends.
  535. This is the song that never ends.
  536. TEXT;
  537. $this->assertTextEquals($expected, $result, 'Text not wrapped.');
  538. }
  539. /**
  540. * Testing camel cased naming of tasks
  541. *
  542. * @return void
  543. */
  544. public function testShellNaming() {
  545. $this->Shell->tasks = array('TestApple');
  546. $this->Shell->loadTasks();
  547. $expected = 'TestApple';
  548. $this->assertEquals($expected, $this->Shell->TestApple->name);
  549. }
  550. /**
  551. * Test that option parsers are created with the correct name/command.
  552. *
  553. * @return void
  554. */
  555. public function testGetOptionParser() {
  556. $this->Shell->name = 'test';
  557. $this->Shell->plugin = 'plugin';
  558. $parser = $this->Shell->getOptionParser();
  559. $this->assertEquals('plugin.test', $parser->command());
  560. }
  561. /**
  562. * Test file and console and logging quiet output
  563. *
  564. * @return void
  565. */
  566. public function testQuietLog() {
  567. $io = $this->getMock('Cake\Console\ConsoleIo', [], [], '', false);
  568. $io->expects($this->once())
  569. ->method('level')
  570. ->with(Shell::QUIET);
  571. $io->expects($this->at(0))
  572. ->method('setLoggers')
  573. ->with(true);
  574. $io->expects($this->at(2))
  575. ->method('setLoggers')
  576. ->with(false);
  577. $this->Shell = $this->getMock(__NAMESPACE__ . '\ShellTestShell', array('_useLogger'), array($io));
  578. $this->Shell->runCommand('foo', array('--quiet'));
  579. }
  580. }