ConsoleIntegrationTestTrait.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @since 3.7.0
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\TestSuite;
  15. use Cake\Console\Command;
  16. use Cake\Console\CommandRunner;
  17. use Cake\Console\ConsoleIo;
  18. use Cake\Console\Exception\StopException;
  19. use Cake\Core\Configure;
  20. use Cake\TestSuite\Constraint\Console\ContentsContain;
  21. use Cake\TestSuite\Constraint\Console\ContentsContainRow;
  22. use Cake\TestSuite\Constraint\Console\ContentsEmpty;
  23. use Cake\TestSuite\Constraint\Console\ContentsNotContain;
  24. use Cake\TestSuite\Constraint\Console\ContentsRegExp;
  25. use Cake\TestSuite\Constraint\Console\ExitCode;
  26. use Cake\TestSuite\Stub\ConsoleInput;
  27. use Cake\TestSuite\Stub\ConsoleOutput;
  28. use Cake\TestSuite\Stub\MissingConsoleInputException;
  29. /**
  30. * A test case class intended to make integration tests of cake console commands
  31. * easier.
  32. */
  33. trait ConsoleIntegrationTestTrait
  34. {
  35. /**
  36. * Whether or not to use the CommandRunner
  37. *
  38. * @var bool
  39. */
  40. protected $_useCommandRunner = false;
  41. /**
  42. * Last exit code
  43. *
  44. * @var int|null
  45. */
  46. protected $_exitCode;
  47. /**
  48. * Console output stub
  49. *
  50. * @var \Cake\TestSuite\Stub\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject|null
  51. */
  52. protected $_out;
  53. /**
  54. * Console error output stub
  55. *
  56. * @var \Cake\TestSuite\Stub\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject|null
  57. */
  58. protected $_err;
  59. /**
  60. * Console input mock
  61. *
  62. * @var \Cake\Console\ConsoleInput|\PHPUnit_Framework_MockObject_MockObject|null
  63. */
  64. protected $_in;
  65. /**
  66. * Runs cli integration test
  67. *
  68. * @param string $command Command to run
  69. * @param array $input Input values to pass to an interactive shell
  70. * @return void
  71. */
  72. public function exec($command, array $input = [])
  73. {
  74. $runner = $this->makeRunner();
  75. $this->_out = new ConsoleOutput();
  76. $this->_err = new ConsoleOutput();
  77. $this->_in = new ConsoleInput($input);
  78. $args = $this->commandStringToArgs("cake $command");
  79. $io = new ConsoleIo($this->_out, $this->_err, $this->_in);
  80. try {
  81. $this->_exitCode = $runner->run($args, $io);
  82. } catch (MissingConsoleInputException $e) {
  83. $messages = $this->_out->messages();
  84. if (count($messages)) {
  85. $e->setQuestion($messages[count($messages) - 1]);
  86. }
  87. throw $e;
  88. } catch (StopException $exception) {
  89. $this->_exitCode = $exception->getCode();
  90. }
  91. }
  92. /**
  93. * Cleans state to get ready for the next test
  94. *
  95. * @after
  96. * @return void
  97. */
  98. public function cleanupConsoleTrait()
  99. {
  100. $this->_exitCode = null;
  101. $this->_out = null;
  102. $this->_err = null;
  103. $this->_in = null;
  104. $this->_useCommandRunner = false;
  105. }
  106. /**
  107. * Set this test case to use the CommandRunner rather than the legacy
  108. * ShellDispatcher
  109. *
  110. * @return void
  111. */
  112. public function useCommandRunner()
  113. {
  114. $this->_useCommandRunner = true;
  115. }
  116. /**
  117. * Asserts shell exited with the expected code
  118. *
  119. * @param int $expected Expected exit code
  120. * @param string $message Failure message
  121. * @return void
  122. */
  123. public function assertExitCode($expected, $message = '')
  124. {
  125. $this->assertThat($expected, new ExitCode($this->_exitCode), $message);
  126. }
  127. /**
  128. * Asserts shell exited with the Command::CODE_SUCCESS
  129. *
  130. * @param string $message Failure message
  131. * @return void
  132. */
  133. public function assertExitSuccess($message = '')
  134. {
  135. $this->assertThat(Command::CODE_SUCCESS, new ExitCode($this->_exitCode), $message);
  136. }
  137. /**
  138. * Asserts shell exited with Command::CODE_ERROR
  139. *
  140. * @param string $message Failure message
  141. * @return void
  142. */
  143. public function assertExitError($message = '')
  144. {
  145. $this->assertThat(Command::CODE_ERROR, new ExitCode($this->_exitCode), $message);
  146. }
  147. /**
  148. * Asserts that `stdout` is empty
  149. *
  150. * @param string $message The message to output when the assertion fails.
  151. * @return void
  152. */
  153. public function assertOutputEmpty($message = '')
  154. {
  155. $this->assertThat(null, new ContentsEmpty($this->_out->messages(), 'output'), $message);
  156. }
  157. /**
  158. * Asserts `stdout` contains expected output
  159. *
  160. * @param string $expected Expected output
  161. * @param string $message Failure message
  162. * @return void
  163. */
  164. public function assertOutputContains($expected, $message = '')
  165. {
  166. $this->assertThat($expected, new ContentsContain($this->_out->messages(), 'output'), $message);
  167. }
  168. /**
  169. * Asserts `stdout` does not contain expected output
  170. *
  171. * @param string $expected Expected output
  172. * @param string $message Failure message
  173. * @return void
  174. */
  175. public function assertOutputNotContains($expected, $message = '')
  176. {
  177. $this->assertThat($expected, new ContentsNotContain($this->_out->messages(), 'output'), $message);
  178. }
  179. /**
  180. * Asserts `stdout` contains expected regexp
  181. *
  182. * @param string $pattern Expected pattern
  183. * @param string $message Failure message
  184. * @return void
  185. */
  186. public function assertOutputRegExp($pattern, $message = '')
  187. {
  188. $this->assertThat($pattern, new ContentsRegExp($this->_out->messages(), 'output'), $message);
  189. }
  190. /**
  191. * Check that a row of cells exists in the output.
  192. *
  193. * @param array $row Row of cells to ensure exist in the output.
  194. * @param string $message Failure message.
  195. * @return void
  196. */
  197. protected function assertOutputContainsRow(array $row, $message = '')
  198. {
  199. $this->assertThat($row, new ContentsContainRow($this->_out->messages(), 'output'), $message);
  200. }
  201. /**
  202. * Asserts `stderr` contains expected output
  203. *
  204. * @param string $expected Expected output
  205. * @param string $message Failure message
  206. * @return void
  207. */
  208. public function assertErrorContains($expected, $message = '')
  209. {
  210. $this->assertThat($expected, new ContentsContain($this->_err->messages(), 'error output'), $message);
  211. }
  212. /**
  213. * Asserts `stderr` contains expected regexp
  214. *
  215. * @param string $pattern Expected pattern
  216. * @param string $message Failure message
  217. * @return void
  218. */
  219. public function assertErrorRegExp($pattern, $message = '')
  220. {
  221. $this->assertThat($pattern, new ContentsRegExp($this->_err->messages(), 'error output'), $message);
  222. }
  223. /**
  224. * Asserts that `stderr` is empty
  225. *
  226. * @param string $message The message to output when the assertion fails.
  227. * @return void
  228. */
  229. public function assertErrorEmpty($message = '')
  230. {
  231. $this->assertThat(null, new ContentsEmpty($this->_err->messages(), 'error output'), $message);
  232. }
  233. /**
  234. * Builds the appropriate command dispatcher
  235. *
  236. * @return CommandRunner|LegacyCommandRunner
  237. */
  238. protected function makeRunner()
  239. {
  240. if ($this->_useCommandRunner) {
  241. $applicationClassName = Configure::read('App.namespace') . '\Application';
  242. return new CommandRunner(new $applicationClassName(CONFIG));
  243. }
  244. return new LegacyCommandRunner();
  245. }
  246. /**
  247. * Creates an $argv array from a command string
  248. *
  249. * @param string $command Command string
  250. * @return array
  251. */
  252. protected function commandStringToArgs($command)
  253. {
  254. $charCount = strlen($command);
  255. $argv = [];
  256. $arg = '';
  257. $inDQuote = false;
  258. $inSQuote = false;
  259. for ($i = 0; $i < $charCount; $i++) {
  260. $char = substr($command, $i, 1);
  261. // end of argument
  262. if ($char === ' ' && !$inDQuote && !$inSQuote) {
  263. if (strlen($arg)) {
  264. $argv[] = $arg;
  265. }
  266. $arg = '';
  267. continue;
  268. }
  269. // exiting single quote
  270. if ($inSQuote && $char === "'") {
  271. $inSQuote = false;
  272. continue;
  273. }
  274. // exiting double quote
  275. if ($inDQuote && $char === '"') {
  276. $inDQuote = false;
  277. continue;
  278. }
  279. // entering double quote
  280. if ($char === '"' && !$inSQuote) {
  281. $inDQuote = true;
  282. continue;
  283. }
  284. // entering single quote
  285. if ($char === "'" && !$inDQuote) {
  286. $inSQuote = true;
  287. continue;
  288. }
  289. $arg .= $char;
  290. }
  291. $argv[] = $arg;
  292. return $argv;
  293. }
  294. }