Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10816 from jeremyharris/cli-integrationtest
3.next RFC ConsoleIntegrationTestCase
- Loading branch information
Showing
7 changed files
with
630 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @since 3.5.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\TestSuite; | ||
|
||
use Cake\Console\CommandRunner; | ||
use Cake\Console\ConsoleInput; | ||
use Cake\Console\ConsoleIo; | ||
use Cake\Core\Configure; | ||
use Cake\TestSuite\Stub\ConsoleOutput; | ||
|
||
/** | ||
* A test case class intended to make integration tests of cake console commands | ||
* easier. | ||
*/ | ||
class ConsoleIntegrationTestCase extends TestCase | ||
{ | ||
/** | ||
* Whether or not to use the CommandRunner | ||
* | ||
* @var bool | ||
*/ | ||
protected $_useCommandRunner = false; | ||
|
||
/** | ||
* Last exit code | ||
* | ||
* @var int | ||
*/ | ||
protected $_exitCode; | ||
|
||
/** | ||
* Console output stub | ||
* | ||
* @var ConsoleOutput | ||
*/ | ||
protected $_out; | ||
|
||
/** | ||
* Console error output stub | ||
* | ||
* @var ConsoleOutput | ||
*/ | ||
protected $_err; | ||
|
||
/** | ||
* Console input mock | ||
* | ||
* @var ConsoleInput | ||
*/ | ||
protected $_in; | ||
|
||
/** | ||
* Runs cli integration test | ||
* | ||
* @param string $command Command to run | ||
* @param array $input Input values to pass to an interactive shell | ||
* @return void | ||
*/ | ||
public function exec($command, array $input = []) | ||
{ | ||
$runner = $this->_makeRunner(); | ||
|
||
$this->_out = new ConsoleOutput(); | ||
$this->_err = new ConsoleOutput(); | ||
$this->_in = $this->getMockBuilder(ConsoleInput::class) | ||
->disableOriginalConstructor() | ||
->setMethods(['read']) | ||
->getMock(); | ||
|
||
$i = 0; | ||
foreach ($input as $in) { | ||
$this->_in | ||
->expects($this->at($i++)) | ||
->method('read') | ||
->will($this->returnValue($in)); | ||
} | ||
|
||
$args = $this->_commandStringToArgs("cake $command"); | ||
|
||
$io = new ConsoleIo($this->_out, $this->_err, $this->_in); | ||
|
||
$this->_exitCode = $runner->run($args, $io); | ||
} | ||
|
||
/** | ||
* tearDown | ||
* | ||
* @return void | ||
*/ | ||
public function tearDown() | ||
{ | ||
parent::tearDown(); | ||
|
||
$this->_exitCode = null; | ||
$this->_out = null; | ||
$this->_err = null; | ||
$this->_in = null; | ||
$this->_useCommandRunner = false; | ||
} | ||
|
||
/** | ||
* Set this test case to use the CommandRunner rather than the legacy | ||
* ShellDispatcher | ||
* | ||
* @return void | ||
*/ | ||
public function enableCommandRunner() | ||
{ | ||
$this->_useCommandRunner = true; | ||
} | ||
|
||
/** | ||
* Asserts shell exited with the expected code | ||
* | ||
* @param int $expected Expected exit code | ||
* @param string $message Failure message to be appended to the generated message | ||
* @return void | ||
*/ | ||
public function assertExitCode($expected, $message = '') | ||
{ | ||
$message = sprintf( | ||
'Shell exited with code %d instead of the expected code %d. %s', | ||
$this->_exitCode, | ||
$expected, | ||
$message | ||
); | ||
$this->assertSame($expected, $this->_exitCode, $message); | ||
} | ||
|
||
/** | ||
* Asserts `stdout` contains expected output | ||
* | ||
* @param string $expected Expected output | ||
* @param string $message Failure message | ||
* @return void | ||
*/ | ||
public function assertOutputContains($expected, $message = '') | ||
{ | ||
$output = implode(PHP_EOL, $this->_out->messages()); | ||
$this->assertContains($expected, $output, $message); | ||
} | ||
|
||
/** | ||
* Asserts `stdout` contains expected regexp | ||
* | ||
* @param string $pattern Expected pattern | ||
* @param string $message Failure message | ||
* @return void | ||
*/ | ||
public function assertOutputRegExp($pattern, $message = '') | ||
{ | ||
$output = implode(PHP_EOL, $this->_out->messages()); | ||
$this->assertRegExp($pattern, $output, $message); | ||
} | ||
|
||
/** | ||
* Asserts `stderr` contains expected output | ||
* | ||
* @param string $expected Expected output | ||
* @param string $message Failure message | ||
* @return void | ||
*/ | ||
public function assertErrorContains($expected, $message = '') | ||
{ | ||
$output = implode(PHP_EOL, $this->_err->messages()); | ||
$this->assertContains($expected, $output, $message); | ||
} | ||
|
||
/** | ||
* Asserts `stderr` contains expected regexp | ||
* | ||
* @param string $pattern Expected pattern | ||
* @param string $message Failure message | ||
* @return void | ||
*/ | ||
public function assertErrorRegExp($pattern, $message = '') | ||
{ | ||
$output = implode(PHP_EOL, $this->_err->messages()); | ||
$this->assertRegExp($pattern, $output, $message); | ||
} | ||
|
||
/** | ||
* Builds the appropriate command dispatcher | ||
* | ||
* @return CommandRunner|LegacyCommandRunner | ||
*/ | ||
protected function _makeRunner() | ||
{ | ||
if ($this->_useCommandRunner) { | ||
$applicationClassName = Configure::read('App.namespace') . '\Application'; | ||
|
||
return new CommandRunner(new $applicationClassName([CONFIG])); | ||
} | ||
|
||
return new LegacyCommandRunner(); | ||
} | ||
|
||
/** | ||
* Creates an $argv array from a command string | ||
* | ||
* @param string $command Command string | ||
* @return array | ||
*/ | ||
protected function _commandStringToArgs($command) | ||
{ | ||
$charCount = strlen($command); | ||
$argv = []; | ||
$arg = ''; | ||
$inDQuote = false; | ||
$inSQuote = false; | ||
for ($i = 0; $i < $charCount; $i++) { | ||
$char = substr($command, $i, 1); | ||
|
||
// end of argument | ||
if ($char === ' ' && !$inDQuote && !$inSQuote) { | ||
if (strlen($arg)) { | ||
$argv[] = $arg; | ||
} | ||
$arg = ''; | ||
continue; | ||
} | ||
|
||
// exiting single quote | ||
if ($inSQuote && $char === "'") { | ||
$inSQuote = false; | ||
continue; | ||
} | ||
|
||
// exiting double quote | ||
if ($inDQuote && $char === '"') { | ||
$inDQuote = false; | ||
continue; | ||
} | ||
|
||
// entering double quote | ||
if ($char === '"' && !$inSQuote) { | ||
$inDQuote = true; | ||
continue; | ||
} | ||
|
||
// entering single quote | ||
if ($char === "'" && !$inDQuote) { | ||
$inSQuote = true; | ||
continue; | ||
} | ||
|
||
$arg .= $char; | ||
} | ||
$argv[] = $arg; | ||
|
||
return $argv; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @since 3.5.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\TestSuite; | ||
|
||
use Cake\Console\ConsoleIo; | ||
|
||
/** | ||
* Class that dispatches to the legacy ShellDispatcher using the same signature | ||
* as the newer CommandRunner | ||
*/ | ||
class LegacyCommandRunner | ||
{ | ||
/** | ||
* @var \Cake\Console\ConsoleIo | ||
*/ | ||
protected $_io; | ||
|
||
/** | ||
* Mimics functionality of Cake\Console\CommandRunner | ||
* | ||
* @param array $argv Argument array | ||
* @param ConsoleIo $io ConsoleIo | ||
* @return int | ||
*/ | ||
public function run(array $argv, ConsoleIo $io = null) | ||
{ | ||
$dispatcher = new LegacyShellDispatcher($argv, true, $io); | ||
|
||
return $dispatcher->dispatch(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @since 3.5.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\TestSuite; | ||
|
||
use Cake\Console\ShellDispatcher; | ||
|
||
/** | ||
* Allows injecting mock IO into shells | ||
*/ | ||
class LegacyShellDispatcher extends ShellDispatcher | ||
{ | ||
/** | ||
* @var \Cake\Console\ConsoleIo | ||
*/ | ||
protected $_io; | ||
|
||
/** | ||
* Constructor | ||
* | ||
* @param array $args Argument array | ||
* @param bool $bootstrap Initialize environment | ||
* @param \Cake\Console\ConsoleIo $io ConsoleIo | ||
*/ | ||
public function __construct($args = [], $bootstrap = true, $io = null) | ||
{ | ||
$this->_io = $io; | ||
parent::__construct($args, $bootstrap); | ||
} | ||
|
||
/** | ||
* Injects mock and stub io components into the shell | ||
* | ||
* @param string $className Class name | ||
* @param string $shortName Short name | ||
* @return \Cake\Console\Shell | ||
*/ | ||
protected function _createShell($className, $shortName) | ||
{ | ||
list($plugin) = pluginSplit($shortName); | ||
$instance = new $className($this->_io); | ||
$instance->plugin = trim($plugin, '.'); | ||
|
||
return $instance; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.