Merge branch '2.0' into 2.1

This commit is contained in:
mark_story 2011-11-11 22:38:11 -05:00
commit 254357e9c9
37 changed files with 474 additions and 319 deletions

View file

@ -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');

View file

@ -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

View file

@ -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:',

View file

@ -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(

View file

@ -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";

View file

@ -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) {

View file

@ -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;

View file

@ -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
));
}
}

View file

@ -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;

View file

@ -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');

View file

@ -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

View file

@ -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(

View file

@ -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'))) {

View file

@ -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();

View file

@ -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()));
}
/**

View file

@ -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.'));
}

View file

@ -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;
}

View file

@ -115,7 +115,7 @@ class DbConfigTaskTest extends CakeTestCase {
->with(array(
array(
'name' => 'default',
'driver' => 'mysql',
'datasource' => 'mysql',
'persistent' => 'false',
'host' => 'localhost',
'login' => 'root',

View file

@ -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');
}
}

View file

@ -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
*

View file

@ -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.');
}
/**

View file

@ -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();

View file

@ -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
*/

View file

@ -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('/<div class="code-coverage-results" id\="coverage\-myfile\.php"/', $result);
$this->assertRegExp('/<pre>/', $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.
*

View file

@ -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' => '*****',

View file

@ -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))
));

View file

@ -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

View file

@ -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());
}
/**

View file

@ -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),
);

View file

@ -0,0 +1 @@
admin_edit template

View file

@ -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()
*

View file

@ -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,6 +216,7 @@ abstract class ControllerTestCase extends CakeTestCase {
), $options);
$_SERVER['REQUEST_METHOD'] = strtoupper($options['method']);
if (is_array($options['data'])) {
if (strtoupper($options['method']) == 'GET') {
$_GET = $options['data'];
$_POST = array();
@ -220,7 +224,15 @@ abstract class ControllerTestCase extends CakeTestCase {
$_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) {

View file

@ -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++;
}
}

View file

@ -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);

View file

@ -148,7 +148,7 @@ class CakeHtmlReporter extends CakeBaseReporter {
}
if (method_exists($coverage, 'getData')) {
$report = $coverage->getData();
echo '<div class="cake-error">' . __('Coverage generation is not supported with PHPUnit 3.6 at this time.') . '</div>';
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();
}

View file

@ -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;

View file

@ -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);