Browse Source

Merge branch 'master' into 3.next

Mark Story 8 years ago
parent
commit
5564febbd7

+ 12 - 0
.gitattributes

@@ -8,13 +8,18 @@
 *.php text
 *.default text
 *.ctp text
+*.sql text
 *.md text
 *.po text
 *.js text
 *.css text
 *.ini text
+*.properties text
 *.txt text
 *.xml text
+*.svg text
+*.yml text
+.htaccess text
 
 # Declare files that will always have CRLF line endings on checkout.
 *.bat eol=crlf
@@ -28,6 +33,13 @@
 *.gif binary
 *.ico binary
 *.mo binary
+*.pdf binary
+*.phar binary
+*.woff binary
+*.woff2 binary
+*.ttf binary
+*.otf binary
+*.eot binary
 
 # Remove files for archives generated using `git archive`
 appveyor.yml export-ignore

+ 4 - 0
src/Console/CommandRunner.php

@@ -23,6 +23,7 @@ use Cake\Core\ConsoleApplicationInterface;
 use Cake\Event\EventManagerTrait;
 use Cake\Shell\HelpShell;
 use Cake\Shell\VersionShell;
+use Cake\Utility\Inflector;
 use RuntimeException;
 
 /**
@@ -170,6 +171,9 @@ class CommandRunner
             $name = $this->aliases[$name];
         }
         if (!$commands->has($name)) {
+            $name = Inflector::underscore($name);
+        }
+        if (!$commands->has($name)) {
             throw new RuntimeException(
                 "Unknown command `{$this->root} {$name}`." .
                 " Run `{$this->root} --help` to get the list of valid commands."

+ 3 - 2
src/Console/Shell.php

@@ -33,7 +33,7 @@ use ReflectionMethod;
  *
  * Is the equivalent of Cake\Controller\Controller on the command line.
  *
- * @method int main()
+ * @method int|bool main()
  */
 class Shell
 {
@@ -508,7 +508,8 @@ class Shell
             return $this->main(...$this->args);
         }
 
-        $this->err($this->OptionParser->help($command));
+        $this->err('No subcommand provided. Choose one of the available subcommands.', 2);
+        $this->out($this->OptionParser->help($command));
 
         return false;
     }

+ 1 - 1
src/Error/Debugger.php

@@ -792,7 +792,7 @@ class Debugger
             $file = $files[1];
         }
         if ($file) {
-            $code = static::excerpt($file['file'], $file['line'] - 1, 1);
+            $code = static::excerpt($file['file'], $file['line'], 1);
         }
         $trace = static::trace(['start' => $data['start'], 'depth' => '20']);
         $insertOpts = ['before' => '{:', 'after' => '}'];

+ 15 - 4
src/Http/Client.php

@@ -20,6 +20,7 @@ use Cake\Http\Client\CookieCollection;
 use Cake\Http\Client\Request;
 use Cake\Http\Cookie\CookieInterface;
 use Cake\Utility\Hash;
+use InvalidArgumentException;
 use Zend\Diactoros\Uri;
 
 /**
@@ -192,6 +193,9 @@ class Client
      */
     public function addCookie(CookieInterface $cookie)
     {
+        if (!$cookie->getDomain() || !$cookie->getPath()) {
+            throw new InvalidArgumentException('Cookie must have a domain and a path set.');
+        }
         $this->_cookies = $this->_cookies->add($cookie);
 
         return $this;
@@ -402,7 +406,8 @@ class Client
                 $locationUrl = $this->buildUrl($location, [], [
                     'host' => $url->getHost(),
                     'port' => $url->getPort(),
-                    'scheme' => $url->getScheme()
+                    'scheme' => $url->getScheme(),
+                    'protocolRelative' => true
                 ]);
 
                 $request = $request->withUri(new Uri($locationUrl));
@@ -448,15 +453,21 @@ class Client
             $url .= $q;
             $url .= is_string($query) ? $query : http_build_query($query);
         }
-        if (preg_match('#^https?://#', $url)) {
-            return $url;
-        }
         $defaults = [
             'host' => null,
             'port' => null,
             'scheme' => 'http',
+            'protocolRelative' => false
         ];
         $options += $defaults;
+
+        if ($options['protocolRelative'] && preg_match('#^//#', $url)) {
+            $url = $options['scheme'] . ':' . $url;
+        }
+        if (preg_match('#^https?://#', $url)) {
+            return $url;
+        }
+
         $defaultPorts = [
             'http' => 80,
             'https' => 443

+ 1 - 1
src/Http/Cookie/CookieCollection.php

@@ -219,7 +219,7 @@ class CookieCollection implements IteratorAggregate, Countable
         $cookies = $this->findMatchingCookies(
             $uri->getScheme(),
             $uri->getHost(),
-            $uri->getPath()
+            $uri->getPath() ?: '/'
         );
         $cookies = array_merge($cookies, $extraCookies);
         $cookiePairs = [];

+ 1 - 1
src/Network/Session.php

@@ -455,7 +455,7 @@ class Session
      */
     public function id($id = null)
     {
-        if ($id !== null && !headers_sent()) {
+        if ($id !== null && ($this->_isCLI || !headers_sent())) {
             session_id($id);
         }
 

+ 2 - 2
src/Routing/Middleware/RoutingMiddleware.php

@@ -48,7 +48,7 @@ class RoutingMiddleware
     }
 
     /**
-     * Trigger the application's routes() hook if the application exists.
+     * Trigger the application's routes() hook if the application exists and Router isn't initialized.
      *
      * If the middleware is created without an Application, routes will be
      * loaded via the automatic route loading that pre-dates the routes() hook.
@@ -57,7 +57,7 @@ class RoutingMiddleware
      */
     protected function loadRoutes()
     {
-        if ($this->app) {
+        if ($this->app && !Router::$initialized) {
             $builder = Router::createRouteBuilder('/');
             $this->app->routes($builder);
             // Prevent routes from being loaded again

+ 13 - 8
src/Utility/Text.php

@@ -35,29 +35,34 @@ class Text
      * Warning: This method should not be used as a random seed for any cryptographic operations.
      * Instead you should use the openssl or mcrypt extensions.
      *
+     * It should also not be used to create identifiers that have security implications, such as
+     * 'unguessable' URL identifiers. Instead you should use `Security::randomBytes()` for that.
+     *
      * @see https://www.ietf.org/rfc/rfc4122.txt
      * @return string RFC 4122 UUID
      * @copyright Matt Farina MIT License https://github.com/lootils/uuid/blob/master/LICENSE
      */
     public static function uuid()
     {
+        $random = function_exists('random_int') ? 'random_int' : 'mt_rand';
+
         return sprintf(
             '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
             // 32 bits for "time_low"
-            mt_rand(0, 65535),
-            mt_rand(0, 65535),
+            $random(0, 65535),
+            $random(0, 65535),
             // 16 bits for "time_mid"
-            mt_rand(0, 65535),
+            $random(0, 65535),
             // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
-            mt_rand(0, 4095) | 0x4000,
+            $random(0, 4095) | 0x4000,
             // 16 bits, 8 bits for "clk_seq_hi_res",
             // 8 bits for "clk_seq_low",
             // two most significant bits holds zero and one for variant DCE1.1
-            mt_rand(0, 0x3fff) | 0x8000,
+            $random(0, 0x3fff) | 0x8000,
             // 48 bits for "node"
-            mt_rand(0, 65535),
-            mt_rand(0, 65535),
-            mt_rand(0, 65535)
+            $random(0, 65535),
+            $random(0, 65535),
+            $random(0, 65535)
         );
     }
 

+ 2 - 1
src/View/Helper/HtmlHelper.php

@@ -346,7 +346,8 @@ class HtmlHelper extends Helper
     {
         $escapeTitle = true;
         if ($url !== null) {
-            $url = $this->Url->build($url);
+            $url = $this->Url->build($url, $options);
+            unset($options['fullBase']);
         } else {
             $url = $this->Url->build($title);
             $title = htmlspecialchars_decode($url, ENT_QUOTES);

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

@@ -209,6 +209,29 @@ class CommandRunnerTest extends TestCase
     }
 
     /**
+     * Test running a valid command and that backwards compatible
+     * inflection is hooked up.
+     *
+     * @return void
+     */
+    public function testRunValidCommandInflection()
+    {
+        $app = $this->getMockBuilder(BaseApplication::class)
+            ->setMethods(['middleware', 'bootstrap'])
+            ->setConstructorArgs([$this->config])
+            ->getMock();
+
+        $output = new ConsoleOutput();
+
+        $runner = new CommandRunner($app, 'cake');
+        $result = $runner->run(['cake', 'OrmCache', 'build'], $this->getMockIo($output));
+        $this->assertSame(Shell::CODE_SUCCESS, $result);
+
+        $contents = implode("\n", $output->messages());
+        $this->assertContains('Cache', $contents);
+    }
+
+    /**
      * Test running a valid raising an error
      *
      * @return void

+ 6 - 6
tests/TestCase/Console/ShellTest.php

@@ -1049,7 +1049,7 @@ TEXT;
     public function testRunCommandBaseClassMethod()
     {
         $shell = $this->getMockBuilder('Cake\Console\Shell')
-            ->setMethods(['startup', 'getOptionParser', 'err', 'hr'])
+            ->setMethods(['startup', 'getOptionParser', 'out', 'hr'])
             ->disableOriginalConstructor()
             ->getMock();
 
@@ -1062,7 +1062,7 @@ TEXT;
         $shell->expects($this->once())->method('getOptionParser')
             ->will($this->returnValue($parser));
         $shell->expects($this->never())->method('hr');
-        $shell->expects($this->once())->method('err');
+        $shell->expects($this->once())->method('out');
 
         $shell->runCommand(['hr']);
     }
@@ -1075,7 +1075,7 @@ TEXT;
     public function testRunCommandMissingMethod()
     {
         $shell = $this->getMockBuilder('Cake\Console\Shell')
-            ->setMethods(['startup', 'getOptionParser', 'err', 'hr'])
+            ->setMethods(['startup', 'getOptionParser', 'out', 'hr'])
             ->disableOriginalConstructor()
             ->getMock();
         $shell->io($this->getMockBuilder('Cake\Console\ConsoleIo')->getMock());
@@ -1086,7 +1086,7 @@ TEXT;
         $parser->expects($this->once())->method('help');
         $shell->expects($this->once())->method('getOptionParser')
             ->will($this->returnValue($parser));
-        $shell->expects($this->once())->method('err');
+        $shell->expects($this->once())->method('out');
 
         $result = $shell->runCommand(['idontexist']);
         $this->assertFalse($result);
@@ -1127,7 +1127,7 @@ TEXT;
     public function testRunCommandNotCallUnexposedTask()
     {
         $shell = $this->getMockBuilder('Cake\Console\Shell')
-            ->setMethods(['startup', 'hasTask', 'err'])
+            ->setMethods(['startup', 'hasTask', 'out'])
             ->disableOriginalConstructor()
             ->getMock();
         $shell->io($this->getMockBuilder('Cake\Console\ConsoleIo')->getMock());
@@ -1143,7 +1143,7 @@ TEXT;
             ->method('hasTask')
             ->will($this->returnValue(true));
         $shell->expects($this->never())->method('startup');
-        $shell->expects($this->once())->method('err');
+        $shell->expects($this->once())->method('out');
         $shell->RunCommand = $task;
 
         $result = $shell->runCommand(['run_command', 'one']);

+ 25 - 0
tests/TestCase/Error/DebuggerTest.php

@@ -207,6 +207,31 @@ class DebuggerTest extends TestCase
     }
 
     /**
+     * Tests that the correct line is being highlighted.
+     *
+     * @return void
+     */
+    public function testOutputErrorLineHighlight()
+    {
+        Debugger::outputAs('js');
+
+        ob_start();
+        $debugger = Debugger::getInstance();
+        $data = [
+            'level' => E_NOTICE,
+            'code' => E_NOTICE,
+            'file' => __FILE__,
+            'line' => __LINE__,
+            'description' => 'Error description',
+            'start' => 1
+        ];
+        $debugger->outputError($data);
+        $result = ob_get_clean();
+
+        $this->assertRegExp('#^\<span class\="code\-highlight"\>.*outputError.*\</span\>$#m', $result);
+    }
+
+    /**
      * Tests that changes in output formats using Debugger::output() change the templates used.
      *
      * @return void

+ 58 - 1
tests/TestCase/Http/ClientTest.php

@@ -137,6 +137,27 @@ class ClientTest extends TestCase
                 [],
                 'query string data with some already on the url.'
             ],
+            [
+                'http://example.com/test.html',
+                '//test.html',
+                [],
+                [
+                    'scheme' => 'http',
+                    'host' => 'example.com',
+                    'protocolRelative' => false
+                ],
+                'url with a double slash',
+            ],
+            [
+                'http://example.com/test.html',
+                '//example.com/test.html',
+                [],
+                [
+                    'scheme' => 'http',
+                    'protocolRelative' => true
+                ],
+                'protocol relative url',
+            ],
         ];
     }
 
@@ -644,7 +665,43 @@ class ClientTest extends TestCase
     public function testAddCookie()
     {
         $client = new Client();
-        $cookie = new Cookie('foo');
+        $cookie = new Cookie('foo', '', null, '/', 'example.com');
+
+        $this->assertFalse($client->cookies()->has('foo'));
+
+        $client->addCookie($cookie);
+        $this->assertTrue($client->cookies()->has('foo'));
+    }
+
+    /**
+     * Test addCookie() method without a domain.
+     *
+     * @return void
+     * @expectedException InvalidArgumentException
+     * @expectedExceptionMessage Cookie must have a domain and a path set.
+     */
+    public function testAddCookieWithoutDomain()
+    {
+        $client = new Client();
+        $cookie = new Cookie('foo', '', null, '/', '');
+
+        $this->assertFalse($client->cookies()->has('foo'));
+
+        $client->addCookie($cookie);
+        $this->assertTrue($client->cookies()->has('foo'));
+    }
+
+    /**
+     * Test addCookie() method without a path.
+     *
+     * @return void
+     * @expectedException InvalidArgumentException
+     * @expectedExceptionMessage Cookie must have a domain and a path set.
+     */
+    public function testAddCookieWithoutPath()
+    {
+        $client = new Client();
+        $cookie = new Cookie('foo', '', null, '', 'example.com');
 
         $this->assertFalse($client->cookies()->has('foo'));
 

+ 9 - 4
tests/TestCase/Http/Cookie/CookieCollectionTest.php

@@ -336,24 +336,29 @@ class CookieCollectionTest extends TestCase
     {
         $collection = new CookieCollection();
         $collection = $collection
+            ->add(new Cookie('default', '1', null, '/', 'example.com'))
             ->add(new Cookie('api', 'A', null, '/api', 'example.com'))
             ->add(new Cookie('blog', 'b', null, '/blog', 'blog.example.com'))
             ->add(new Cookie('expired', 'ex', new DateTime('-2 seconds'), '/', 'example.com'));
         $request = new ClientRequest('http://example.com/api');
         $request = $collection->addToRequest($request);
-        $this->assertSame('api=A', $request->getHeaderLine('Cookie'));
+        $this->assertSame('default=1; api=A', $request->getHeaderLine('Cookie'));
 
         $request = new ClientRequest('http://example.com/');
         $request = $collection->addToRequest($request);
-        $this->assertSame('', $request->getHeaderLine(''));
+        $this->assertSame('default=1', $request->getHeaderLine('Cookie'));
+
+        $request = new ClientRequest('http://example.com');
+        $request = $collection->addToRequest($request);
+        $this->assertSame('default=1', $request->getHeaderLine('Cookie'));
 
         $request = new ClientRequest('http://example.com/blog');
         $request = $collection->addToRequest($request);
-        $this->assertSame('', $request->getHeaderLine('Cookie'), 'domain matching should apply');
+        $this->assertSame('default=1', $request->getHeaderLine('Cookie'), 'domain matching should apply');
 
         $request = new ClientRequest('http://foo.blog.example.com/blog');
         $request = $collection->addToRequest($request);
-        $this->assertSame('blog=b', $request->getHeaderLine('Cookie'));
+        $this->assertSame('default=1; blog=b', $request->getHeaderLine('Cookie'));
     }
 
     /**

+ 25 - 44
tests/TestCase/Shell/CommandListShellTest.php

@@ -14,16 +14,15 @@
  */
 namespace Cake\Test\TestCase\Shell;
 
-use Cake\Console\ConsoleIo;
+use Cake\Console\Shell;
 use Cake\Core\Configure;
 use Cake\Core\Plugin;
-use Cake\TestSuite\Stub\ConsoleOutput;
-use Cake\TestSuite\TestCase;
+use Cake\TestSuite\ConsoleIntegrationTestCase;
 
 /**
  * CommandListShellTest
  */
-class CommandListShellTest extends TestCase
+class CommandListShellTest extends ConsoleIntegrationTestCase
 {
 
     /**
@@ -35,19 +34,6 @@ class CommandListShellTest extends TestCase
     {
         parent::setUp();
         Plugin::load(['TestPlugin', 'TestPluginTwo']);
-
-        $this->out = new ConsoleOutput();
-        $io = new ConsoleIo($this->out);
-
-        $this->Shell = $this->getMockBuilder('Cake\Shell\CommandListShell')
-            ->setMethods(['in', 'err', '_stop', 'clear'])
-            ->setConstructorArgs([$io])
-            ->getMock();
-
-        $this->Shell->Command = $this->getMockBuilder('Cake\Shell\Task\CommandTask')
-            ->setMethods(['in', '_stop', 'err', 'clear'])
-            ->setConstructorArgs([$io])
-            ->getMock();
     }
 
     /**
@@ -58,7 +44,6 @@ class CommandListShellTest extends TestCase
     public function tearDown()
     {
         parent::tearDown();
-        unset($this->Shell);
         Plugin::unload();
     }
 
@@ -69,21 +54,21 @@ class CommandListShellTest extends TestCase
      */
     public function testMain()
     {
-        $this->Shell->main();
-        $output = $this->out->messages();
-        $output = implode("\n", $output);
+        $this->exec('command_list');
 
         $expected = "/\[.*TestPlugin.*\] example/";
-        $this->assertRegExp($expected, $output);
+        $this->assertOutputRegExp($expected);
 
         $expected = "/\[.*TestPluginTwo.*\] example, unique, welcome/";
-        $this->assertRegExp($expected, $output);
+        $this->assertOutputRegExp($expected);
 
         $expected = "/\[.*CORE.*\] cache, help, i18n, orm_cache, plugin, routes, schema_cache, server/";
-        $this->assertRegExp($expected, $output);
+        $this->assertOutputRegExp($expected);
 
         $expected = "/\[.*app.*\] i18m, integration, sample/";
-        $this->assertRegExp($expected, $output);
+        $this->assertOutputRegExp($expected);
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertErrorEmpty();
     }
 
     /**
@@ -95,16 +80,16 @@ class CommandListShellTest extends TestCase
     public function testMainAppPriority()
     {
         rename(APP . 'Shell' . DS . 'I18mShell.php', APP . 'Shell' . DS . 'I18nShell.php');
-        $this->Shell->main();
-        $output = $this->out->messages();
-        $output = implode("\n", $output);
+        $this->exec('command_list');
         rename(APP . 'Shell' . DS . 'I18nShell.php', APP . 'Shell' . DS . 'I18mShell.php');
 
         $expected = "/\[.*CORE.*\] cache, help, orm_cache, plugin, routes, schema_cache, server/";
-        $this->assertRegExp($expected, $output);
+        $this->assertOutputRegExp($expected);
 
         $expected = "/\[.*app.*\] i18n, integration, sample/";
-        $this->assertRegExp($expected, $output);
+        $this->assertOutputRegExp($expected);
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertErrorEmpty();
     }
 
     /**
@@ -114,20 +99,18 @@ class CommandListShellTest extends TestCase
      */
     public function testMainXml()
     {
-        $this->Shell->params['xml'] = true;
-        $this->Shell->main();
-
-        $output = $this->out->messages();
-        $output = implode("\n", $output);
+        $this->exec('command_list --xml');
 
         $find = '<shell name="sample" call_as="sample" provider="app" help="sample -h"';
-        $this->assertContains($find, $output);
+        $this->assertOutputContains($find);
 
         $find = '<shell name="orm_cache" call_as="orm_cache" provider="CORE" help="orm_cache -h"';
-        $this->assertContains($find, $output);
+        $this->assertOutputContains($find);
 
         $find = '<shell name="welcome" call_as="TestPluginTwo.welcome" provider="TestPluginTwo" help="TestPluginTwo.welcome -h"';
-        $this->assertContains($find, $output);
+        $this->assertOutputContains($find);
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertErrorEmpty();
     }
 
     /**
@@ -137,12 +120,10 @@ class CommandListShellTest extends TestCase
      */
     public function testMainVersion()
     {
-        $this->Shell->params['version'] = true;
-        $this->Shell->main();
-        $output = $this->out->messages();
-        $output = implode("\n", $output);
-
+        $this->exec('command_list --version');
         $expected = Configure::version();
-        $this->assertEquals($expected, $output);
+        $this->assertOutputContains($expected);
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertErrorEmpty();
     }
 }

+ 23 - 39
tests/TestCase/Shell/HelpShellTest.php

@@ -14,17 +14,14 @@
  */
 namespace Cake\Test\TestCase\Shell;
 
-use Cake\Console\CommandCollection;
-use Cake\Console\ConsoleIo;
+use Cake\Console\Shell;
 use Cake\Core\Plugin;
-use Cake\Shell\HelpShell;
-use Cake\TestSuite\Stub\ConsoleOutput;
-use Cake\TestSuite\TestCase;
+use Cake\TestSuite\ConsoleIntegrationTestCase;
 
 /**
  * HelpShell test.
  */
-class HelpShellTest extends TestCase
+class HelpShellTest extends ConsoleIntegrationTestCase
 {
     /**
      * setup method
@@ -35,16 +32,8 @@ class HelpShellTest extends TestCase
     {
         parent::setUp();
         $this->setAppNamespace();
+        $this->useCommandRunner(true);
         Plugin::load('TestPlugin');
-
-        $this->out = new ConsoleOutput();
-        $this->err = new ConsoleOutput();
-        $this->io = new ConsoleIo($this->out, $this->err);
-        $this->shell = new HelpShell($this->io);
-
-        $commands = new CommandCollection();
-        $commands->addMany($commands->autoDiscover());
-        $this->shell->setCommandCollection($commands);
     }
 
     /**
@@ -54,11 +43,9 @@ class HelpShellTest extends TestCase
      */
     public function testMainNoCommandsFallback()
     {
-        $shell = new HelpShell($this->io);
-        $this->assertNull($shell->main());
-
-        $output = implode("\n", $this->out->messages());
-        $this->assertOutput($output);
+        $this->exec('help');
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertCommandList();
     }
 
     /**
@@ -68,10 +55,9 @@ class HelpShellTest extends TestCase
      */
     public function testMain()
     {
-        $this->assertNull($this->shell->main());
-
-        $output = implode("\n", $this->out->messages());
-        $this->assertOutput($output);
+        $this->exec('help');
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertCommandList();
     }
 
     /**
@@ -80,14 +66,14 @@ class HelpShellTest extends TestCase
      * @param string $output The output to check.
      * @return void
      */
-    protected function assertOutput($output)
+    protected function assertCommandList()
     {
-        $this->assertContains('- sample', $output, 'app shell');
-        $this->assertContains('- test_plugin.sample', $output, 'Long plugin name');
-        $this->assertContains('- routes', $output, 'core shell');
-        $this->assertContains('- test_plugin.example', $output, 'Long plugin name');
-        $this->assertContains('To run a command', $output, 'more info present');
-        $this->assertContains('To get help', $output, 'more info present');
+        $this->assertOutputContains('- sample', 'app shell');
+        $this->assertOutputContains('- test_plugin.sample', 'Long plugin name');
+        $this->assertOutputContains('- routes', 'core shell');
+        $this->assertOutputContains('- test_plugin.example', 'Long plugin name');
+        $this->assertOutputContains('To run a command', 'more info present');
+        $this->assertOutputContains('To get help', 'more info present');
     }
 
     /**
@@ -97,19 +83,17 @@ class HelpShellTest extends TestCase
      */
     public function testMainAsXml()
     {
-        $this->shell->params['xml'] = true;
-        $this->shell->main();
-        $output = implode("\n", $this->out->messages());
-
-        $this->assertContains('<shells>', $output);
+        $this->exec('help --xml');
+        $this->assertExitCode(Shell::CODE_SUCCESS);
+        $this->assertOutputContains('<shells>');
 
         $find = '<shell name="sample" call_as="sample" provider="TestApp\Shell\SampleShell" help="sample -h"';
-        $this->assertContains($find, $output);
+        $this->assertOutputContains($find);
 
         $find = '<shell name="orm_cache" call_as="orm_cache" provider="Cake\Shell\OrmCacheShell" help="orm_cache -h"';
-        $this->assertContains($find, $output);
+        $this->assertOutputContains($find);
 
         $find = '<shell name="test_plugin.sample" call_as="test_plugin.sample" provider="TestPlugin\Shell\SampleShell" help="test_plugin.sample -h"';
-        $this->assertContains($find, $output);
+        $this->assertOutputContains($find);
     }
 }

+ 19 - 0
tests/TestCase/TestSuite/IntegrationTestCaseTest.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\Test\TestCase\TestSuite;
 
+use Cake\Core\Configure;
 use Cake\Event\EventManager;
 use Cake\Http\Response;
 use Cake\Routing\DispatcherFactory;
@@ -190,6 +191,24 @@ class IntegrationTestCaseTest extends IntegrationTestCase
     }
 
     /**
+     * Test sending get request and using default `test_app/config/routes.php`.
+     *
+     * @return void
+     */
+    public function testGetUsingApplicationWithDefaultRoutes()
+    {
+        // first clean routes to have Router::$initailized === false
+        Router::reload();
+
+        $this->useHttpServer(true);
+        $this->configApplication(Configure::read('App.namespace') . '\ApplicationWithDefaultRoutes', null);
+
+        $this->get('/some_alias');
+        $this->assertResponseOk();
+        $this->assertEquals('5', $this->_getBodyAsString());
+    }
+
+    /**
      * Test sending head requests.
      *
      * @return void

+ 8 - 0
tests/TestCase/View/Helper/HtmlHelperTest.php

@@ -310,6 +310,14 @@ class HtmlHelperTest extends TestCase
         $result = $this->Html->link('say hello to 0123465-798', 'sms:0123465-798?body=hello "cakephp"');
         $expected = ['a' => ['href' => 'sms:0123465-798?body=hello &quot;cakephp&quot;'], 'say hello to 0123465-798', '/a'];
         $this->assertHtml($expected, $result);
+
+        $result = $this->Html->link('Home', '/', ['fullBase' => true]);
+        $expected = ['a' => ['href' => 'http://localhost/'], 'Home', '/a'];
+        $this->assertHtml($expected, $result);
+
+        $result = $this->Html->link('Home', '/', ['fullBase' => false]);
+        $expected = ['a' => ['href' => '/'], 'Home', '/a'];
+        $this->assertHtml($expected, $result);
     }
 
     /**

+ 48 - 0
tests/test_app/TestApp/ApplicationWithDefaultRoutes.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         3.5.2
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace TestApp;
+
+use Cake\Http\BaseApplication;
+use Cake\Routing\Middleware\RoutingMiddleware;
+
+/**
+ * Simple Application class doing nothing that:
+ *
+ * - do nothing in bootstrap
+ * - add just `RoutingMiddleware` to middleware queue
+ *
+ * Useful to test app using the default `BaseApplication::routes()` method
+ */
+class ApplicationWithDefaultRoutes extends BaseApplication
+{
+    /**
+     * Bootstrap hook.
+     *
+     * Nerfed as this is for IntegrationTestCase testing.
+     *
+     * @return void
+     */
+    public function bootstrap()
+    {
+        // Do nothing.
+    }
+
+    public function middleware($middlewareQueue)
+    {
+        $middlewareQueue->add(new RoutingMiddleware($this));
+
+        return $middlewareQueue;
+    }
+}

+ 10 - 1
tests/test_app/config/routes.php

@@ -17,6 +17,15 @@ use Cake\Routing\Router;
 Router::extensions('json');
 Router::scope('/', function ($routes) {
     $routes->connect('/', ['controller' => 'pages', 'action' => 'display', 'home']);
-    $routes->connect('/some_alias', ['controller' => 'tests_apps', 'action' => 'some_method']);
+    $routes->connect(
+        '/some_alias',
+        [
+            'controller' => 'tests_apps',
+            'action' => 'some_method'],
+        [
+            '_name' => 'some_alias',
+            'routeClass' => 'InflectedRoute',
+        ]
+    );
     $routes->fallbacks();
 });