Browse Source

Add more test coverage for CommandRunner.

Cover failure/working paths for shell invocation.
Mark Story 8 years ago
parent
commit
a7f9372c2a

+ 33 - 4
src/Console/CommandRunner.php

@@ -15,6 +15,9 @@
 namespace Cake\Console;
 
 use Cake\Console\CommandCollection;
+use Cake\Console\ConsoleIo;
+use Cake\Console\Exception\StopException;
+use Cake\Console\Shell;
 use Cake\Http\BaseApplication;
 use RuntimeException;
 
@@ -41,10 +44,11 @@ class CommandRunner
      * Run the command contained in $argv.
      *
      * @param array $argv The arguments from the CLI environment.
+     * @param \Cake\Console\ConsoleIo $io The ConsoleIo instance. Used primarily for testing.
      * @return int The exit code of the command.
      * @throws \RuntimeException
      */
-    public function run(array $argv)
+    public function run(array $argv, ConsoleIo $io = null)
     {
         $this->app->bootstrap();
 
@@ -62,10 +66,30 @@ class CommandRunner
                 "Unknown root command{$command}. Was expecting `{$this->root}`."
             );
         }
-        // Remove the root command
+        $io = $io ?: new ConsoleIo();
+
+        // Remove the root executable segment
         array_shift($argv);
 
-        $shell = $this->getShell($commands, $argv);
+        $shell = $this->getShell($io, $commands, $argv);
+
+        // Remove the command name segment
+        array_shift($argv);
+        try {
+            $shell->initialize();
+            $result = $shell->runCommand($argv, true);
+        } catch (StopException $e) {
+            return $e->getCode();
+        }
+
+        if ($result === null || $result === true) {
+            return Shell::CODE_SUCCESS;
+        }
+        if (is_int($result)) {
+            return $result;
+        }
+
+        return Shell::CODE_ERROR;
     }
 
     /**
@@ -73,7 +97,7 @@ class CommandRunner
      *
      * @return \Cake\Console\Shell
      */
-    protected function getShell(CommandCollection $commands, array $argv)
+    protected function getShell(ConsoleIo $io, CommandCollection $commands, array $argv)
     {
         $command = array_shift($argv);
         if (!$commands->has($command)) {
@@ -82,5 +106,10 @@ class CommandRunner
                 " Run `{$this->root} --help` to get the list of valid commands."
             );
         }
+        $classOrInstance = $commands->get($command);
+        if (is_string($classOrInstance)) {
+            return new $classOrInstance($io);
+        }
+        return $classOrInstance;
     }
 }

+ 80 - 0
tests/TestCase/Console/CommandRunnerTest.php

@@ -16,9 +16,12 @@ namespace Cake\Test\Console;
 
 use Cake\Console\CommandCollection;
 use Cake\Console\CommandRunner;
+use Cake\Console\ConsoleIo;
+use Cake\Console\Shell;
 use Cake\Core\Configure;
 use Cake\Http\BaseApplication;
 use Cake\TestSuite\TestCase;
+use Cake\TestSuite\Stub\ConsoleOutput;
 
 /**
  * Test case for the CommandCollection
@@ -143,4 +146,81 @@ class CommandRunnerTest extends TestCase
     {
         $this->markTestIncomplete();
     }
+
+    /**
+     * Test running a valid command
+     *
+     * @return void
+     */
+    public function testRunValidCommand()
+    {
+        $app = $this->getMockBuilder(BaseApplication::class)
+            ->setMethods(['middleware', 'bootstrap'])
+            ->setConstructorArgs([$this->config])
+            ->getMock();
+
+        $output = new ConsoleOutput();
+
+        $runner = new CommandRunner($app, 'cake');
+        $result = $runner->run(['cake', 'routes'], $this->getMockIo($output));
+        $this->assertSame(Shell::CODE_SUCCESS, $result);
+
+        $contents = implode("\n", $output->messages());
+        $this->assertContains('URI template', $contents);
+        $this->assertContains('Welcome to CakePHP', $contents);
+    }
+
+    /**
+     * Test running a valid raising an error
+     *
+     * @return void
+     */
+    public function testRunValidCommandWithAbort()
+    {
+        $app = $this->getMockBuilder(BaseApplication::class)
+            ->setMethods(['middleware', 'bootstrap', 'console'])
+            ->setConstructorArgs([$this->config])
+            ->getMock();
+
+        $commands = new CommandCollection(['failure' => 'TestApp\Shell\SampleShell']);
+        $app->method('console')->will($this->returnValue($commands));
+
+        $output = new ConsoleOutput();
+
+        $runner = new CommandRunner($app, 'cake');
+        $result = $runner->run(['cake', 'failure', 'with_abort'], $this->getMockIo($output));
+        $this->assertSame(Shell::CODE_ERROR, $result);
+    }
+
+    /**
+     * Test returning a non-zero value
+     *
+     * @return void
+     */
+    public function testRunValidCommandReturnInteger()
+    {
+        $app = $this->getMockBuilder(BaseApplication::class)
+            ->setMethods(['middleware', 'bootstrap', 'console'])
+            ->setConstructorArgs([$this->config])
+            ->getMock();
+
+        $commands = new CommandCollection(['failure' => 'TestApp\Shell\SampleShell']);
+        $app->method('console')->will($this->returnValue($commands));
+
+        $output = new ConsoleOutput();
+
+        $runner = new CommandRunner($app, 'cake');
+        $result = $runner->run(['cake', 'failure', 'returnValue'], $this->getMockIo($output));
+        $this->assertSame(99, $result);
+    }
+
+    protected function getMockIo($output)
+    {
+        $io = $this->getMockBuilder(ConsoleIo::class)
+            ->setConstructorArgs([$output, $output, null, null])
+            ->setMethods(['in'])
+            ->getMock();
+
+        return $io;
+    }
 }

+ 10 - 0
tests/test_app/TestApp/Shell/SampleShell.php

@@ -46,4 +46,14 @@ class SampleShell extends Shell
     {
         $this->out('This is the example method called from TestPlugin.SampleShell');
     }
+
+    public function withAbort()
+    {
+        $this->abort('Bad things');
+    }
+
+    public function returnValue()
+    {
+        return 99;
+    }
 }