ConsoleOptionParserTest.php 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. <?php
  2. /**
  3. * CakePHP(tm) : 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(tm) Project
  12. * @since 2.0.0
  13. * @license https://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. * ConsoleOptionParserTest
  23. */
  24. class ConsoleOptionParserTest extends TestCase
  25. {
  26. /**
  27. * test setting the console description
  28. *
  29. * @group deprecated
  30. * @return void
  31. */
  32. public function testDescriptionDeprecated()
  33. {
  34. $this->deprecated(function () {
  35. $parser = new ConsoleOptionParser('test', false);
  36. $result = $parser->description('A test');
  37. $this->assertEquals($parser, $result, 'Setting description is not chainable');
  38. $this->assertEquals('A test', $parser->description(), 'getting value is wrong.');
  39. });
  40. }
  41. /**
  42. * test setting the console description
  43. *
  44. * @return void
  45. */
  46. public function testDescription()
  47. {
  48. $parser = new ConsoleOptionParser('test', false);
  49. $result = $parser->setDescription('A test');
  50. $this->assertEquals($parser, $result, 'Setting description is not chainable');
  51. $this->assertEquals('A test', $parser->getDescription(), 'getting value is wrong.');
  52. $result = $parser->setDescription(['A test', 'something']);
  53. $this->assertEquals("A test\nsomething", $parser->getDescription(), 'getting value is wrong.');
  54. }
  55. /**
  56. * test setting the console description
  57. *
  58. * @group deprecated
  59. * @return void
  60. */
  61. public function testEplilogDeprecated()
  62. {
  63. $this->deprecated(function () {
  64. $parser = new ConsoleOptionParser('test', false);
  65. $result = $parser->epilog('A test');
  66. $this->assertEquals($parser, $result, 'Setting epilog is not chainable');
  67. $this->assertEquals('A test', $parser->epilog(), 'getting value is wrong.');
  68. });
  69. }
  70. /**
  71. * test setting the console epilog
  72. *
  73. * @return void
  74. */
  75. public function testEpilog()
  76. {
  77. $parser = new ConsoleOptionParser('test', false);
  78. $result = $parser->setEpilog('A test');
  79. $this->assertEquals($parser, $result, 'Setting epilog is not chainable');
  80. $this->assertEquals('A test', $parser->getEpilog(), 'getting value is wrong.');
  81. $result = $parser->setEpilog(['A test', 'something']);
  82. $this->assertEquals("A test\nsomething", $parser->getEpilog(), 'getting value is wrong.');
  83. }
  84. /**
  85. * test adding an option returns self.
  86. *
  87. * @return void
  88. */
  89. public function testAddOptionReturnSelf()
  90. {
  91. $parser = new ConsoleOptionParser('test', false);
  92. $result = $parser->addOption('test');
  93. $this->assertEquals($parser, $result, 'Did not return $this from addOption');
  94. }
  95. /**
  96. * test removing an option
  97. *
  98. * @return void
  99. */
  100. public function testRemoveOption()
  101. {
  102. $parser = new ConsoleOptionParser('test', false);
  103. $result = $parser->addOption('test')
  104. ->removeOption('test')
  105. ->removeOption('help');
  106. $this->assertSame($parser, $result, 'Did not return $this from removeOption');
  107. $this->assertEquals([], $result->options());
  108. }
  109. /**
  110. * test adding an option and using the long value for parsing.
  111. *
  112. * @return void
  113. */
  114. public function testAddOptionLong()
  115. {
  116. $parser = new ConsoleOptionParser('test', false);
  117. $parser->addOption('test', [
  118. 'short' => 't',
  119. ]);
  120. $result = $parser->parse(['--test', 'value']);
  121. $this->assertEquals(['test' => 'value', 'help' => false], $result[0], 'Long parameter did not parse out');
  122. }
  123. /**
  124. * test adding an option with a zero value
  125. *
  126. * @return void
  127. */
  128. public function testAddOptionZero()
  129. {
  130. $parser = new ConsoleOptionParser('test', false);
  131. $parser->addOption('count', []);
  132. $result = $parser->parse(['--count', '0']);
  133. $this->assertEquals(['count' => '0', 'help' => false], $result[0], 'Zero parameter did not parse out');
  134. }
  135. /**
  136. * test addOption with an object.
  137. *
  138. * @return void
  139. */
  140. public function testAddOptionObject()
  141. {
  142. $parser = new ConsoleOptionParser('test', false);
  143. $parser->addOption(new ConsoleInputOption('test', 't'));
  144. $result = $parser->parse(['--test=value']);
  145. $this->assertEquals(['test' => 'value', 'help' => false], $result[0], 'Long parameter did not parse out');
  146. }
  147. /**
  148. * test adding an option and using the long value for parsing.
  149. *
  150. * @return void
  151. */
  152. public function testAddOptionLongEquals()
  153. {
  154. $parser = new ConsoleOptionParser('test', false);
  155. $parser->addOption('test', [
  156. 'short' => 't',
  157. ]);
  158. $result = $parser->parse(['--test=value']);
  159. $this->assertEquals(['test' => 'value', 'help' => false], $result[0], 'Long parameter did not parse out');
  160. }
  161. /**
  162. * test adding an option and using the default.
  163. *
  164. * @return void
  165. */
  166. public function testAddOptionDefault()
  167. {
  168. $parser = new ConsoleOptionParser('test', false);
  169. $parser->addOption('test', [
  170. 'default' => 'default value',
  171. ]);
  172. $result = $parser->parse(['--test']);
  173. $this->assertEquals(['test' => 'default value', 'help' => false], $result[0], 'Default value did not parse out');
  174. $parser = new ConsoleOptionParser('test', false);
  175. $parser->addOption('test', [
  176. 'default' => 'default value',
  177. ]);
  178. $result = $parser->parse([]);
  179. $this->assertEquals(['test' => 'default value', 'help' => false], $result[0], 'Default value did not parse out');
  180. }
  181. /**
  182. * test adding an option and using the short value for parsing.
  183. *
  184. * @return void
  185. */
  186. public function testAddOptionShort()
  187. {
  188. $parser = new ConsoleOptionParser('test', false);
  189. $parser->addOption('test', [
  190. 'short' => 't',
  191. ]);
  192. $result = $parser->parse(['-t', 'value']);
  193. $this->assertEquals(['test' => 'value', 'help' => false], $result[0], 'Short parameter did not parse out');
  194. }
  195. /**
  196. * test adding an option and using the short value for parsing.
  197. *
  198. * @return void
  199. */
  200. public function testAddOptionWithMultipleShort()
  201. {
  202. $parser = new ConsoleOptionParser('test', false);
  203. $parser->addOption('source', [
  204. 'multiple' => true,
  205. 'short' => 's',
  206. ]);
  207. $result = $parser->parse(['-s', 'mysql', '-s', 'postgres']);
  208. $this->assertEquals(
  209. [
  210. 'source' => ['mysql', 'postgres'],
  211. 'help' => false,
  212. ],
  213. $result[0],
  214. 'Short parameter did not parse out'
  215. );
  216. }
  217. /**
  218. * Test that adding an option using a two letter short value causes an exception.
  219. * As they will not parse correctly.
  220. *
  221. * @return void
  222. */
  223. public function testAddOptionShortOneLetter()
  224. {
  225. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  226. $parser = new ConsoleOptionParser('test', false);
  227. $parser->addOption('test', ['short' => 'te']);
  228. }
  229. /**
  230. * test adding and using boolean options.
  231. *
  232. * @return void
  233. */
  234. public function testAddOptionBoolean()
  235. {
  236. $parser = new ConsoleOptionParser('test', false);
  237. $parser->addOption('test', [
  238. 'boolean' => true,
  239. ]);
  240. $result = $parser->parse(['--test', 'value']);
  241. $expected = [['test' => true, 'help' => false], ['value']];
  242. $this->assertEquals($expected, $result);
  243. $result = $parser->parse(['value']);
  244. $expected = [['test' => false, 'help' => false], ['value']];
  245. $this->assertEquals($expected, $result);
  246. }
  247. /**
  248. * test adding an multiple shorts.
  249. *
  250. * @return void
  251. */
  252. public function testAddOptionMultipleShort()
  253. {
  254. $parser = new ConsoleOptionParser('test', false);
  255. $parser->addOption('test', ['short' => 't', 'boolean' => true])
  256. ->addOption('file', ['short' => 'f', 'boolean' => true])
  257. ->addOption('output', ['short' => 'o', 'boolean' => true]);
  258. $result = $parser->parse(['-o', '-t', '-f']);
  259. $expected = ['file' => true, 'test' => true, 'output' => true, 'help' => false];
  260. $this->assertEquals($expected, $result[0], 'Short parameter did not parse out');
  261. $result = $parser->parse(['-otf']);
  262. $this->assertEquals($expected, $result[0], 'Short parameter did not parse out');
  263. }
  264. /**
  265. * test multiple options at once.
  266. *
  267. * @return void
  268. */
  269. public function testMultipleOptions()
  270. {
  271. $parser = new ConsoleOptionParser('test', false);
  272. $parser->addOption('test')
  273. ->addOption('connection')
  274. ->addOption('table', ['short' => 't', 'default' => true]);
  275. $result = $parser->parse(['--test', 'value', '-t', '--connection', 'postgres']);
  276. $expected = ['test' => 'value', 'table' => true, 'connection' => 'postgres', 'help' => false];
  277. $this->assertEquals($expected, $result[0], 'multiple options did not parse');
  278. }
  279. /**
  280. * test adding an option that accepts multiple values.
  281. *
  282. * @return void
  283. */
  284. public function testAddOptionWithMultiple()
  285. {
  286. $parser = new ConsoleOptionParser('test', false);
  287. $parser->addOption('source', ['short' => 's', 'multiple' => true]);
  288. $result = $parser->parse(['--source', 'mysql', '-s', 'postgres']);
  289. $expected = [
  290. 'source' => [
  291. 'mysql',
  292. 'postgres',
  293. ],
  294. 'help' => false,
  295. ];
  296. $this->assertEquals($expected, $result[0], 'options with multiple values did not parse');
  297. }
  298. /**
  299. * test adding multiple options, including one that accepts multiple values.
  300. *
  301. * @return void
  302. */
  303. public function testAddMultipleOptionsWithMultiple()
  304. {
  305. $parser = new ConsoleOptionParser('test', false);
  306. $parser
  307. ->addOption('source', ['multiple' => true])
  308. ->addOption('name')
  309. ->addOption('export', ['boolean' => true]);
  310. $result = $parser->parse(['--export', '--source', 'mysql', '--name', 'annual-report', '--source', 'postgres']);
  311. $expected = [
  312. 'export' => true,
  313. 'source' => [
  314. 'mysql',
  315. 'postgres',
  316. ],
  317. 'name' => 'annual-report',
  318. 'help' => false,
  319. ];
  320. $this->assertEquals($expected, $result[0], 'options with multiple values did not parse');
  321. }
  322. /**
  323. * Test adding multiple options.
  324. *
  325. * @return void
  326. */
  327. public function testAddOptions()
  328. {
  329. $parser = new ConsoleOptionParser('something', false);
  330. $result = $parser->addOptions([
  331. 'name' => ['help' => 'The name'],
  332. 'other' => ['help' => 'The other arg'],
  333. ]);
  334. $this->assertEquals($parser, $result, 'addOptions is not chainable.');
  335. $result = $parser->options();
  336. $this->assertCount(3, $result, 'Not enough options');
  337. }
  338. /**
  339. * test that boolean options work
  340. *
  341. * @return void
  342. */
  343. public function testOptionWithBooleanParam()
  344. {
  345. $parser = new ConsoleOptionParser('test', false);
  346. $parser->addOption('no-commit', ['boolean' => true])
  347. ->addOption('table', ['short' => 't']);
  348. $result = $parser->parse(['--table', 'posts', '--no-commit', 'arg1', 'arg2']);
  349. $expected = [['table' => 'posts', 'no-commit' => true, 'help' => false], ['arg1', 'arg2']];
  350. $this->assertEquals($expected, $result, 'Boolean option did not parse correctly.');
  351. }
  352. /**
  353. * test parsing options that do not exist.
  354. *
  355. * @expectedExceptionMessageRegexp /Unknown option `fail`.\n\nDid you mean `help` \?\n\nAvailable options are :\n\n
  356. * - help\n - no-commit/
  357. * @return void
  358. */
  359. public function testOptionThatDoesNotExist()
  360. {
  361. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  362. $parser = new ConsoleOptionParser('test', false);
  363. $parser->addOption('no-commit', ['boolean' => true]);
  364. $parser->parse(['--fail', 'other']);
  365. }
  366. /**
  367. * test parsing short options that do not exist.
  368. *
  369. * @expectedExceptionMessageRegexp /Unknown short option `f`.\n\nAvailable short options are :\n\n
  370. * - `n` (short for `--no-commit`)\n - `c` (short for `--clear`)/
  371. * @return void
  372. */
  373. public function testShortOptionThatDoesNotExist()
  374. {
  375. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  376. $parser = new ConsoleOptionParser('test', false);
  377. $parser->addOption('no-commit', ['boolean' => true, 'short' => 'n']);
  378. $parser->addOption('construct', ['boolean' => true]);
  379. $parser->addOption('clear', ['boolean' => true, 'short' => 'c']);
  380. $parser->parse(['-f']);
  381. }
  382. /**
  383. * test that options with choices enforce them.
  384. *
  385. * @return void
  386. */
  387. public function testOptionWithChoices()
  388. {
  389. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  390. $parser = new ConsoleOptionParser('test', false);
  391. $parser->addOption('name', ['choices' => ['mark', 'jose']]);
  392. $result = $parser->parse(['--name', 'mark']);
  393. $expected = ['name' => 'mark', 'help' => false];
  394. $this->assertEquals($expected, $result[0], 'Got the correct value.');
  395. $result = $parser->parse(['--name', 'jimmy']);
  396. }
  397. /**
  398. * Ensure that option values can start with -
  399. *
  400. * @return void
  401. */
  402. public function testOptionWithValueStartingWithMinus()
  403. {
  404. $parser = new ConsoleOptionParser('test', false);
  405. $parser->addOption('name')
  406. ->addOption('age');
  407. $result = $parser->parse(['--name', '-foo', '--age', 'old']);
  408. $expected = ['name' => '-foo', 'age' => 'old', 'help' => false];
  409. $this->assertEquals($expected, $result[0], 'Option values starting with "-" are broken.');
  410. }
  411. /**
  412. * test positional argument parsing.
  413. *
  414. * @return void
  415. */
  416. public function testPositionalArgument()
  417. {
  418. $parser = new ConsoleOptionParser('test', false);
  419. $result = $parser->addArgument('name', ['help' => 'An argument']);
  420. $this->assertEquals($parser, $result, 'Should return this');
  421. }
  422. /**
  423. * test addOption with an object.
  424. *
  425. * @return void
  426. */
  427. public function testAddArgumentObject()
  428. {
  429. $parser = new ConsoleOptionParser('test', false);
  430. $parser->addArgument(new ConsoleInputArgument('test'));
  431. $result = $parser->arguments();
  432. $this->assertCount(1, $result);
  433. $this->assertEquals('test', $result[0]->name());
  434. }
  435. /**
  436. * Test adding arguments out of order.
  437. *
  438. * @return void
  439. */
  440. public function testAddArgumentOutOfOrder()
  441. {
  442. $parser = new ConsoleOptionParser('test', false);
  443. $parser->addArgument('name', ['index' => 1, 'help' => 'first argument'])
  444. ->addArgument('bag', ['index' => 2, 'help' => 'second argument'])
  445. ->addArgument('other', ['index' => 0, 'help' => 'Zeroth argument']);
  446. $result = $parser->arguments();
  447. $this->assertCount(3, $result);
  448. $this->assertEquals('other', $result[0]->name());
  449. $this->assertEquals('name', $result[1]->name());
  450. $this->assertEquals('bag', $result[2]->name());
  451. $this->assertSame([0, 1, 2], array_keys($result));
  452. $this->assertEquals(
  453. ['other', 'name', 'bag'],
  454. $parser->argumentNames()
  455. );
  456. }
  457. /**
  458. * test overwriting positional arguments.
  459. *
  460. * @return void
  461. */
  462. public function testPositionalArgOverwrite()
  463. {
  464. $parser = new ConsoleOptionParser('test', false);
  465. $parser->addArgument('name', ['help' => 'An argument'])
  466. ->addArgument('other', ['index' => 0]);
  467. $result = $parser->arguments();
  468. $this->assertCount(1, $result, 'Overwrite did not occur');
  469. }
  470. /**
  471. * test parsing arguments.
  472. *
  473. * @return void
  474. */
  475. public function testParseArgumentTooMany()
  476. {
  477. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  478. $parser = new ConsoleOptionParser('test', false);
  479. $parser->addArgument('name', ['help' => 'An argument'])
  480. ->addArgument('other');
  481. $expected = ['one', 'two'];
  482. $result = $parser->parse($expected);
  483. $this->assertEquals($expected, $result[1], 'Arguments are not as expected');
  484. $result = $parser->parse(['one', 'two', 'three']);
  485. }
  486. /**
  487. * test parsing arguments with 0 value.
  488. *
  489. * @return void
  490. */
  491. public function testParseArgumentZero()
  492. {
  493. $parser = new ConsoleOptionParser('test', false);
  494. $expected = ['one', 'two', 0, 'after', 'zero'];
  495. $result = $parser->parse($expected);
  496. $this->assertEquals($expected, $result[1], 'Arguments are not as expected');
  497. }
  498. /**
  499. * test that when there are not enough arguments an exception is raised
  500. *
  501. * @return void
  502. */
  503. public function testPositionalArgNotEnough()
  504. {
  505. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  506. $parser = new ConsoleOptionParser('test', false);
  507. $parser->addArgument('name', ['required' => true])
  508. ->addArgument('other', ['required' => true]);
  509. $parser->parse(['one']);
  510. }
  511. /**
  512. * test that when there are required arguments after optional ones an exception is raised
  513. *
  514. * @return void
  515. */
  516. public function testPositionalArgRequiredAfterOptional()
  517. {
  518. $this->expectException(\LogicException::class);
  519. $parser = new ConsoleOptionParser('test');
  520. $parser->addArgument('name', ['required' => false])
  521. ->addArgument('other', ['required' => true]);
  522. }
  523. /**
  524. * test that arguments with choices enforce them.
  525. *
  526. * @return void
  527. */
  528. public function testPositionalArgWithChoices()
  529. {
  530. $this->expectException(\Cake\Console\Exception\ConsoleException::class);
  531. $parser = new ConsoleOptionParser('test', false);
  532. $parser->addArgument('name', ['choices' => ['mark', 'jose']])
  533. ->addArgument('alias', ['choices' => ['cowboy', 'samurai']])
  534. ->addArgument('weapon', ['choices' => ['gun', 'sword']]);
  535. $result = $parser->parse(['mark', 'samurai', 'sword']);
  536. $expected = ['mark', 'samurai', 'sword'];
  537. $this->assertEquals($expected, $result[1], 'Got the correct value.');
  538. $result = $parser->parse(['jose', 'coder']);
  539. }
  540. /**
  541. * Test adding multiple arguments.
  542. *
  543. * @return void
  544. */
  545. public function testAddArguments()
  546. {
  547. $parser = new ConsoleOptionParser('test', false);
  548. $result = $parser->addArguments([
  549. 'name' => ['help' => 'The name'],
  550. 'other' => ['help' => 'The other arg'],
  551. ]);
  552. $this->assertEquals($parser, $result, 'addArguments is not chainable.');
  553. $result = $parser->arguments();
  554. $this->assertCount(2, $result, 'Not enough arguments');
  555. }
  556. /**
  557. * test setting a subcommand up.
  558. *
  559. * @return void
  560. */
  561. public function testSubcommand()
  562. {
  563. $parser = new ConsoleOptionParser('test', false);
  564. $result = $parser->addSubcommand('initdb', [
  565. 'help' => 'Initialize the database',
  566. ]);
  567. $this->assertEquals($parser, $result, 'Adding a subcommand is not chainable');
  568. }
  569. /**
  570. * Tests setting a subcommand up for a Shell method `initMyDb`.
  571. *
  572. * @return void
  573. */
  574. public function testSubcommandCamelBacked()
  575. {
  576. $parser = new ConsoleOptionParser('test', false);
  577. $result = $parser->addSubcommand('initMyDb', [
  578. 'help' => 'Initialize the database',
  579. ]);
  580. $subcommands = array_keys($result->subcommands());
  581. $this->assertEquals(['init_my_db'], $subcommands, 'Adding a subcommand does not work with camel backed method names.');
  582. }
  583. /**
  584. * test addSubcommand with an object.
  585. *
  586. * @return void
  587. */
  588. public function testAddSubcommandObject()
  589. {
  590. $parser = new ConsoleOptionParser('test', false);
  591. $parser->addSubcommand(new ConsoleInputSubcommand('test'));
  592. $result = $parser->subcommands();
  593. $this->assertCount(1, $result);
  594. $this->assertEquals('test', $result['test']->name());
  595. }
  596. /**
  597. * test addSubcommand without sorting applied.
  598. */
  599. public function testAddSubcommandSort()
  600. {
  601. $parser = new ConsoleOptionParser('test', false);
  602. $this->assertEquals(true, $parser->isSubcommandSortEnabled());
  603. $parser->enableSubcommandSort(false);
  604. $this->assertEquals(false, $parser->isSubcommandSortEnabled());
  605. $parser->addSubcommand(new ConsoleInputSubcommand('betaTest'), []);
  606. $parser->addSubcommand(new ConsoleInputSubcommand('alphaTest'), []);
  607. $result = $parser->subcommands();
  608. $this->assertCount(2, $result);
  609. $firstResult = key($result);
  610. $this->assertEquals('betaTest', $firstResult);
  611. }
  612. /**
  613. * test removeSubcommand with an object.
  614. *
  615. * @return void
  616. */
  617. public function testRemoveSubcommand()
  618. {
  619. $parser = new ConsoleOptionParser('test', false);
  620. $parser->addSubcommand(new ConsoleInputSubcommand('test'));
  621. $result = $parser->subcommands();
  622. $this->assertCount(1, $result);
  623. $parser->removeSubcommand('test');
  624. $result = $parser->subcommands();
  625. $this->assertCount(0, $result, 'Remove a subcommand does not work');
  626. }
  627. /**
  628. * test adding multiple subcommands
  629. *
  630. * @return void
  631. */
  632. public function testAddSubcommands()
  633. {
  634. $parser = new ConsoleOptionParser('test', false);
  635. $result = $parser->addSubcommands([
  636. 'initdb' => ['help' => 'Initialize the database'],
  637. 'create' => ['help' => 'Create something'],
  638. ]);
  639. $this->assertEquals($parser, $result, 'Adding a subcommands is not chainable');
  640. $result = $parser->subcommands();
  641. $this->assertCount(2, $result, 'Not enough subcommands');
  642. }
  643. /**
  644. * test that no exception is triggered when help is being generated
  645. *
  646. * @return void
  647. */
  648. public function testHelpNoExceptionWhenGettingHelp()
  649. {
  650. $parser = new ConsoleOptionParser('mycommand', false);
  651. $parser->addOption('test', ['help' => 'A test option.'])
  652. ->addArgument('model', ['help' => 'The model to make.', 'required' => true]);
  653. $result = $parser->parse(['--help']);
  654. $this->assertTrue($result[0]['help']);
  655. }
  656. /**
  657. * test that help() with a command param shows the help for a subcommand
  658. *
  659. * @return void
  660. */
  661. public function testHelpSubcommandHelp()
  662. {
  663. $subParser = new ConsoleOptionParser('method', false);
  664. $subParser->addOption('connection', ['help' => 'Db connection.']);
  665. $subParser->addOption('zero', ['short' => '0', 'help' => 'Zero.']);
  666. $parser = new ConsoleOptionParser('mycommand', false);
  667. $parser->addSubcommand('method', [
  668. 'help' => 'This is another command',
  669. 'parser' => $subParser,
  670. ])
  671. ->addOption('test', ['help' => 'A test option.']);
  672. $result = $parser->help('method');
  673. $expected = <<<TEXT
  674. This is another command
  675. <info>Usage:</info>
  676. cake mycommand method [--connection] [-h] [-0]
  677. <info>Options:</info>
  678. --connection Db connection.
  679. --help, -h Display this help.
  680. --zero, -0 Zero.
  681. TEXT;
  682. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  683. }
  684. /**
  685. * Test addSubcommand inherits options as no
  686. * parser is created.
  687. *
  688. * @return void
  689. */
  690. public function testHelpSubcommandInheritOptions()
  691. {
  692. $parser = new ConsoleOptionParser('mycommand', false);
  693. $parser->addSubcommand('build', [
  694. 'help' => 'Build things.',
  695. ])->addSubcommand('destroy', [
  696. 'help' => 'Destroy things.',
  697. ])->addOption('connection', [
  698. 'help' => 'Db connection.',
  699. 'short' => 'c',
  700. ])->addArgument('name', ['required' => false]);
  701. $result = $parser->help('build');
  702. $expected = <<<TEXT
  703. Build things.
  704. <info>Usage:</info>
  705. cake mycommand build [-c] [-h] [-q] [-v] [<name>]
  706. <info>Options:</info>
  707. --connection, -c Db connection.
  708. --help, -h Display this help.
  709. --quiet, -q Enable quiet output.
  710. --verbose, -v Enable verbose output.
  711. <info>Arguments:</info>
  712. name <comment>(optional)</comment>
  713. TEXT;
  714. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  715. }
  716. /**
  717. * test that help() with a command param shows the help for a subcommand
  718. *
  719. * @return void
  720. */
  721. public function testHelpSubcommandHelpArray()
  722. {
  723. $subParser = [
  724. 'options' => [
  725. 'foo' => [
  726. 'short' => 'f',
  727. 'help' => 'Foo.',
  728. 'boolean' => true,
  729. ],
  730. ],
  731. ];
  732. $parser = new ConsoleOptionParser('mycommand', false);
  733. $parser->addSubcommand('method', [
  734. 'help' => 'This is a subcommand',
  735. 'parser' => $subParser,
  736. ])
  737. ->setRootName('tool')
  738. ->addOption('test', ['help' => 'A test option.']);
  739. $result = $parser->help('method');
  740. $expected = <<<TEXT
  741. This is a subcommand
  742. <info>Usage:</info>
  743. tool mycommand method [-f] [-h] [-q] [-v]
  744. <info>Options:</info>
  745. --foo, -f Foo.
  746. --help, -h Display this help.
  747. --quiet, -q Enable quiet output.
  748. --verbose, -v Enable verbose output.
  749. TEXT;
  750. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  751. }
  752. /**
  753. * test that help() with a command param shows the help for a subcommand
  754. *
  755. * @return void
  756. */
  757. public function testHelpSubcommandInheritParser()
  758. {
  759. $subParser = new ConsoleOptionParser('method', false);
  760. $subParser->addOption('connection', ['help' => 'Db connection.']);
  761. $subParser->addOption('zero', ['short' => '0', 'help' => 'Zero.']);
  762. $parser = new ConsoleOptionParser('mycommand', false);
  763. $parser->addSubcommand('method', [
  764. 'help' => 'This is another command',
  765. 'parser' => $subParser,
  766. ])
  767. ->addOption('test', ['help' => 'A test option.']);
  768. $result = $parser->help('method');
  769. $expected = <<<TEXT
  770. This is another command
  771. <info>Usage:</info>
  772. cake mycommand method [--connection] [-h] [-0]
  773. <info>Options:</info>
  774. --connection Db connection.
  775. --help, -h Display this help.
  776. --zero, -0 Zero.
  777. TEXT;
  778. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  779. }
  780. /**
  781. * test that help() with a custom rootName
  782. *
  783. * @return void
  784. */
  785. public function testHelpWithRootName()
  786. {
  787. $parser = new ConsoleOptionParser('sample', false);
  788. $parser->setDescription('A command!')
  789. ->setRootName('tool')
  790. ->addOption('test', ['help' => 'A test option.']);
  791. $result = $parser->help();
  792. $expected = <<<TEXT
  793. A command!
  794. <info>Usage:</info>
  795. tool sample [-h] [--test]
  796. <info>Options:</info>
  797. --help, -h Display this help.
  798. --test A test option.
  799. TEXT;
  800. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  801. }
  802. /**
  803. * test that getCommandError() with an unknown subcommand param shows a helpful message
  804. *
  805. * @return void
  806. */
  807. public function testHelpUnknownSubcommand()
  808. {
  809. $subParser = [
  810. 'options' => [
  811. 'foo' => [
  812. 'short' => 'f',
  813. 'help' => 'Foo.',
  814. 'boolean' => true,
  815. ],
  816. ],
  817. ];
  818. $parser = new ConsoleOptionParser('mycommand', false);
  819. $parser
  820. ->addSubcommand('method', [
  821. 'help' => 'This is a subcommand',
  822. 'parser' => $subParser,
  823. ])
  824. ->addOption('test', ['help' => 'A test option.'])
  825. ->addSubcommand('unstash');
  826. $result = $parser->help('unknown');
  827. $expected = <<<TEXT
  828. Unable to find the `mycommand unknown` subcommand. See `bin/cake mycommand --help`.
  829. Did you mean : `mycommand unstash` ?
  830. Available subcommands for the `mycommand` command are :
  831. - method
  832. - unstash
  833. TEXT;
  834. $this->assertTextEquals($expected, $result, 'Help is not correct.');
  835. }
  836. /**
  837. * test building a parser from an array.
  838. *
  839. * @return void
  840. */
  841. public function testBuildFromArray()
  842. {
  843. $spec = [
  844. 'command' => 'test',
  845. 'arguments' => [
  846. 'name' => ['help' => 'The name'],
  847. 'other' => ['help' => 'The other arg'],
  848. ],
  849. 'options' => [
  850. 'name' => ['help' => 'The name'],
  851. 'other' => ['help' => 'The other arg'],
  852. ],
  853. 'subcommands' => [
  854. 'initdb' => ['help' => 'make database'],
  855. ],
  856. 'description' => 'description text',
  857. 'epilog' => 'epilog text',
  858. ];
  859. $parser = ConsoleOptionParser::buildFromArray($spec);
  860. $this->assertEquals($spec['description'], $parser->getDescription());
  861. $this->assertEquals($spec['epilog'], $parser->getEpilog());
  862. $options = $parser->options();
  863. $this->assertArrayHasKey('name', $options);
  864. $this->assertArrayHasKey('other', $options);
  865. $args = $parser->arguments();
  866. $this->assertCount(2, $args);
  867. $commands = $parser->subcommands();
  868. $this->assertCount(1, $commands);
  869. }
  870. /**
  871. * test that create() returns instances
  872. *
  873. * @return void
  874. */
  875. public function testCreateFactory()
  876. {
  877. $parser = ConsoleOptionParser::create('factory', false);
  878. $this->assertInstanceOf('Cake\Console\ConsoleOptionParser', $parser);
  879. $this->assertEquals('factory', $parser->getCommand());
  880. }
  881. /**
  882. * test that command() inflects the command name.
  883. *
  884. * @return void
  885. */
  886. public function testCommandInflection()
  887. {
  888. $parser = new ConsoleOptionParser('CommandLine');
  889. $this->assertEquals('command_line', $parser->getCommand());
  890. }
  891. /**
  892. * test that parse() takes a subcommand argument, and that the subcommand parser
  893. * is used.
  894. *
  895. * @return void
  896. */
  897. public function testParsingWithSubParser()
  898. {
  899. $parser = new ConsoleOptionParser('test', false);
  900. $parser->addOption('primary')
  901. ->addArgument('one', ['required' => true, 'choices' => ['a', 'b']])
  902. ->addArgument('two', ['required' => true])
  903. ->addSubcommand('sub', [
  904. 'parser' => [
  905. 'options' => [
  906. 'secondary' => ['boolean' => true],
  907. 'fourth' => ['help' => 'fourth option'],
  908. ],
  909. 'arguments' => [
  910. 'sub_arg' => ['choices' => ['c', 'd']],
  911. ],
  912. ],
  913. ]);
  914. $result = $parser->parse(['sub', '--secondary', '--fourth', '4', 'c']);
  915. $expected = [[
  916. 'secondary' => true,
  917. 'fourth' => '4',
  918. 'help' => false,
  919. 'verbose' => false,
  920. 'quiet' => false], ['c']];
  921. $this->assertEquals($expected, $result, 'Sub parser did not parse request.');
  922. }
  923. /**
  924. * Tests toArray()
  925. *
  926. * @return void
  927. */
  928. public function testToArray()
  929. {
  930. $spec = [
  931. 'command' => 'test',
  932. 'arguments' => [
  933. 'name' => ['help' => 'The name'],
  934. 'other' => ['help' => 'The other arg'],
  935. ],
  936. 'options' => [
  937. 'name' => ['help' => 'The name'],
  938. 'other' => ['help' => 'The other arg'],
  939. ],
  940. 'subcommands' => [
  941. 'initdb' => ['help' => 'make database'],
  942. ],
  943. 'description' => 'description text',
  944. 'epilog' => 'epilog text',
  945. ];
  946. $parser = ConsoleOptionParser::buildFromArray($spec);
  947. $result = $parser->toArray();
  948. $this->assertEquals($spec['description'], $result['description']);
  949. $this->assertEquals($spec['epilog'], $result['epilog']);
  950. $options = $result['options'];
  951. $this->assertArrayHasKey('name', $options);
  952. $this->assertArrayHasKey('other', $options);
  953. $this->assertCount(2, $result['arguments']);
  954. $this->assertCount(1, $result['subcommands']);
  955. }
  956. /**
  957. * Tests merge()
  958. *
  959. * @return void
  960. */
  961. public function testMerge()
  962. {
  963. $parser = new ConsoleOptionParser('test');
  964. $parser->addOption('test', ['short' => 't', 'boolean' => true])
  965. ->addArgument('one', ['required' => true, 'choices' => ['a', 'b']])
  966. ->addArgument('two', ['required' => true]);
  967. $parserTwo = new ConsoleOptionParser('test');
  968. $parserTwo->addOption('file', ['short' => 'f', 'boolean' => true])
  969. ->addOption('output', ['short' => 'o', 'boolean' => true])
  970. ->addArgument('one', ['required' => true, 'choices' => ['a', 'b']]);
  971. $parser->merge($parserTwo);
  972. $result = $parser->toArray();
  973. $options = $result['options'];
  974. $this->assertArrayHasKey('quiet', $options);
  975. $this->assertArrayHasKey('test', $options);
  976. $this->assertArrayHasKey('file', $options);
  977. $this->assertArrayHasKey('output', $options);
  978. $this->assertCount(2, $result['arguments']);
  979. $this->assertCount(6, $result['options']);
  980. }
  981. }