/src/TestSuite/ConsoleIntegrationTestCase.php

https://github.com/LubosRemplik/cakephp · PHP · 324 lines · 159 code · 31 blank · 134 comment · 9 complexity · b30eb313836a8d6e5a7686f55c173dc3 MD5 · raw file

  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.5.0
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\TestSuite;
  15. use Cake\Console\CommandRunner;
  16. use Cake\Console\ConsoleInput;
  17. use Cake\Console\ConsoleIo;
  18. use Cake\Console\Exception\StopException;
  19. use Cake\Core\Configure;
  20. use Cake\TestSuite\Stub\ConsoleOutput;
  21. /**
  22. * A test case class intended to make integration tests of cake console commands
  23. * easier.
  24. */
  25. abstract class ConsoleIntegrationTestCase extends TestCase
  26. {
  27. /**
  28. * Whether or not to use the CommandRunner
  29. *
  30. * @var bool
  31. */
  32. protected $_useCommandRunner = false;
  33. /**
  34. * Last exit code
  35. *
  36. * @var int|null
  37. */
  38. protected $_exitCode;
  39. /**
  40. * Console output stub
  41. *
  42. * @var \Cake\Console\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject|null
  43. */
  44. protected $_out;
  45. /**
  46. * Console error output stub
  47. *
  48. * @var \Cake\Console\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject|null
  49. */
  50. protected $_err;
  51. /**
  52. * Console input mock
  53. *
  54. * @var \Cake\Console\ConsoleInput|\PHPUnit_Framework_MockObject_MockObject|null
  55. */
  56. protected $_in;
  57. /**
  58. * Runs cli integration test
  59. *
  60. * @param string $command Command to run
  61. * @param array $input Input values to pass to an interactive shell
  62. * @return void
  63. */
  64. public function exec($command, array $input = [])
  65. {
  66. $runner = $this->_makeRunner();
  67. $this->_out = new ConsoleOutput();
  68. $this->_err = new ConsoleOutput();
  69. $this->_in = $this->getMockBuilder(ConsoleInput::class)
  70. ->disableOriginalConstructor()
  71. ->setMethods(['read'])
  72. ->getMock();
  73. $i = 0;
  74. foreach ($input as $in) {
  75. $this->_in
  76. ->expects($this->at($i++))
  77. ->method('read')
  78. ->will($this->returnValue($in));
  79. }
  80. $args = $this->_commandStringToArgs("cake $command");
  81. $io = new ConsoleIo($this->_out, $this->_err, $this->_in);
  82. try {
  83. $this->_exitCode = $runner->run($args, $io);
  84. } catch (StopException $exception) {
  85. $this->_exitCode = $exception->getCode();
  86. }
  87. }
  88. /**
  89. * tearDown
  90. *
  91. * @return void
  92. */
  93. public function tearDown()
  94. {
  95. parent::tearDown();
  96. $this->_exitCode = null;
  97. $this->_out = null;
  98. $this->_err = null;
  99. $this->_in = null;
  100. $this->_useCommandRunner = false;
  101. }
  102. /**
  103. * Set this test case to use the CommandRunner rather than the legacy
  104. * ShellDispatcher
  105. *
  106. * @return void
  107. */
  108. public function useCommandRunner()
  109. {
  110. $this->_useCommandRunner = true;
  111. }
  112. /**
  113. * Asserts shell exited with the expected code
  114. *
  115. * @param int $expected Expected exit code
  116. * @param string $message Failure message to be appended to the generated message
  117. * @return void
  118. */
  119. public function assertExitCode($expected, $message = '')
  120. {
  121. $message = sprintf(
  122. 'Shell exited with code %d instead of the expected code %d. %s',
  123. $this->_exitCode,
  124. $expected,
  125. $message
  126. );
  127. $this->assertSame($expected, $this->_exitCode, $message);
  128. }
  129. /**
  130. * Asserts that `stdout` is empty
  131. *
  132. * @param string $message The message to output when the assertion fails.
  133. * @return void
  134. */
  135. public function assertOutputEmpty($message = 'stdout was not empty')
  136. {
  137. $output = implode(PHP_EOL, $this->_out->messages());
  138. $this->assertSame('', $output, $message);
  139. }
  140. /**
  141. * Asserts `stdout` contains expected output
  142. *
  143. * @param string $expected Expected output
  144. * @param string $message Failure message
  145. * @return void
  146. */
  147. public function assertOutputContains($expected, $message = '')
  148. {
  149. $output = implode(PHP_EOL, $this->_out->messages());
  150. $this->assertContains($expected, $output, $message);
  151. }
  152. /**
  153. * Asserts `stdout` does not contain expected output
  154. *
  155. * @param string $expected Expected output
  156. * @param string $message Failure message
  157. * @return void
  158. */
  159. public function assertOutputNotContains($expected, $message = '')
  160. {
  161. $output = implode(PHP_EOL, $this->_out->messages());
  162. $this->assertNotContains($expected, $output, $message);
  163. }
  164. /**
  165. * Asserts `stdout` contains expected regexp
  166. *
  167. * @param string $pattern Expected pattern
  168. * @param string $message Failure message
  169. * @return void
  170. */
  171. public function assertOutputRegExp($pattern, $message = '')
  172. {
  173. $output = implode(PHP_EOL, $this->_out->messages());
  174. $this->assertRegExp($pattern, $output, $message);
  175. }
  176. /**
  177. * Check that a row of cells exists in the output.
  178. *
  179. * @param array $row Row of cells to ensure exist in the output.
  180. * @param string $message Failure message.
  181. * @return void
  182. */
  183. protected function assertOutputContainsRow(array $row, $message = '')
  184. {
  185. $row = array_map(function ($cell) {
  186. return preg_quote($cell, '/');
  187. }, $row);
  188. $cells = implode('\s+\|\s+', $row);
  189. $pattern = '/' . $cells . '/';
  190. $this->assertOutputRegExp($pattern);
  191. }
  192. /**
  193. * Asserts `stderr` contains expected output
  194. *
  195. * @param string $expected Expected output
  196. * @param string $message Failure message
  197. * @return void
  198. */
  199. public function assertErrorContains($expected, $message = '')
  200. {
  201. $output = implode(PHP_EOL, $this->_err->messages());
  202. $this->assertContains($expected, $output, $message);
  203. }
  204. /**
  205. * Asserts `stderr` contains expected regexp
  206. *
  207. * @param string $pattern Expected pattern
  208. * @param string $message Failure message
  209. * @return void
  210. */
  211. public function assertErrorRegExp($pattern, $message = '')
  212. {
  213. $output = implode(PHP_EOL, $this->_err->messages());
  214. $this->assertRegExp($pattern, $output, $message);
  215. }
  216. /**
  217. * Asserts that `stderr` is empty
  218. *
  219. * @param string $message The message to output when the assertion fails.
  220. * @return void
  221. */
  222. public function assertErrorEmpty($message = 'stderr was not empty')
  223. {
  224. $output = implode(PHP_EOL, $this->_err->messages());
  225. $this->assertSame('', $output, $message);
  226. }
  227. /**
  228. * Builds the appropriate command dispatcher
  229. *
  230. * @return CommandRunner|LegacyCommandRunner
  231. */
  232. protected function _makeRunner()
  233. {
  234. if ($this->_useCommandRunner) {
  235. $applicationClassName = Configure::read('App.namespace') . '\Application';
  236. /** @var \Cake\Http\BaseApplication $applicationClass */
  237. $applicationClass = new $applicationClassName(CONFIG);
  238. return new CommandRunner($applicationClass);
  239. }
  240. return new LegacyCommandRunner();
  241. }
  242. /**
  243. * Creates an $argv array from a command string
  244. *
  245. * @param string $command Command string
  246. * @return array
  247. */
  248. protected function _commandStringToArgs($command)
  249. {
  250. $charCount = strlen($command);
  251. $argv = [];
  252. $arg = '';
  253. $inDQuote = false;
  254. $inSQuote = false;
  255. for ($i = 0; $i < $charCount; $i++) {
  256. $char = substr($command, $i, 1);
  257. // end of argument
  258. if ($char === ' ' && !$inDQuote && !$inSQuote) {
  259. if (strlen($arg)) {
  260. $argv[] = $arg;
  261. }
  262. $arg = '';
  263. continue;
  264. }
  265. // exiting single quote
  266. if ($inSQuote && $char === "'") {
  267. $inSQuote = false;
  268. continue;
  269. }
  270. // exiting double quote
  271. if ($inDQuote && $char === '"') {
  272. $inDQuote = false;
  273. continue;
  274. }
  275. // entering double quote
  276. if ($char === '"' && !$inSQuote) {
  277. $inDQuote = true;
  278. continue;
  279. }
  280. // entering single quote
  281. if ($char === "'" && !$inDQuote) {
  282. $inSQuote = true;
  283. continue;
  284. }
  285. $arg .= $char;
  286. }
  287. $argv[] = $arg;
  288. return $argv;
  289. }
  290. }