diff --git a/app/Config/core.php b/app/Config/core.php index 691b02606..97d26629d 100644 --- a/app/Config/core.php +++ b/app/Config/core.php @@ -225,8 +225,8 @@ Configure::write('Acl.database', 'default'); /** - * If you are on PHP 5.3 uncomment this line and correct your server timezone - * to fix the date & time related errors. + * Uncomment this line and correct your server timezone to fix + * any date & time related errors. */ //date_default_timezone_set('UTC'); diff --git a/app/Config/database.php.default b/app/Config/database.php.default index 3ce441be3..11fca6fb5 100644 --- a/app/Config/database.php.default +++ b/app/Config/database.php.default @@ -27,14 +27,14 @@ * Database configuration class. * You can specify multiple configurations for production, development and testing. * - * driver => The name of a supported driver; valid options are as follows: + * datasource => The name of a supported datasource; valid options are as follows: * Database/Mysql - MySQL 4 & 5, * Database/Sqlite - SQLite (PHP5 only), * Database/Postgres - PostgreSQL 7 and higher, * Database/Sqlserver - Microsoft SQL Server 2005 and higher * - * You can add custom database drivers (or override existing drivers) by adding the - * appropriate file to app/Model/Datasource/Database. Drivers should be named 'MyDriver.php', + * You can add custom database datasources (or override existing datasources) by adding the + * appropriate file to app/Model/Datasource/Database. Datasources should be named 'MyDatasource.php', * * * persistent => true / false diff --git a/lib/Cake/Console/Command/AclShell.php b/lib/Cake/Console/Command/AclShell.php index 42fa202f1..b2dec95d3 100644 --- a/lib/Cake/Console/Command/AclShell.php +++ b/lib/Cake/Console/Command/AclShell.php @@ -485,7 +485,7 @@ class AclShell extends Shell { ) ) ))->addSubcommand('initdb', array( - 'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema run create DbAcl') + 'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema create DbAcl') ))->epilog( array( 'Node and parent arguments can be in one of the following formats:', diff --git a/lib/Cake/Console/Command/Task/ControllerTask.php b/lib/Cake/Console/Command/Task/ControllerTask.php index 1946b0890..d7797024c 100644 --- a/lib/Cake/Console/Command/Task/ControllerTask.php +++ b/lib/Cake/Console/Command/Task/ControllerTask.php @@ -219,7 +219,7 @@ class ControllerTask extends BakeTask { $this->out(__d('cake_console', "Controller Name:\n\t%s", $controllerName)); if (strtolower($useDynamicScaffold) == 'y') { - $this->out("var \$scaffold;"); + $this->out("public \$scaffold;"); } $properties = array( diff --git a/lib/Cake/Console/Command/Task/DbConfigTask.php b/lib/Cake/Console/Command/Task/DbConfigTask.php index 20469bcd3..3d61baaea 100644 --- a/lib/Cake/Console/Command/Task/DbConfigTask.php +++ b/lib/Cake/Console/Command/Task/DbConfigTask.php @@ -104,7 +104,7 @@ class DbConfigTask extends Shell { } } - $driver = $this->in(__d('cake_console', 'Driver:'), array('Mysql', 'Postgres', 'Sqlite', 'Sqlserver'), 'Mysql'); + $datasource = $this->in(__d('cake_console', 'Datasource:'), array('Mysql', 'Postgres', 'Sqlite', 'Sqlserver'), 'Mysql'); $persistent = $this->in(__d('cake_console', 'Persistent Connection?'), array('y', 'n'), 'n'); if (strtolower($persistent) == 'n') { @@ -167,7 +167,7 @@ class DbConfigTask extends Shell { } $schema = ''; - if ($driver == 'postgres') { + if ($datasource == 'postgres') { while ($schema == '') { $schema = $this->in(__d('cake_console', 'Table schema?'), null, 'n'); } @@ -176,7 +176,7 @@ class DbConfigTask extends Shell { $schema = null; } - $config = compact('name', 'driver', 'persistent', 'host', 'login', 'password', 'database', 'prefix', 'encoding', 'port', 'schema'); + $config = compact('name', 'datasource', 'persistent', 'host', 'login', 'password', 'database', 'prefix', 'encoding', 'port', 'schema'); while ($this->_verify($config) == false) { $this->_interactive(); @@ -209,7 +209,7 @@ class DbConfigTask extends Shell { $this->out(__d('cake_console', 'The following database configuration will be created:')); $this->hr(); $this->out(__d('cake_console', "Name: %s", $name)); - $this->out(__d('cake_console', "Driver: %s", $driver)); + $this->out(__d('cake_console', "Datasource: %s", $datasource)); $this->out(__d('cake_console', "Persistent: %s", $persistent)); $this->out(__d('cake_console', "Host: %s", $host)); @@ -314,7 +314,7 @@ class DbConfigTask extends Shell { extract($config); $out .= "\tpublic \${$name} = array(\n"; - $out .= "\t\t'datasource' => 'Database/{$driver}',\n"; + $out .= "\t\t'datasource' => 'Database/{$datasource}',\n"; $out .= "\t\t'persistent' => {$persistent},\n"; $out .= "\t\t'host' => '{$host}',\n"; diff --git a/lib/Cake/Console/Command/Task/ViewTask.php b/lib/Cake/Console/Command/Task/ViewTask.php index 0228c32ad..fff2572e8 100644 --- a/lib/Cake/Console/Command/Task/ViewTask.php +++ b/lib/Cake/Console/Command/Task/ViewTask.php @@ -143,7 +143,7 @@ class ViewTask extends BakeTask { } $adminRoute = $this->Project->getPrefix(); foreach ($methods as $i => $method) { - if ($adminRoute && isset($this->params['admin'])) { + if ($adminRoute && !empty($this->params['admin'])) { if ($scaffoldActions) { $methods[$i] = $adminRoute . $method; continue; @@ -393,6 +393,10 @@ class ViewTask extends BakeTask { if (!empty($this->template) && $action != $this->template) { return $this->template; } + $themePath = $this->Template->getThemePath(); + if (file_exists($themePath . 'views' . DS . $action . '.ctp')) { + return $action; + } $template = $action; $prefixes = Configure::read('Routing.prefixes'); foreach ((array)$prefixes as $prefix) { diff --git a/lib/Cake/Console/Command/TestsuiteShell.php b/lib/Cake/Console/Command/TestsuiteShell.php index e8bd411fd..6363c1a6a 100644 --- a/lib/Cake/Console/Command/TestsuiteShell.php +++ b/lib/Cake/Console/Command/TestsuiteShell.php @@ -148,7 +148,7 @@ class TestsuiteShell extends TestShell { ))->addOption('fixture', array( 'help' => __d('cake_console', 'Choose a custom fixture manager.'), ))->addOption('debug', array( - 'help' => __d('cake_console', 'More verbose output.'), + 'help' => __d('cake_console', 'Enable full output of testsuite. (supported in PHPUnit 3.6.0 and greater)'), )); return $parser; diff --git a/lib/Cake/Console/Command/UpgradeShell.php b/lib/Cake/Console/Command/UpgradeShell.php index 3c619da2d..66d59be5b 100644 --- a/lib/Cake/Console/Command/UpgradeShell.php +++ b/lib/Cake/Console/Command/UpgradeShell.php @@ -214,6 +214,9 @@ class UpgradeShell extends Shell { } $patterns = array(); + App::build(array( + 'View/Helper' => App::core('View/Helper'), + ), App::APPEND); $helpers = App::objects('helper'); $plugins = App::objects('plugin'); $pluginHelpers = array(); @@ -513,6 +516,43 @@ class UpgradeShell extends Shell { $this->_filesRegexpUpdate($patterns); } +/** + * Replace cakeError with built-in exceptions. + * NOTE: this ignores calls where you've passed your own secondary parameters to cakeError(). + * @return void + */ + public function exceptions() { + $controllers = array_diff(App::path('controllers'), App::core('controllers'), array(APP)); + $components = array_diff(App::path('components'), App::core('components')); + + $this->_paths = array_merge($controllers, $components); + + if (!empty($this->params['plugin'])) { + $pluginPath = App::pluginPath($this->params['plugin']); + $this->_paths = array( + $pluginPath . 'controllers' . DS, + $pluginPath . 'controllers' . DS . 'components' .DS, + ); + } + $patterns = array( + array( + '$this->cakeError("error400") -> throw new BadRequestException()', + '/(\$this->cakeError\(["\']error400["\']\));/', + 'throw new BadRequestException();' + ), + array( + '$this->cakeError("error404") -> throw new NotFoundException()', + '/(\$this->cakeError\(["\']error404["\']\));/', + 'throw new NotFoundException();' + ), + array( + '$this->cakeError("error500") -> throw new InternalErrorException()', + '/(\$this->cakeError\(["\']error500["\']\));/', + 'throw new InternalErrorException();' + ), + ); + $this->_filesRegexpUpdate($patterns); + } /** * Move application views files to where they now should be * @@ -662,11 +702,11 @@ class UpgradeShell extends Shell { * @return void */ protected function _findFiles($extensions = '') { + $this->_files = array(); foreach ($this->_paths as $path) { if (!is_dir($path)) { continue; } - $this->_files = array(); $Iterator = new RegexIterator( new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)), '/^.+\.(' . $extensions . ')$/i', @@ -773,6 +813,10 @@ class UpgradeShell extends Shell { ->addSubcommand('components', array( 'help' => __d('cake_console', 'Update components to extend Component class.'), 'parser' => $subcommandParser + )) + ->addSubcommand('exceptions', array( + 'help' => __d('cake_console', 'Replace use of cakeError with exceptions.'), + 'parser' => $subcommandParser )); } } diff --git a/lib/Cake/Console/Shell.php b/lib/Cake/Console/Shell.php index b26628951..53e02c03f 100644 --- a/lib/Cake/Console/Shell.php +++ b/lib/Cake/Console/Shell.php @@ -273,15 +273,12 @@ class Shell extends Object { * @return boolean */ public function hasMethod($name) { - if (empty($this->_reflection)) { - $this->_reflection = new ReflectionClass($this); - } try { - $method = $this->_reflection->getMethod($name); + $method = new ReflectionMethod($this, $name); if (!$method->isPublic() || substr($name, 0, 1) === '_') { return false; } - if ($method->getDeclaringClass() != $this->_reflection) { + if ($method->getDeclaringClass()->name == 'Shell') { return false; } return true; diff --git a/lib/Cake/Console/Templates/skel/Config/core.php b/lib/Cake/Console/Templates/skel/Config/core.php index 691b02606..97d26629d 100644 --- a/lib/Cake/Console/Templates/skel/Config/core.php +++ b/lib/Cake/Console/Templates/skel/Config/core.php @@ -225,8 +225,8 @@ Configure::write('Acl.database', 'default'); /** - * If you are on PHP 5.3 uncomment this line and correct your server timezone - * to fix the date & time related errors. + * Uncomment this line and correct your server timezone to fix + * any date & time related errors. */ //date_default_timezone_set('UTC'); diff --git a/lib/Cake/Console/Templates/skel/Config/database.php.default b/lib/Cake/Console/Templates/skel/Config/database.php.default index 3ce441be3..11fca6fb5 100644 --- a/lib/Cake/Console/Templates/skel/Config/database.php.default +++ b/lib/Cake/Console/Templates/skel/Config/database.php.default @@ -27,14 +27,14 @@ * Database configuration class. * You can specify multiple configurations for production, development and testing. * - * driver => The name of a supported driver; valid options are as follows: + * datasource => The name of a supported datasource; valid options are as follows: * Database/Mysql - MySQL 4 & 5, * Database/Sqlite - SQLite (PHP5 only), * Database/Postgres - PostgreSQL 7 and higher, * Database/Sqlserver - Microsoft SQL Server 2005 and higher * - * You can add custom database drivers (or override existing drivers) by adding the - * appropriate file to app/Model/Datasource/Database. Drivers should be named 'MyDriver.php', + * You can add custom database datasources (or override existing datasources) by adding the + * appropriate file to app/Model/Datasource/Database. Datasources should be named 'MyDatasource.php', * * * persistent => true / false diff --git a/lib/Cake/Model/Datasource/CakeSession.php b/lib/Cake/Model/Datasource/CakeSession.php index b8641c335..f261bb861 100644 --- a/lib/Cake/Model/Datasource/CakeSession.php +++ b/lib/Cake/Model/Datasource/CakeSession.php @@ -539,8 +539,7 @@ class CakeSession { 'cookieTimeout' => 240, 'ini' => array( 'session.use_trans_sid' => 0, - 'session.cookie_path' => self::$path, - 'session.save_handler' => 'files' + 'session.cookie_path' => self::$path ) ), 'cake' => array( diff --git a/lib/Cake/Model/Datasource/Database/Sqlite.php b/lib/Cake/Model/Datasource/Database/Sqlite.php index 6b616f1b7..d69d288c9 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlite.php +++ b/lib/Cake/Model/Datasource/Database/Sqlite.php @@ -250,7 +250,7 @@ class Sqlite extends DboSource { if (in_array($col, array('text', 'integer', 'float', 'boolean', 'timestamp', 'date', 'datetime', 'time'))) { return $col; } - if (strpos($col, 'varchar') !== false || strpos($col, 'char') !== false) { + if (strpos($col, 'char') !== false) { return 'string'; } if (in_array($col, array('blob', 'clob'))) { diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 0c655bbc6..967f4bade 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -2734,9 +2734,11 @@ class DboSource extends DataSource { /** * Inserts multiple values into a table * - * @param string $table - * @param string $fields - * @param array $values + * @param string $table The table being inserted into. + * @param array $fields The array of field/column names being inserted. + * @param array $values The array of values to insert. The values should + * be an array of rows. Each row should have values keyed by the column name. + * Each row must have the values in the same order as $fields. * @return boolean */ public function insertMulti($table, $fields, $values) { @@ -2744,12 +2746,32 @@ class DboSource extends DataSource { $holder = implode(',', array_fill(0, count($fields), '?')); $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); + $pdoMap = array( + 'integer' => PDO::PARAM_INT, + 'float' => PDO::PARAM_STR, + 'boolean' => PDO::PARAM_BOOL, + 'string' => PDO::PARAM_STR, + 'text' => PDO::PARAM_STR + ); + $columnMap = array(); + $count = count($values); $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$holder})"; $statement = $this->_connection->prepare($sql); $this->begin(); + + foreach ($values[0] as $key => $val) { + $type = $this->introspectType($val); + $columnMap[$key] = $pdoMap[$type]; + } + for ($x = 0; $x < $count; $x++) { - $statement->execute($values[$x]); + $i = 1; + foreach ($values[$x] as $key => $val) { + $statement->bindValue($i, $val, $columnMap[$key]); + $i += 1; + } + $statement->execute(); $statement->closeCursor(); } return $this->commit(); diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index 6209b3b4e..efb41d537 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -669,7 +669,8 @@ class CakeResponse { * @return boolean */ public function outputCompressed() { - return ini_get("zlib.output_compression") === '1' || in_array('ob_gzhandler', ob_list_handlers()); + return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false + && (ini_get("zlib.output_compression") === '1' || in_array('ob_gzhandler', ob_list_handlers())); } /** diff --git a/lib/Cake/Network/Http/HttpResponse.php b/lib/Cake/Network/Http/HttpResponse.php index ca227540d..1c73ee46c 100644 --- a/lib/Cake/Network/Http/HttpResponse.php +++ b/lib/Cake/Network/Http/HttpResponse.php @@ -213,7 +213,7 @@ class HttpResponse implements ArrayAccess { $chunkLength = null; while ($chunkLength !== 0) { - if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) { + if (!preg_match('/^([0-9a-f]+) *(?:;(.+)=(.+))?(?:\r\n|\n)/iU', $body, $match)) { throw new SocketException(__d('cake_dev', 'HttpSocket::_decodeChunkedBody - Could not parse malformed chunk.')); } diff --git a/lib/Cake/Routing/Router.php b/lib/Cake/Routing/Router.php index e05b4add4..04679aded 100644 --- a/lib/Cake/Routing/Router.php +++ b/lib/Cake/Routing/Router.php @@ -820,7 +820,7 @@ class Router { * @see Router::url() */ protected static function _handleNoRoute($url) { - $named = $args = $query = array(); + $named = $args = array(); $skip = array_merge( array('bare', 'action', 'controller', 'plugin', 'prefix'), self::$_prefixes @@ -847,7 +847,7 @@ class Router { } } - if (empty($named) && empty($args) && empty($query) && (!isset($url['action']) || $url['action'] === 'index')) { + if (empty($named) && empty($args) && (!isset($url['action']) || $url['action'] === 'index')) { $url['action'] = null; } @@ -881,9 +881,6 @@ class Router { } } } - if (!empty($query)) { - $output .= Router::queryString($query); - } return $output; } diff --git a/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php index 56db22d1a..de3cb8779 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php @@ -115,7 +115,7 @@ class DbConfigTaskTest extends CakeTestCase { ->with(array( array( 'name' => 'default', - 'driver' => 'mysql', + 'datasource' => 'mysql', 'persistent' => 'false', 'host' => 'localhost', 'login' => 'root', diff --git a/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php index 1652b9267..57d254468 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php @@ -230,6 +230,7 @@ class ViewTaskTest extends CakeTestCase { $this->Task->path = TMP; $this->Task->Template->params['theme'] = 'default'; + $this->Task->Template->templatePaths = array('default' => CAKE . 'Console' . DS . 'Templates' . DS . 'default' .DS); } /** @@ -563,7 +564,7 @@ class ViewTaskTest extends CakeTestCase { } /** - * test `cake bake view $controller -admin` + * test `cake bake view $controller --admin` * Which only bakes admin methods, not non-admin methods. * * @return void @@ -701,7 +702,7 @@ class ViewTaskTest extends CakeTestCase { } /** - * test getting templates, make sure noTemplateActions works + * test getting templates, make sure noTemplateActions works and prefixed template is used before generic one. * * @return void */ @@ -716,6 +717,14 @@ class ViewTaskTest extends CakeTestCase { $result = $this->Task->getTemplate('admin_add'); $this->assertEqual($result, 'form'); + + $this->Task->Template->templatePaths = array( + 'test' => CAKE . 'Test' . DS . 'test_app' . DS . 'Console' . DS . 'Templates' . DS . 'test' .DS + ); + $this->Task->Template->params['theme'] = 'test'; + + $result = $this->Task->getTemplate('admin_edit'); + $this->assertEqual($result, 'admin_edit'); } } diff --git a/lib/Cake/Test/Case/Network/CakeResponseTest.php b/lib/Cake/Test/Case/Network/CakeResponseTest.php index 95a9c373b..d5d2d3e41 100644 --- a/lib/Cake/Test/Case/Network/CakeResponseTest.php +++ b/lib/Cake/Test/Case/Network/CakeResponseTest.php @@ -389,6 +389,43 @@ class CakeResponseTest extends CakeTestCase { $this->assertEquals($expected, $result); } +/** +* Tests the outputCompressed method +* +*/ + public function testOutputCompressed() { + $response = new CakeResponse(); + + $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip'; + $result = $response->outputCompressed(); + $this->assertFalse($result); + + $_SERVER['HTTP_ACCEPT_ENCODING'] = ''; + $result = $response->outputCompressed(); + $this->assertFalse($result); + + if (!extension_loaded("zlib")) { + $this->markTestSkipped('Skipping further tests for outputCompressed as zlib extension is not loaded'); + } + if (php_sapi_name() !== 'cli') { + $this->markTestSkipped('Testing outputCompressed method with compression enabled done only in cli'); + } + + if (ini_get("zlib.output_compression") !== '1') { + ob_start('ob_gzhandler'); + } + $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip'; + $result = $response->outputCompressed(); + $this->assertTrue($result); + + $_SERVER['HTTP_ACCEPT_ENCODING'] = ''; + $result = $response->outputCompressed(); + $this->assertFalse($result); + if (ini_get("zlib.output_compression") !== '1') { + ob_get_clean(); + } + } + /** * Tests the send and setting of Content-Length * diff --git a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php index 895b2a7c3..fb1742192 100644 --- a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php @@ -356,6 +356,15 @@ class HttpResponseTest extends CakeTestCase { $r = $this->HttpResponse->decodeBody($sample['encoded'], $encoding); $this->assertEquals($r, $sample['decoded']); + + $encoding = 'chunked'; + $sample = array( + 'encoded' => "19\nThis is a chunked message\r\n0\n", + 'decoded' => array('body' => "This is a chunked message", 'header' => false) + ); + + $r = $this->HttpResponse->decodeBody($sample['encoded'], $encoding); + $this->assertEquals($r, $sample['decoded'], 'Inconsistent line terminators should be tolerated.'); } /** diff --git a/lib/Cake/Test/Case/Routing/DispatcherTest.php b/lib/Cake/Test/Case/Routing/DispatcherTest.php index f421e8e39..12dc144ea 100644 --- a/lib/Cake/Test/Case/Routing/DispatcherTest.php +++ b/lib/Cake/Test/Case/Routing/DispatcherTest.php @@ -1191,113 +1191,117 @@ class DispatcherTest extends CakeTestCase { } catch (MissingControllerException $e) { $this->assertEquals('Controller class ThemeController could not be found.', $e->getMessage()); } + } - ob_start(); - $Dispatcher->dispatch(new CakeRequest('theme/test_theme/flash/theme_test.swf'), $response); - $result = ob_get_clean(); +/** + * Data provider for asset() + * + * - theme assets. + * - plugin assets. + * - plugin assets in sub directories. + * - unknown plugin assets. + * + * @return array + */ + public static function assetProvider() { + return array( + array( + 'theme/test_theme/flash/theme_test.swf', + 'View/Themed/TestTheme/webroot/flash/theme_test.swf' + ), + array( + 'theme/test_theme/pdfs/theme_test.pdf', + 'View/Themed/TestTheme/webroot/pdfs/theme_test.pdf' + ), + array( + 'theme/test_theme/img/test.jpg', + 'View/Themed/TestTheme/webroot/img/test.jpg' + ), + array( + 'theme/test_theme/css/test_asset.css', + 'View/Themed/TestTheme/webroot/css/test_asset.css' + ), + array( + 'theme/test_theme/js/theme.js', + 'View/Themed/TestTheme/webroot/js/theme.js' + ), + array( + 'theme/test_theme/js/one/theme_one.js', + 'View/Themed/TestTheme/webroot/js/one/theme_one.js' + ), + array( + 'test_plugin/root.js', + 'Plugin/TestPlugin/webroot/root.js' + ), + array( + 'test_plugin/flash/plugin_test.swf', + 'Plugin/TestPlugin/webroot/flash/plugin_test.swf' + ), + array( + 'test_plugin/pdfs/plugin_test.pdf', + 'Plugin/TestPlugin/webroot/pdfs/plugin_test.pdf' + ), + array( + 'test_plugin/js/test_plugin/test.js', + 'Plugin/TestPlugin/webroot/js/test_plugin/test.js' + ), + array( + 'test_plugin/css/test_plugin_asset.css', + 'Plugin/TestPlugin/webroot/css/test_plugin_asset.css' + ), + array( + 'test_plugin/img/cake.icon.gif', + 'Plugin/TestPlugin/webroot/img/cake.icon.gif' + ), + array( + 'plugin_js/js/plugin_js.js', + 'Plugin/PluginJs/webroot/js/plugin_js.js' + ), + array( + 'plugin_js/js/one/plugin_one.js', + 'Plugin/PluginJs/webroot/js/one/plugin_one.js' + ), + array( + 'test_plugin/css/unknown.extension', + 'Plugin/TestPlugin/webroot/css/unknown.extension' + ), + array( + 'test_plugin/css/theme_one.htc', + 'Plugin/TestPlugin/webroot/css/theme_one.htc' + ), + ); + } - $file = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Themed' . DS . 'TestTheme' . DS . 'webroot' . DS . 'flash' . DS . 'theme_test.swf'); - $this->assertEqual($file, $result); - $this->assertEqual('this is just a test to load swf file from the theme.', $result); +/** + * Test assets + * + * @dataProvider assetProvider + * @outputBuffering enabled + * @return void + */ + public function testAsset($url, $file) { + Router::reload(); - ob_start(); - $Dispatcher->dispatch(new CakeRequest('theme/test_theme/pdfs/theme_test.pdf'), $response); - $result = ob_get_clean(); - $file = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Themed' . DS . 'TestTheme' . DS . 'webroot' . DS . 'pdfs' . DS . 'theme_test.pdf'); - $this->assertEqual($file, $result); - $this->assertEqual('this is just a test to load pdf file from the theme.', $result); - - ob_start(); - $Dispatcher->dispatch(new CakeRequest('theme/test_theme/img/test.jpg'), $response); - $result = ob_get_clean(); - $file = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Themed' . DS . 'TestTheme' . DS . 'webroot' . DS . 'img' . DS . 'test.jpg'); - $this->assertEqual($file, $result); - - ob_start(); - $Dispatcher->asset('theme/test_theme/css/test_asset.css', $response); - $result = ob_get_clean(); - $this->assertEqual('this is the test asset css file', $result); - - ob_start(); - $Dispatcher->asset('theme/test_theme/js/theme.js', $response); - $result = ob_get_clean(); - $this->assertEqual('root theme js file', $result); - - ob_start(); - $Dispatcher->asset('theme/test_theme/js/one/theme_one.js', $response); - $result = ob_get_clean(); - $this->assertEqual('nested theme js file', $result); - - ob_start(); - $Dispatcher->asset('test_plugin/root.js', $response); - $result = ob_get_clean(); - $expected = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' . DS . 'webroot' . DS . 'root.js'); - $this->assertEqual($expected, $result); - - ob_start(); - $Dispatcher->dispatch(new CakeRequest('test_plugin/flash/plugin_test.swf'), $response); - $result = ob_get_clean(); - $file = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' . DS . 'webroot' . DS . 'flash' . DS . 'plugin_test.swf'); - $this->assertEqual($file, $result); - $this->assertEqual('this is just a test to load swf file from the plugin.', $result); - - ob_start(); - $Dispatcher->dispatch(new CakeRequest('test_plugin/pdfs/plugin_test.pdf'), $response); - $result = ob_get_clean(); - $file = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' . DS . 'webroot' . DS . 'pdfs' . DS . 'plugin_test.pdf'); - $this->assertEqual($file, $result); - $this->assertEqual('this is just a test to load pdf file from the plugin.', $result); - - ob_start(); - $Dispatcher->asset('test_plugin/js/test_plugin/test.js', $response); - $result = ob_get_clean(); - $this->assertEqual('alert("Test App");', $result); - - ob_start(); - $Dispatcher->asset('test_plugin/js/test_plugin/test.js', $response); - $result = ob_get_clean(); - $this->assertEqual('alert("Test App");', $result); - - ob_start(); - $Dispatcher->asset('test_plugin/css/test_plugin_asset.css', $response); - $result = ob_get_clean(); - $this->assertEqual('this is the test plugin asset css file', $result); - - ob_start(); - $Dispatcher->asset('test_plugin/img/cake.icon.gif', $response); - $result = ob_get_clean(); - $file = file_get_contents(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' .DS . 'webroot' . DS . 'img' . DS . 'cake.icon.gif'); - $this->assertEqual($file, $result); - - ob_start(); - $Dispatcher->asset('plugin_js/js/plugin_js.js', $response); - $result = ob_get_clean(); - $expected = "alert('win sauce');"; - $this->assertEqual($expected, $result); - - ob_start(); - $Dispatcher->asset('plugin_js/js/one/plugin_one.js', $response); - $result = ob_get_clean(); - $expected = "alert('plugin one nested js file');"; - $this->assertEqual($expected, $result); - - ob_start(); - $Dispatcher->asset('test_plugin/css/unknown.extension', $response); - $result = ob_get_clean(); - $this->assertEqual('Testing a file with unknown extension to mime mapping.', $result); - - ob_start(); - $Dispatcher->asset('test_plugin/css/theme_one.htc', $response); - $result = ob_get_clean(); - $this->assertEqual('htc file', $result); + App::build(array( + 'Plugin' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS), + 'Vendor' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Vendor'. DS), + 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View'. DS) + )); + CakePlugin::loadAll(); + $Dispatcher = new TestDispatcher(); $response = $this->getMock('CakeResponse', array('_sendHeader')); - ob_start(); - $Dispatcher->asset('test_plugin/css/unknown.extension', $response); - ob_end_clean(); - $expected = filesize(CakePlugin::path('TestPlugin') . 'webroot' . DS . 'css' . DS . 'unknown.extension'); + + $Dispatcher->dispatch(new CakeRequest($url), $response); + $result = ob_get_clean(); + + $path = CAKE. 'Test' . DS . 'test_app' . DS . str_replace('/', DS, $file); + $file = file_get_contents($path); + $this->assertEquals($file, $result); + + $expected = filesize($path); $headers = $response->header(); - $this->assertEqual($expected, $headers['Content-Length']); + $this->assertEquals($expected, $headers['Content-Length']); } /** @@ -1342,17 +1346,41 @@ class DispatcherTest extends CakeTestCase { $this->assertFalse($Dispatcher->asset('js/cjs/debug_kit.js', $response)); } + +/** + * Data provider for cached actions. + * + * - Test simple views + * - Test views with nocache tags + * - Test requests with named + passed params. + * - Test themed views. + * + * @return array + */ + public static function cacheActionProvider() { + return array( + array('/'), + array('test_cached_pages/index'), + array('TestCachedPages/index'), + array('test_cached_pages/test_nocache_tags'), + array('TestCachedPages/test_nocache_tags'), + array('test_cached_pages/view/param/param'), + array('test_cached_pages/view/foo:bar/value:goo'), + array('test_cached_pages/themed'), + ); + } + /** * testFullPageCachingDispatch method * + * @dataProvider cacheActionProvider * @return void */ - public function testFullPageCachingDispatch() { + public function testFullPageCachingDispatch($url) { Configure::write('Cache.disable', false); Configure::write('Cache.check', true); Configure::write('debug', 2); - Router::reload(); Router::connect('/', array('controller' => 'test_cached_pages', 'action' => 'index')); Router::connect('/:controller/:action/*'); @@ -1362,141 +1390,7 @@ class DispatcherTest extends CakeTestCase { ), true); $dispatcher = new TestDispatcher(); - $request = new CakeRequest('/'); - $response = new CakeResponse(); - - ob_start(); - $dispatcher->dispatch($request, $response); - $out = ob_get_clean(); - - ob_start(); - $dispatcher->cached($request->here); - $cached = ob_get_clean(); - - $result = str_replace(array("\t", "\r\n", "\n"), "", $out); - $cached = preg_replace('//', '', $cached); - $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); - - $this->assertEqual($expected, $result); - - $filename = $this->__cachePath($request->here); - unlink($filename); - - $request = new CakeRequest('test_cached_pages/index'); - $_POST = array( - 'slasher' => "Up in your's grill \ '" - ); - - ob_start(); - $dispatcher->dispatch($request, $response); - $out = ob_get_clean(); - - ob_start(); - $dispatcher->cached($request->here); - $cached = ob_get_clean(); - - $result = str_replace(array("\t", "\r\n", "\n"), "", $out); - $cached = preg_replace('//', '', $cached); - $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); - - $this->assertEqual($expected, $result); - $filename = $this->__cachePath($request->here); - unlink($filename); - - $request = new CakeRequest('TestCachedPages/index'); - - ob_start(); - $dispatcher->dispatch($request, $response); - $out = ob_get_clean(); - - ob_start(); - $dispatcher->cached($request->here); - $cached = ob_get_clean(); - - $result = str_replace(array("\t", "\r\n", "\n"), "", $out); - $cached = preg_replace('//', '', $cached); - $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); - - $this->assertEqual($expected, $result); - $filename = $this->__cachePath($request->here); - unlink($filename); - - $request = new CakeRequest('TestCachedPages/test_nocache_tags'); - - ob_start(); - $dispatcher->dispatch($request, $response); - $out = ob_get_clean(); - - ob_start(); - $dispatcher->cached($request->here); - $cached = ob_get_clean(); - - $result = str_replace(array("\t", "\r\n", "\n"), "", $out); - $cached = preg_replace('//', '', $cached); - $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); - - $this->assertEqual($expected, $result); - $filename = $this->__cachePath($request->here); - unlink($filename); - - $request = new CakeRequest('test_cached_pages/view/param/param'); - - ob_start(); - $dispatcher->dispatch($request, $response); - $out = ob_get_clean(); - - ob_start(); - $dispatcher->cached($request->here); - $cached = ob_get_clean(); - - $result = str_replace(array("\t", "\r\n", "\n"), "", $out); - $cached = preg_replace('//', '', $cached); - $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); - - $this->assertEqual($expected, $result); - $filename = $this->__cachePath($request->here); - unlink($filename); - - $request = new CakeRequest('test_cached_pages/view/foo:bar/value:goo'); - - ob_start(); - $dispatcher->dispatch($request, $response); - $out = ob_get_clean(); - - ob_start(); - $dispatcher->cached($request->here); - $cached = ob_get_clean(); - - $result = str_replace(array("\t", "\r\n", "\n"), "", $out); - $cached = preg_replace('//', '', $cached); - $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); - - $this->assertEqual($expected, $result); - $filename = $this->__cachePath($request->here); - $this->assertTrue(file_exists($filename)); - - unlink($filename); - } - -/** - * Test full page caching with themes. - * - * @return void - */ - public function testFullPageCachingWithThemes() { - Configure::write('Cache.disable', false); - Configure::write('Cache.check', true); - Configure::write('debug', 2); - - Router::reload(); - Router::connect('/:controller/:action/*'); - - App::build(array( - 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS), - ), true); - - $dispatcher = new TestDispatcher(); - $request = new CakeRequest('/test_cached_pages/themed'); + $request = new CakeRequest($url); $response = new CakeResponse(); ob_start(); diff --git a/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php b/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php index af9d932dd..f69ca32c9 100644 --- a/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php +++ b/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php @@ -425,6 +425,21 @@ class ControllerTestCaseTest extends CakeTestCase { $this->assertTrue(isset($query['blue'])); } +/** + * Test that REST actions with XML/JSON input work. + * + * @return void + */ + public function testTestActionJsonData() { + $result = $this->Case->testAction('/tests_apps_posts/input_data', array( + 'return' => 'vars', + 'method' => 'post', + 'data' => '{"key":"value","json":true}' + )); + $this->assertEquals('value', $result['data']['key']); + $this->assertTrue($result['data']['json']); + } + /** * Tests autoMock ability */ diff --git a/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php b/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php index 8024272e9..b2f6495f4 100644 --- a/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php +++ b/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php @@ -127,6 +127,55 @@ class HtmlCoverageReportTest extends CakeTestCase { } } + +/** + * Test that coverage works with phpunit 3.6 as the data formats from coverage are totally different. + * + * @return void + */ + public function testPhpunit36Compatibility() { + $file = array( + 'line 1', + 'line 2', + 'line 3', + 'line 4', + 'line 5', + 'line 6', + 'line 7', + 'line 8', + 'line 9', + 'line 10', + ); + $coverage = array( + 1 => array('HtmlCoverageReportTest::testGenerateDiff'), + 2 => null, + 3 => array('HtmlCoverageReportTest::testGenerateDiff'), + 4 => array('HtmlCoverageReportTest::testGenerateDiff'), + 5 => array(), + 6 => array('HtmlCoverageReportTest::testGenerateDiff'), + 7 => array('HtmlCoverageReportTest::testGenerateDiff'), + 8 => array('HtmlCoverageReportTest::testGenerateDiff'), + 9 => array(), + 10 => array('HtmlCoverageReportTest::testSomething', 'HtmlCoverageReportTest::testGenerateDiff') + ); + + $result = $this->Coverage->generateDiff('myfile.php', $file, $coverage); + $this->assertRegExp('/myfile\.php Code coverage\: \d+\.?\d*\%/', $result); + $this->assertRegExp('/
assertRegExp('/
/', $result);
+		foreach ($file as $i => $line) {
+			$this->assertTrue(strpos($line, $result) !== 0, 'Content is missing ' . $i);
+			$class = 'covered';
+			if (in_array($i + 1, array(5, 9, 2))) {
+				$class = 'uncovered';
+			}
+			if ($i + 1 == 2) {
+				$class .= ' dead';
+			}
+			$this->assertTrue(strpos($class, $result) !== 0, 'Class name is wrong ' . $i);
+		}
+	}
+
 /**
  * test that covering methods show up as title attributes for lines.
  *
diff --git a/lib/Cake/Test/Case/Utility/DebuggerTest.php b/lib/Cake/Test/Case/Utility/DebuggerTest.php
index bd7bec9f8..6b62aa3e9 100644
--- a/lib/Cake/Test/Case/Utility/DebuggerTest.php
+++ b/lib/Cake/Test/Case/Utility/DebuggerTest.php
@@ -425,7 +425,7 @@ TEXT;
  */
 	public function testNoDbCredentials() {
 		$config = array(
-			'driver' => 'mysql',
+			'datasource' => 'mysql',
 			'persistent' => false,
 			'host' => 'void.cakephp.org',
 			'login' => 'cakephp-user',
@@ -437,7 +437,7 @@ TEXT;
 		$output = Debugger::exportVar($config);
 
 		$expectedArray = array(
-			'driver' => 'mysql',
+			'datasource' => 'mysql',
 			'persistent' => false,
 			'host' => '*****',
 			'login' => '*****',
diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php
index a3dc8c243..08b8cdf70 100644
--- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php
+++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php
@@ -1924,6 +1924,24 @@ class FormHelperTest extends CakeTestCase {
 		);
 		$this->assertTags($result, $expected);
 
+		$result = $this->Form->input('Contact.field', array(
+			'type' => 'text', 'error' => array('attributes' => array('class' => 'error'))
+		));
+		$expected = array(
+			'div' => array('class' => 'input text error'),
+			'label' => array('for' => 'ContactField'),
+			'Field',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][field]',
+				'id' => 'ContactField', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error')),
+			'Badness!',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
 		$result = $this->Form->input('Contact.field', array(
 			'div' => array('tag' => 'span'), 'error' => array('attributes' => array('wrap' => false))
 		));
diff --git a/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php b/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php
index 672d63efc..724ed0adb 100644
--- a/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php
+++ b/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php
@@ -515,7 +515,8 @@ class TimeHelperTest extends CakeTestCase {
 		$result = $this->Time->isThisYear(mktime(0, 0, 0, mt_rand(1, 12), mt_rand(1, 28), date('Y')));
 		$this->assertTrue($result);
 	}
-	/**
+
+/**
  * testWasYesterday method
  *
  * @return void
@@ -534,7 +535,8 @@ class TimeHelperTest extends CakeTestCase {
 		$result = $this->Time->wasYesterday('-2 days');
 		$this->assertFalse($result);
 	}
-	/**
+
+/**
  * testIsTomorrow method
  *
  * @return void
@@ -594,7 +596,8 @@ class TimeHelperTest extends CakeTestCase {
 		$this->assertTrue($this->Time->wasWithinLast('1   ', '-1 minute'));
 		$this->assertTrue($this->Time->wasWithinLast('1   ', '-23 hours -59 minutes -59 seconds'));
 	}
-	/**
+
+/**
  * testUserOffset method
  *
  * @return void
diff --git a/lib/Cake/Test/Case/View/HelperTest.php b/lib/Cake/Test/Case/View/HelperTest.php
index 11335da17..b77198d35 100644
--- a/lib/Cake/Test/Case/View/HelperTest.php
+++ b/lib/Cake/Test/Case/View/HelperTest.php
@@ -220,32 +220,48 @@ class HelperTest extends CakeTestCase {
 		return array(
 			array(
 				'HelperTestPost.id',
-				array('HelperTestPost', 'id')
+				array('HelperTestPost', 'id'),
+				'HelperTestPost',
+				'id'
 			),
 			array(
 				'HelperTestComment.body',
-				array('HelperTestComment', 'body')
+				array('HelperTestComment', 'body'),
+				'HelperTestComment',
+				'body'
 			),
 			array(
 				'HelperTest.1.Comment.body',
-				array('HelperTest', '1', 'Comment', 'body')
+				array('HelperTest', '1', 'Comment', 'body'),
+				'Comment',
+				'body'
 			),
 			array(
 				'HelperTestComment.BigField',
-				array('HelperTestComment', 'BigField')
+				array('HelperTestComment', 'BigField'),
+				'HelperTestComment',
+				'BigField'
+			),
+			array(
+				'HelperTestComment.min',
+				array('HelperTestComment', 'min'),
+				'HelperTestComment',
+				'min'
 			)
 		);
 	}
 
 /**
- * testFormFieldNameParsing method
+ * Test setting an entity and retriving the entity, model and field.
  *
  * @dataProvider entityProvider
  * @return void
  */
-	public function testSetEntity($entity, $expected) {
+	public function testSetEntity($entity, $expected, $modelKey, $fieldKey) {
 		$this->Helper->setEntity($entity);
 		$this->assertEquals($expected, $this->Helper->entity());
+		$this->assertEquals($modelKey, $this->Helper->model());
+		$this->assertEquals($fieldKey, $this->Helper->field());
 	}
 
 /**
@@ -255,7 +271,7 @@ class HelperTest extends CakeTestCase {
  */
 	public function testSetEntityScoped() {
 		$this->Helper->setEntity('HelperTestPost', true);
-	$this->assertEquals(array('HelperTestPost'), $this->Helper->entity());
+		$this->assertEquals(array('HelperTestPost'), $this->Helper->entity());
 
 		$this->Helper->setEntity('id');
 		$expected = array('HelperTestPost', 'id');
diff --git a/lib/Cake/Test/Fixture/CounterCachePostFixture.php b/lib/Cake/Test/Fixture/CounterCachePostFixture.php
index 9a5767dbb..dd7b92f1c 100644
--- a/lib/Cake/Test/Fixture/CounterCachePostFixture.php
+++ b/lib/Cake/Test/Fixture/CounterCachePostFixture.php
@@ -34,7 +34,7 @@ class CounterCachePostFixture extends CakeTestFixture {
 	);
 
     public $records = array(
-		array('id' => 1, 'title' => 'Rock and Roll',  'user_id' => 66, 'published' => 0),
+		array('id' => 1, 'title' => 'Rock and Roll',  'user_id' => 66, 'published' => false),
 		array('id' => 2, 'title' => 'Music',   'user_id' => 66, 'published' => true),
 		array('id' => 3, 'title' => 'Food',   'user_id' => 301, 'published' => true),
     );
diff --git a/lib/Cake/Test/test_app/Console/Templates/test/views/admin_edit.ctp b/lib/Cake/Test/test_app/Console/Templates/test/views/admin_edit.ctp
new file mode 100644
index 000000000..0d7ec6739
--- /dev/null
+++ b/lib/Cake/Test/test_app/Console/Templates/test/views/admin_edit.ctp
@@ -0,0 +1 @@
+admin_edit template
\ No newline at end of file
diff --git a/lib/Cake/Test/test_app/Controller/TestsAppsPostsController.php b/lib/Cake/Test/test_app/Controller/TestsAppsPostsController.php
index 9062a5c20..96a9d5f63 100644
--- a/lib/Cake/Test/test_app/Controller/TestsAppsPostsController.php
+++ b/lib/Cake/Test/test_app/Controller/TestsAppsPostsController.php
@@ -53,6 +53,11 @@ class TestsAppsPostsController extends AppController {
 		$this->render('index');
 	}
 
+	public function input_data() {
+		$this->set('data', $this->request->input('json_decode', true));
+		$this->render('index');
+	}
+
 /**
  * Fixturized action for testAction()
  *
diff --git a/lib/Cake/TestSuite/ControllerTestCase.php b/lib/Cake/TestSuite/ControllerTestCase.php
index 1557958fa..bda1cae45 100644
--- a/lib/Cake/TestSuite/ControllerTestCase.php
+++ b/lib/Cake/TestSuite/ControllerTestCase.php
@@ -187,11 +187,14 @@ abstract class ControllerTestCase extends CakeTestCase {
 	}
 
 /**
- * Tests a controller action.
+ * Lets you do functional tests of a controller action.
  *
  * ### Options:
  *
- * - `data` POST or GET data to pass. Depends on the method.
+ * - `data` Will be used as the request data.  If the `method` is GET,
+ *   data will be used a GET params.  If the `method` is POST, it will be used
+ *   as POST data. By setting `$options['data']` to a string, you can simulate XML or JSON
+ *   payloads to your controllers allowing you to test REST webservices.
  * - `method` POST or GET. Defaults to POST.
  * - `return` Specify the return type you want.  Choose from:
  *     - `vars` Get the set view variables.
@@ -213,14 +216,23 @@ abstract class ControllerTestCase extends CakeTestCase {
 		), $options);
 
 		$_SERVER['REQUEST_METHOD'] = strtoupper($options['method']);
-		if (strtoupper($options['method']) == 'GET') {
-			$_GET = $options['data'];
-			$_POST = array();
-		} else {
-			$_POST = $options['data'];
-			$_GET = array();
+		if (is_array($options['data'])) {
+			if (strtoupper($options['method']) == 'GET') {
+				$_GET = $options['data'];
+				$_POST = array();
+			} else {
+				$_POST = $options['data'];
+				$_GET = array();
+			}
 		}
-		$request = new CakeRequest($url);
+		$request = $this->getMock('CakeRequest', array('_readInput'), array($url));
+
+		if (is_string($options['data'])) {
+			$request->expects($this->any())
+				->method('_readInput')
+				->will($this->returnValue($options['data']));
+		}
+
 		$Dispatch = new ControllerTestDispatcher();
 		foreach (Router::$routes as $route) {
 			if ($route instanceof RedirectRoute) {
diff --git a/lib/Cake/TestSuite/Coverage/BaseCoverageReport.php b/lib/Cake/TestSuite/Coverage/BaseCoverageReport.php
index e12a06faa..788d67d12 100644
--- a/lib/Cake/TestSuite/Coverage/BaseCoverageReport.php
+++ b/lib/Cake/TestSuite/Coverage/BaseCoverageReport.php
@@ -120,7 +120,12 @@ abstract class BaseCoverageReport {
 	}
 
 /**
- * Calculates how many lines are covered and what the total number of executable lines is
+ * Calculates how many lines are covered and what the total number of executable lines is.
+ *
+ * Handles both PHPUnit3.5 and 3.6 formats.
+ *
+ * 3.5 uses -1 for uncovered, and -2 for dead.
+ * 3.6 uses array() for uncovered and null for dead.
  *
  * @param array $fileLines
  * @param array $coverageData
@@ -137,10 +142,10 @@ abstract class BaseCoverageReport {
 			if (!isset($coverageData[$lineno])) {
 				continue;
 			}
-			if (is_array($coverageData[$lineno])) {
+			if (is_array($coverageData[$lineno]) && !empty($coverageData[$lineno])) {
 				$covered++;
 				$total++;
-			} else if ($coverageData[$lineno] === -1) {
+			} else if ($coverageData[$lineno] === -1 || $coverageData[$lineno] === array()) {
 				$total++;
 			}
 		}
diff --git a/lib/Cake/TestSuite/Coverage/HtmlCoverageReport.php b/lib/Cake/TestSuite/Coverage/HtmlCoverageReport.php
index f85d13d1f..651bd1df4 100644
--- a/lib/Cake/TestSuite/Coverage/HtmlCoverageReport.php
+++ b/lib/Cake/TestSuite/Coverage/HtmlCoverageReport.php
@@ -47,6 +47,11 @@ HTML;
 /**
  * Generates an HTML diff for $file based on $coverageData.
  *
+ * Handles both PHPUnit3.5 and 3.6 formats.
+ *
+ * 3.5 uses -1 for uncovered, and -2 for dead.
+ * 3.6 uses array() for uncovered and null for dead.
+ *
  * @param string $filename Name of the file having coverage generated
  * @param array $fileLines File data as an array. See file() for how to get one of these.
  * @param array $coverageData Array of coverage data to use to generate HTML diffs with
@@ -65,17 +70,18 @@ HTML;
 		foreach ($fileLines as $lineno => $line) {
 			$class = 'ignored';
 			$coveringTests = array();
-			if (isset($coverageData[$lineno]) && is_array($coverageData[$lineno])) {
+			if (!empty($coverageData[$lineno]) && is_array($coverageData[$lineno])) {
 				$coveringTests = array();
 				foreach ($coverageData[$lineno] as $test) {
-					$testReflection = new ReflectionClass(current(explode('::', $test['id'])));
+					$class = (is_array($test) && isset($test['id'])) ? $test['id'] : $test;
+					$testReflection = new ReflectionClass(current(explode('::', $class)));
 					$this->_testNames[] = $this->_guessSubjectName($testReflection);
-					$coveringTests[] = $test['id'];
+					$coveringTests[] = $class;
 				}
 				$class = 'covered';
-			} elseif (isset($coverageData[$lineno]) && $coverageData[$lineno] === -1) {
+			} elseif (isset($coverageData[$lineno]) && ($coverageData[$lineno] === -1 || $coverageData[$lineno] === array())) {
 				$class = 'uncovered';
-			} elseif (isset($coverageData[$lineno]) && $coverageData[$lineno] === -2) {
+			} elseif (array_key_exists($lineno, $coverageData) && ($coverageData[$lineno] === -2 || $coverageData[$lineno] === null)) {
 				$class .= ' dead';
 			}
 			$diff[] = $this->_paintLine($line, $lineno, $class, $coveringTests);
diff --git a/lib/Cake/TestSuite/Reporter/CakeHtmlReporter.php b/lib/Cake/TestSuite/Reporter/CakeHtmlReporter.php
index a24a22dce..16bb9d760 100644
--- a/lib/Cake/TestSuite/Reporter/CakeHtmlReporter.php
+++ b/lib/Cake/TestSuite/Reporter/CakeHtmlReporter.php
@@ -148,7 +148,7 @@ class CakeHtmlReporter extends CakeBaseReporter {
 			}
 			if (method_exists($coverage, 'getData')) {
 				$report = $coverage->getData();
-				echo '
' . __('Coverage generation is not supported with PHPUnit 3.6 at this time.') . '
'; + echo $this->paintCoverage($report); } } $this->paintDocumentEnd(); @@ -161,6 +161,7 @@ class CakeHtmlReporter extends CakeBaseReporter { */ public function paintCoverage(array $coverage) { App::uses('HtmlCoverageReport', 'TestSuite/Coverage'); + $reporter = new HtmlCoverageReport($coverage, $this); echo $reporter->report(); } diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index 91fafae3b..523e59fee 100644 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -446,7 +446,11 @@ class Helper extends Object { // 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them. if ( - $count >= 2 && is_numeric($parts[0]) && !is_numeric($parts[1]) && $this->_modelScope && strpos($entity, $this->_modelScope) === false + $count >= 2 && + is_numeric($parts[0]) && + !is_numeric($parts[1]) && + $this->_modelScope && + strpos($entity, $this->_modelScope) === false ) { $entity = $this->_modelScope . '.' . $entity; } @@ -500,6 +504,8 @@ class Helper extends Object { /** * Gets the currently-used model field of the rendering context. + * Strips off fieldsuffixes such as year, month, day, hour, min, meridian + * when the current entity is longer than 2 elements. * * @return string */ @@ -507,7 +513,7 @@ class Helper extends Object { $entity = $this->entity(); $count = count($entity); $last = $entity[$count - 1]; - if (in_array($last, $this->_fieldSuffixes)) { + if ($count > 2 && in_array($last, $this->_fieldSuffixes)) { $last = isset($entity[$count - 2]) ? $entity[$count - 2] : null; } return $last; diff --git a/lib/Cake/View/Helper/TimeHelper.php b/lib/Cake/View/Helper/TimeHelper.php index 7bd5b5e6f..55fede407 100644 --- a/lib/Cake/View/Helper/TimeHelper.php +++ b/lib/Cake/View/Helper/TimeHelper.php @@ -61,6 +61,7 @@ class TimeHelper extends AppHelper { * Accepts the special specifier %S which mimics th modifier S for date() * @param string $time UNIX timestamp * @return string windows safe and date() function compatible format for strftime + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function convertSpecifiers($format, $time = null) { if (!$time) { @@ -172,7 +173,8 @@ class TimeHelper extends AppHelper { * * @param string $serverTime UNIX timestamp * @param integer $userOffset User's offset from GMT (in hours) - * @return string UNIX timestamp + * @return integer UNIX timestamp + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function convert($serverTime, $userOffset) { $serverOffset = $this->serverOffset(); @@ -185,6 +187,7 @@ class TimeHelper extends AppHelper { * Returns server's offset from GMT in seconds. * * @return integer Offset + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function serverOffset() { return date('Z', time()); @@ -311,6 +314,7 @@ class TimeHelper extends AppHelper { * @param string $dateString Datetime string or Unix timestamp * @param integer $userOffset User's offset from GMT (in hours) * @return boolean True if datetime string is today + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time */ public function isToday($dateString, $userOffset = null) { $date = $this->fromString($dateString, $userOffset); @@ -387,7 +391,7 @@ class TimeHelper extends AppHelper { * * @param string $dateString * @param boolean $range if true returns a range in Y-m-d format - * @return boolean True if datetime string is within current week + * @return mixed 1, 2, 3, or 4 quarter of year or array if $range true * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function toQuarter($dateString, $range = false) { @@ -676,10 +680,10 @@ class TimeHelper extends AppHelper { } /** - * Returns gmt, given either a UNIX timestamp or a valid strtotime() date string. + * Returns gmt as a UNIX timestamp. * - * @param string $string Datetime string - * @return string Formatted date string + * @param string $string UNIX timestamp or a valid strtotime() date string + * @return integer UNIX timestamp * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function gmt($string = null) { @@ -688,7 +692,6 @@ class TimeHelper extends AppHelper { } else { $string = time(); } - $string = $this->fromString($string); $hour = intval(date("G", $string)); $minute = intval(date("i", $string)); $second = intval(date("s", $string)); @@ -709,6 +712,7 @@ class TimeHelper extends AppHelper { * @param boolean $invalid flag to ignore results of fromString == false * @param integer $userOffset User's offset from GMT (in hours) * @return string Formatted date string + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function format($format, $date = null, $invalid = false, $userOffset = null) { $time = $this->fromString($date, $userOffset); @@ -733,6 +737,7 @@ class TimeHelper extends AppHelper { * @param boolean $invalid flag to ignore results of fromString == false * @param integer $userOffset User's offset from GMT (in hours) * @return string Formatted and translated date string + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting */ public function i18nFormat($date, $format = null, $invalid = false, $userOffset = null) { $date = $this->fromString($date, $userOffset);