ConsoleOptionParserTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. <?php
  2. /**
  3. * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
  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://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
  12. * @since 2.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Console;
  16. use Cake\Console\ConsoleInputArgument;
  17. use Cake\Console\ConsoleInputOption;
  18. use Cake\Console\ConsoleInputSubcommand;
  19. use Cake\Console\ConsoleOptionParser;
  20. use Cake\TestSuite\TestCase;
  21. /**
  22. * Class ConsoleOptionParserTest
  23. *
  24. */
  25. class ConsoleOptionParserTest extends TestCase {
  26. /**
  27. * test setting the console description
  28. *
  29. * @return void
  30. */
  31. public function testDescription() {
  32. $parser = new ConsoleOptionParser('test', false);
  33. $result = $parser->description('A test');
  34. $this->assertEquals($parser, $result, 'Setting description is not chainable');
  35. $this->assertEquals('A test', $parser->description(), 'getting value is wrong.');
  36. $result = $parser->description(array('A test', 'something'));
  37. $this->assertEquals("A test\nsomething", $parser->description(), 'getting value is wrong.');
  38. }
  39. /**
  40. * test setting the console epilog
  41. *
  42. * @return void
  43. */
  44. public function testEpilog() {
  45. $parser = new ConsoleOptionParser('test', false);
  46. $result = $parser->epilog('A test');
  47. $this->assertEquals($parser, $result, 'Setting epilog is not chainable');
  48. $this->assertEquals('A test', $parser->epilog(), 'getting value is wrong.');
  49. $result = $parser->epilog(array('A test', 'something'));
  50. $this->assertEquals("A test\nsomething", $parser->epilog(), 'getting value is wrong.');
  51. }
  52. /**
  53. * test adding an option returns self.
  54. *
  55. * @return void
  56. */
  57. public function testAddOptionReturnSelf() {
  58. $parser = new ConsoleOptionParser('test', false);
  59. $result = $parser->addOption('test');
  60. $this->assertEquals($parser, $result, 'Did not return $this from addOption');
  61. }
  62. /**
  63. * test removing an option
  64. *
  65. * @return void
  66. */
  67. public function testRemoveOption() {
  68. $parser = new ConsoleOptionParser('test', false);
  69. $result = $parser->addOption('test')
  70. ->removeOption('test')
  71. ->removeOption('help');
  72. $this->assertSame($parser, $result, 'Did not return $this from removeOption');
  73. $this->assertEquals([], $result->options());
  74. }
  75. /**
  76. * test adding an option and using the long value for parsing.
  77. *
  78. * @return void
  79. */
  80. public function testAddOptionLong() {
  81. $parser = new ConsoleOptionParser('test', false);
  82. $parser->addOption('test', array(
  83. 'short' => 't'
  84. ));
  85. $result = $parser->parse(array('--test', 'value'));
  86. $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
  87. }
  88. /**
  89. * test adding an option with a zero value
  90. *
  91. * @return void
  92. */
  93. public function testAddOptionZero() {
  94. $parser = new ConsoleOptionParser('test', false);
  95. $parser->addOption('count', array());
  96. $result = $parser->parse(array('--count', '0'));
  97. $this->assertEquals(array('count' => '0', 'help' => false), $result[0], 'Zero parameter did not parse out');
  98. }
  99. /**
  100. * test addOption with an object.
  101. *
  102. * @return void
  103. */
  104. public function testAddOptionObject() {
  105. $parser = new ConsoleOptionParser('test', false);
  106. $parser->addOption(new ConsoleInputOption('test', 't'));
  107. $result = $parser->parse(array('--test=value'));
  108. $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
  109. }
  110. /**
  111. * test adding an option and using the long value for parsing.
  112. *
  113. * @return void
  114. */
  115. public function testAddOptionLongEquals() {
  116. $parser = new ConsoleOptionParser('test', false);
  117. $parser->addOption('test', array(
  118. 'short' => 't'
  119. ));
  120. $result = $parser->parse(array('--test=value'));
  121. $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
  122. }
  123. /**
  124. * test adding an option and using the default.
  125. *
  126. * @return void
  127. */
  128. public function testAddOptionDefault() {
  129. $parser = new ConsoleOptionParser('test', false);
  130. $parser->addOption('test', array(
  131. 'default' => 'default value',
  132. ));
  133. $result = $parser->parse(array('--test'));
  134. $this->assertEquals(array('test' => 'default value', 'help' => false), $result[0], 'Default value did not parse out');
  135. $parser = new ConsoleOptionParser('test', false);
  136. $parser->addOption('test', array(
  137. 'default' => 'default value',
  138. ));
  139. $result = $parser->parse(array());
  140. $this->assertEquals(array('test' => 'default value', 'help' => false), $result[0], 'Default value did not parse out');
  141. }
  142. /**
  143. * test adding an option and using the short value for parsing.
  144. *
  145. * @return void
  146. */
  147. public function testAddOptionShort() {
  148. $parser = new ConsoleOptionParser('test', false);
  149. $parser->addOption('test', array(
  150. 'short' => 't'
  151. ));
  152. $result = $parser->parse(array('-t', 'value'));
  153. $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Short parameter did not parse out');
  154. }
  155. /**
  156. * Test that adding an option using a two letter short value causes an exception.
  157. * As they will not parse correctly.
  158. *
  159. * @expectedException \Cake\Console\Error\ConsoleException
  160. * @return void
  161. */
  162. public function testAddOptionShortOneLetter() {
  163. $parser = new ConsoleOptionParser('test', false);
  164. $parser->addOption('test', array('short' => 'te'));
  165. }
  166. /**
  167. * test adding and using boolean options.
  168. *
  169. * @return void
  170. */
  171. public function testAddOptionBoolean() {
  172. $parser = new ConsoleOptionParser('test', false);
  173. $parser->addOption('test', array(
  174. 'boolean' => true,
  175. ));
  176. $result = $parser->parse(array('--test', 'value'));
  177. $expected = array(array('test' => true, 'help' => false), array('value'));
  178. $this->assertEquals($expected, $result);
  179. $result = $parser->parse(array('value'));
  180. $expected = array(array('test' => false, 'help' => false), array('value'));
  181. $this->assertEquals($expected, $result);
  182. }
  183. /**
  184. * test adding an multiple shorts.
  185. *
  186. * @return void
  187. */
  188. public function testAddOptionMultipleShort() {
  189. $parser = new ConsoleOptionParser('test', false);
  190. $parser->addOption('test', array('short' => 't', 'boolean' => true))
  191. ->addOption('file', array('short' => 'f', 'boolean' => true))
  192. ->addOption('output', array('short' => 'o', 'boolean' => true));
  193. $result = $parser->parse(array('-o', '-t', '-f'));
  194. $expected = array('file' => true, 'test' => true, 'output' => true, 'help' => false);
  195. $this->assertEquals($expected, $result[0], 'Short parameter did not parse out');
  196. $result = $parser->parse(array('-otf'));
  197. $this->assertEquals($expected, $result[0], 'Short parameter did not parse out');
  198. }
  199. /**
  200. * test multiple options at once.
  201. *
  202. * @return void
  203. */
  204. public function testMultipleOptions() {
  205. $parser = new ConsoleOptionParser('test', false);
  206. $parser->addOption('test')
  207. ->addOption('connection')
  208. ->addOption('table', array('short' => 't', 'default' => true));
  209. $result = $parser->parse(array('--test', 'value', '-t', '--connection', 'postgres'));
  210. $expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres', 'help' => false);
  211. $this->assertEquals($expected, $result[0], 'multiple options did not parse');
  212. }
  213. /**
  214. * Test adding multiple options.
  215. *
  216. * @return void
  217. */
  218. public function testAddOptions() {
  219. $parser = new ConsoleOptionParser('something', false);
  220. $result = $parser->addOptions(array(
  221. 'name' => array('help' => 'The name'),
  222. 'other' => array('help' => 'The other arg')
  223. ));
  224. $this->assertEquals($parser, $result, 'addOptions is not chainable.');
  225. $result = $parser->options();
  226. $this->assertEquals(3, count($result), 'Not enough options');
  227. }
  228. /**
  229. * test that boolean options work
  230. *
  231. * @return void
  232. */
  233. public function testOptionWithBooleanParam() {
  234. $parser = new ConsoleOptionParser('test', false);
  235. $parser->addOption('no-commit', array('boolean' => true))
  236. ->addOption('table', array('short' => 't'));
  237. $result = $parser->parse(array('--table', 'posts', '--no-commit', 'arg1', 'arg2'));
  238. $expected = array(array('table' => 'posts', 'no-commit' => true, 'help' => false), array('arg1', 'arg2'));
  239. $this->assertEquals($expected, $result, 'Boolean option did not parse correctly.');
  240. }
  241. /**
  242. * test parsing options that do not exist.
  243. *
  244. * @expectedException \Cake\Console\Error\ConsoleException
  245. * @return void
  246. */
  247. public function testOptionThatDoesNotExist() {
  248. $parser = new ConsoleOptionParser('test', false);
  249. $parser->addOption('no-commit', array('boolean' => true));
  250. $parser->parse(array('--fail', 'other'));
  251. }
  252. /**
  253. * test parsing short options that do not exist.
  254. *
  255. * @expectedException \Cake\Console\Error\ConsoleException
  256. * @return void
  257. */
  258. public function testShortOptionThatDoesNotExist() {
  259. $parser = new ConsoleOptionParser('test', false);
  260. $parser->addOption('no-commit', array('boolean' => true));
  261. $parser->parse(array('-f'));
  262. }
  263. /**
  264. * test that options with choices enforce them.
  265. *
  266. * @expectedException \Cake\Console\Error\ConsoleException
  267. * @return void
  268. */
  269. public function testOptionWithChoices() {
  270. $parser = new ConsoleOptionParser('test', false);
  271. $parser->addOption('name', array('choices' => array('mark', 'jose')));
  272. $result = $parser->parse(array('--name', 'mark'));
  273. $expected = array('name' => 'mark', 'help' => false);
  274. $this->assertEquals($expected, $result[0], 'Got the correct value.');
  275. $result = $parser->parse(array('--name', 'jimmy'));
  276. }
  277. /**
  278. * Ensure that option values can start with -
  279. *
  280. * @return void
  281. */
  282. public function testOptionWithValueStartingWithMinus() {
  283. $parser = new ConsoleOptionParser('test', false);
  284. $parser->addOption('name')
  285. ->addOption('age');
  286. $result = $parser->parse(array('--name', '-foo', '--age', 'old'));
  287. $expected = array('name' => '-foo', 'age' => 'old', 'help' => false);
  288. $this->assertEquals($expected, $result[0], 'Option values starting with "-" are broken.');
  289. }
  290. /**
  291. * test positional argument parsing.
  292. *
  293. * @return void
  294. */
  295. public function testPositionalArgument() {
  296. $parser = new ConsoleOptionParser('test', false);
  297. $result = $parser->addArgument('name', array('help' => 'An argument'));
  298. $this->assertEquals($parser, $result, 'Should return this');
  299. }
  300. /**
  301. * test addOption with an object.
  302. *
  303. * @return void
  304. */
  305. public function testAddArgumentObject() {
  306. $parser = new ConsoleOptionParser('test', false);
  307. $parser->addArgument(new ConsoleInputArgument('test'));
  308. $result = $parser->arguments();
  309. $this->assertCount(1, $result);
  310. $this->assertEquals('test', $result[0]->name());
  311. }
  312. /**
  313. * Test adding arguments out of order.
  314. *
  315. * @return void
  316. */
  317. public function testAddArgumentOutOfOrder() {
  318. $parser = new ConsoleOptionParser('test', false);
  319. $parser->addArgument('name', array('index' => 1, 'help' => 'first argument'))
  320. ->addArgument('bag', array('index' => 2, 'help' => 'second argument'))
  321. ->addArgument('other', array('index' => 0, 'help' => 'Zeroth argument'));
  322. $result = $parser->arguments();
  323. $this->assertCount(3, $result);
  324. $this->assertEquals('other', $result[0]->name());
  325. $this->assertEquals('name', $result[1]->name());
  326. $this->assertEquals('bag', $result[2]->name());
  327. $this->assertSame(array(0, 1, 2), array_keys($result));
  328. }
  329. /**
  330. * test overwriting positional arguments.
  331. *
  332. * @return void
  333. */
  334. public function testPositionalArgOverwrite() {
  335. $parser = new ConsoleOptionParser('test', false);
  336. $parser->addArgument('name', array('help' => 'An argument'))
  337. ->addArgument('other', array('index' => 0));
  338. $result = $parser->arguments();
  339. $this->assertEquals(1, count($result), 'Overwrite did not occur');
  340. }
  341. /**
  342. * test parsing arguments.
  343. *
  344. * @expectedException \Cake\Console\Error\ConsoleException
  345. * @return void
  346. */
  347. public function testParseArgumentTooMany() {
  348. $parser = new ConsoleOptionParser('test', false);
  349. $parser->addArgument('name', array('help' => 'An argument'))
  350. ->addArgument('other');
  351. $expected = array('one', 'two');
  352. $result = $parser->parse($expected);
  353. $this->assertEquals($expected, $result[1], 'Arguments are not as expected');
  354. $result = $parser->parse(array('one', 'two', 'three'));
  355. }
  356. /**
  357. * test parsing arguments with 0 value.
  358. *
  359. * @return void
  360. */
  361. public function testParseArgumentZero() {
  362. $parser = new ConsoleOptionParser('test', false);
  363. $expected = array('one', 'two', 0, 'after', 'zero');
  364. $result = $parser->parse($expected);
  365. $this->assertEquals($expected, $result[1], 'Arguments are not as expected');
  366. }
  367. /**
  368. * test that when there are not enough arguments an exception is raised
  369. *
  370. * @expectedException \Cake\Console\Error\ConsoleException
  371. * @return void
  372. */
  373. public function testPositionalArgNotEnough() {
  374. $parser = new ConsoleOptionParser('test', false);
  375. $parser->addArgument('name', array('required' => true))
  376. ->addArgument('other', array('required' => true));
  377. $parser->parse(array('one'));
  378. }
  379. /**
  380. * test that arguments with choices enforce them.
  381. *
  382. * @expectedException \Cake\Console\Error\ConsoleException
  383. * @return void
  384. */
  385. public function testPositionalArgWithChoices() {
  386. $parser = new ConsoleOptionParser('test', false);
  387. $parser->addArgument('name', array('choices' => array('mark', 'jose')))
  388. ->addArgument('alias', array('choices' => array('cowboy', 'samurai')))
  389. ->addArgument('weapon', array('choices' => array('gun', 'sword')));
  390. $result = $parser->parse(array('mark', 'samurai', 'sword'));
  391. $expected = array('mark', 'samurai', 'sword');
  392. $this->assertEquals($expected, $result[1], 'Got the correct value.');
  393. $result = $parser->parse(array('jose', 'coder'));
  394. }
  395. /**
  396. * Test adding multiple arguments.
  397. *
  398. * @return void
  399. */
  400. public function testAddArguments() {
  401. $parser = new ConsoleOptionParser('test', false);
  402. $result = $parser->addArguments(array(
  403. 'name' => array('help' => 'The name'),
  404. 'other' => array('help' => 'The other arg')
  405. ));
  406. $this->assertEquals($parser, $result, 'addArguments is not chainable.');
  407. $result = $parser->arguments();
  408. $this->assertEquals(2, count($result), 'Not enough arguments');
  409. }
  410. /**
  411. * test setting a subcommand up.
  412. *
  413. * @return void
  414. */
  415. public function testSubcommand() {
  416. $parser = new ConsoleOptionParser('test', false);
  417. $result = $parser->addSubcommand('initdb', array(
  418. 'help' => 'Initialize the database'
  419. ));
  420. $this->assertEquals($parser, $result, 'Adding a subcommand is not chainable');
  421. }
  422. /**
  423. * test addSubcommand with an object.
  424. *
  425. * @return void
  426. */
  427. public function testAddSubcommandObject() {
  428. $parser = new ConsoleOptionParser('test', false);
  429. $parser->addSubcommand(new ConsoleInputSubcommand('test'));
  430. $result = $parser->subcommands();
  431. $this->assertEquals(1, count($result));
  432. $this->assertEquals('test', $result['test']->name());
  433. }
  434. /**
  435. * test adding multiple subcommands
  436. *
  437. * @return void
  438. */
  439. public function testAddSubcommands() {
  440. $parser = new ConsoleOptionParser('test', false);
  441. $result = $parser->addSubcommands(array(
  442. 'initdb' => array('help' => 'Initialize the database'),
  443. 'create' => array('help' => 'Create something')
  444. ));
  445. $this->assertEquals($parser, $result, 'Adding a subcommands is not chainable');
  446. $result = $parser->subcommands();
  447. $this->assertEquals(2, count($result), 'Not enough subcommands');
  448. }
  449. /**
  450. * test that no exception is triggered when help is being generated
  451. *
  452. * @return void
  453. */
  454. public function testHelpNoExceptionWhenGettingHelp() {
  455. $parser = new ConsoleOptionParser('mycommand', false);
  456. $parser->addOption('test', array('help' => 'A test option.'))
  457. ->addArgument('model', array('help' => 'The model to make.', 'required' => true));
  458. $result = $parser->parse(array('--help'));
  459. $this->assertTrue($result[0]['help']);
  460. }
  461. /**
  462. * test that help() with a command param shows the help for a subcommand
  463. *
  464. * @return void
  465. */
  466. public function testHelpSubcommandHelp() {
  467. $subParser = new ConsoleOptionParser('method', false);
  468. $subParser->addOption('connection', array('help' => 'Db connection.'));
  469. $parser = new ConsoleOptionParser('mycommand', false);
  470. $parser->addSubcommand('method', array(
  471. 'help' => 'This is another command',
  472. 'parser' => $subParser
  473. ))
  474. ->addOption('test', array('help' => 'A test option.'));
  475. $result = $parser->help('method');
  476. $expected = <<<TEXT
  477. <info>Usage:</info>
  478. cake mycommand method [-h] [--connection]
  479. <info>Options:</info>
  480. --help, -h Display this help.
  481. --connection Db connection.
  482. TEXT;
  483. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  484. }
  485. /**
  486. * test building a parser from an array.
  487. *
  488. * @return void
  489. */
  490. public function testBuildFromArray() {
  491. $spec = array(
  492. 'command' => 'test',
  493. 'arguments' => array(
  494. 'name' => array('help' => 'The name'),
  495. 'other' => array('help' => 'The other arg')
  496. ),
  497. 'options' => array(
  498. 'name' => array('help' => 'The name'),
  499. 'other' => array('help' => 'The other arg')
  500. ),
  501. 'subcommands' => array(
  502. 'initdb' => array('help' => 'make database')
  503. ),
  504. 'description' => 'description text',
  505. 'epilog' => 'epilog text'
  506. );
  507. $parser = ConsoleOptionParser::buildFromArray($spec);
  508. $this->assertEquals($spec['description'], $parser->description());
  509. $this->assertEquals($spec['epilog'], $parser->epilog());
  510. $options = $parser->options();
  511. $this->assertTrue(isset($options['name']));
  512. $this->assertTrue(isset($options['other']));
  513. $args = $parser->arguments();
  514. $this->assertEquals(2, count($args));
  515. $commands = $parser->subcommands();
  516. $this->assertEquals(1, count($commands));
  517. }
  518. /**
  519. * test that create() returns instances
  520. *
  521. * @return void
  522. */
  523. public function testCreateFactory() {
  524. $parser = ConsoleOptionParser::create('factory', false);
  525. $this->assertInstanceOf('Cake\Console\ConsoleOptionParser', $parser);
  526. $this->assertEquals('factory', $parser->command());
  527. }
  528. /**
  529. * test that command() inflects the command name.
  530. *
  531. * @return void
  532. */
  533. public function testCommandInflection() {
  534. $parser = new ConsoleOptionParser('CommandLine');
  535. $this->assertEquals('command_line', $parser->command());
  536. }
  537. /**
  538. * test that parse() takes a subcommand argument, and that the subcommand parser
  539. * is used.
  540. *
  541. * @return void
  542. */
  543. public function testParsingWithSubParser() {
  544. $parser = new ConsoleOptionParser('test', false);
  545. $parser->addOption('primary')
  546. ->addArgument('one', array('required' => true, 'choices' => array('a', 'b')))
  547. ->addArgument('two', array('required' => true))
  548. ->addSubcommand('sub', array(
  549. 'parser' => array(
  550. 'options' => array(
  551. 'secondary' => array('boolean' => true),
  552. 'fourth' => array('help' => 'fourth option')
  553. ),
  554. 'arguments' => array(
  555. 'sub_arg' => array('choices' => array('c', 'd'))
  556. )
  557. )
  558. ));
  559. $result = $parser->parse(array('sub', '--secondary', '--fourth', '4', 'c'));
  560. $expected = array(array(
  561. 'secondary' => true,
  562. 'fourth' => '4',
  563. 'help' => false,
  564. 'verbose' => false,
  565. 'quiet' => false), array('c'));
  566. $this->assertEquals($expected, $result, 'Sub parser did not parse request.');
  567. }
  568. }