diff options
Diffstat (limited to 'modules/system')
-rw-r--r-- | modules/system/system.api.php | 32 | ||||
-rw-r--r-- | modules/system/system.module | 1 | ||||
-rw-r--r-- | modules/system/system.test | 269 |
3 files changed, 244 insertions, 58 deletions
diff --git a/modules/system/system.api.php b/modules/system/system.api.php index c7db6f1dd..400538900 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -947,8 +947,11 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * called, the corresponding path components will be substituted for the * integers. That is, the integer 0 in an argument list will be replaced with * the first path component, integer 1 with the second, and so on (path - * components are numbered starting from zero). This substitution feature allows - * you to re-use a callback function for several different paths. For example: + * components are numbered starting from zero). To pass an integer without it + * being replaced with its respective path component, use the string value of + * the integer (e.g., '1') as the argument value. This substitution feature + * allows you to re-use a callback function for several different paths. For + * example: * @code * function mymodule_menu() { * $items['abc/def'] = array( @@ -961,9 +964,12 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * When path 'abc/def' is requested, the page callback function will get 'def' * as the first argument and (always) 'foo' as the second argument. * - * Note that if a page or theme callback function has an argument list array, - * these arguments will be passed first to the function, followed by any - * any arguments generated by optional path arguments as described above. + * If a page callback function uses an argument list array, and its path is + * requested with optional path arguments, then the list array's arguments are + * passed to the callback function first, followed by the optional path + * arguments. Using the above example, when path 'abc/def/bar/baz' is requested, + * mymodule_abc_view() will be called with 'def', 'foo', 'bar' and 'baz' as + * arguments, in that order. * * Special care should be taken for the page callback drupal_get_form(), because * your specific form callback function will always receive $form and @@ -1295,7 +1301,7 @@ function hook_menu_link_insert($link) { */ function hook_menu_link_update($link) { // If the parent menu has changed, update our record. - $menu_name = db_result(db_query("SELECT mlid, menu_name, status FROM {menu_example} WHERE mlid = :mlid", array(':mlid' => $link['mlid']))); + $menu_name = db_query("SELECT menu_name FROM {menu_example} WHERE mlid = :mlid", array(':mlid' => $link['mlid']))->fetchField(); if ($menu_name != $link['menu_name']) { db_update('menu_example') ->fields(array('menu_name' => $link['menu_name'])) @@ -3264,6 +3270,14 @@ function hook_update_last_removed() { * module's database tables are removed, allowing your module to query its own * tables during this routine. * + * When hook_uninstall() is called, your module will already be disabled, so + * its .module file will not be automatically included. If you need to call API + * functions from your .module file in this hook, use drupal_load() to make + * them available. (Keep this usage to a minimum, though, especially when + * calling API functions that invoke hooks, or API functions from modules + * listed as dependencies, since these may not be available or work as expected + * when the module is disabled.) + * * @see hook_install() * @see hook_schema() * @see hook_disable() @@ -3924,7 +3938,11 @@ function hook_system_themes_page_alter(&$theme_groups) { foreach ($theme_groups as $state => &$group) { foreach ($theme_groups[$state] as &$theme) { // Add a foo link to each list of theme operations. - $theme->operations[] = l(t('Foo'), 'admin/appearance/foo', array('query' => array('theme' => $theme->name))); + $theme->operations[] = array( + 'title' => t('Foo'), + 'href' => 'admin/appearance/foo', + 'query' => array('theme' => $theme->name) + ); } } } diff --git a/modules/system/system.module b/modules/system/system.module index 5af9ad4ee..c3b4a1e39 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -2018,6 +2018,7 @@ function system_block_info() { $blocks['help'] = array( 'info' => t('System help'), 'weight' => '5', + 'cache' => DRUPAL_NO_CACHE, ); // System-defined menu blocks. foreach (menu_list_system_menus() as $menu_name => $title) { diff --git a/modules/system/system.test b/modules/system/system.test index be4e36698..0fe0bc057 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -37,6 +37,40 @@ class ModuleTestCase extends DrupalWebTestCase { } /** + * Assert that all tables defined in a module's hook_schema() exist. + * + * @param $module + * The name of the module. + */ + function assertModuleTablesExist($module) { + $tables = array_keys(drupal_get_schema_unprocessed($module)); + $tables_exist = TRUE; + foreach ($tables as $table) { + if (!db_table_exists($table)) { + $tables_exist = FALSE; + } + } + return $this->assertTrue($tables_exist, t('All database tables defined by the @module module exist.', array('@module' => $module))); + } + + /** + * Assert that none of the tables defined in a module's hook_schema() exist. + * + * @param $module + * The name of the module. + */ + function assertModuleTablesDoNotExist($module) { + $tables = array_keys(drupal_get_schema_unprocessed($module)); + $tables_exist = FALSE; + foreach ($tables as $table) { + if (db_table_exists($table)) { + $tables_exist = TRUE; + } + } + return $this->assertFalse($tables_exist, t('None of the database tables defined by the @module module exist.', array('@module' => $module))); + } + + /** * Assert the list of modules are enabled or disabled. * * @param $modules @@ -96,6 +130,8 @@ class ModuleTestCase extends DrupalWebTestCase { * Test module enabling/disabling functionality. */ class EnableDisableTestCase extends ModuleTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Enable/disable modules', @@ -105,59 +141,132 @@ class EnableDisableTestCase extends ModuleTestCase { } /** - * Enable a module, check the database for related tables, disable module, - * check for related tables, uninstall module, check for related tables. - * Also check for invocation of the hook_module_action hook. + * Test that all core modules can be enabled, disabled and uninstalled. */ function testEnableDisable() { - // Enable aggregator, and check tables. - $this->assertModules(array('aggregator'), FALSE); - $this->assertTableCount('aggregator', FALSE); - - // Install (and enable) aggregator module. - $edit = array(); - $edit['modules[Core][aggregator][enable]'] = 'aggregator'; - $edit['modules[Core][forum][enable]'] = 'forum'; - $this->drupalPost('admin/modules', $edit, t('Save configuration')); - $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); - - // Check that hook_modules_installed and hook_modules_enabled hooks were invoked and check tables. - $this->assertText(t('hook_modules_installed fired for aggregator'), t('hook_modules_installed fired.')); - $this->assertText(t('hook_modules_enabled fired for aggregator'), t('hook_modules_enabled fired.')); - $this->assertModules(array('aggregator'), TRUE); - $this->assertTableCount('aggregator', TRUE); - $this->assertLogMessage('system', "%module module installed.", array('%module' => 'aggregator'), WATCHDOG_INFO); - $this->assertLogMessage('system', "%module module enabled.", array('%module' => 'aggregator'), WATCHDOG_INFO); + // Try to enable, disable and uninstall all core modules, unless they are + // hidden or required. + $modules = system_rebuild_module_data(); + foreach ($modules as $name => $module) { + if ($module->info['package'] != 'Core' || !empty($module->info['hidden']) || !empty($module->info['required'])) { + unset($modules[$name]); + } + } + $this->assertTrue(count($modules), t('Found @count core modules that we can try to enable in this test.', array('@count' => count($modules)))); + + // Enable the dblog module first, since we will be asserting the presence + // of log messages throughout the test. + if (isset($modules['dblog'])) { + $modules = array('dblog' => $modules['dblog']) + $modules; + } + + // Set a variable so that the hook implementations in system_test.module + // will display messages via drupal_set_message(). + variable_set('test_verbose_module_hooks', TRUE); + + // Throughout this test, some modules may be automatically enabled (due to + // dependencies). We'll keep track of them in an array, so we can handle + // them separately. + $automatically_enabled = array(); + + // Go through each module in the list and try to enable it (unless it was + // already enabled automatically due to a dependency). + foreach ($modules as $name => $module) { + if (empty($automatically_enabled[$name])) { + // Start a list of modules that we expect to be enabled this time. + $modules_to_enable = array($name); + + // Find out if the module has any dependencies that aren't enabled yet; + // if so, add them to the list of modules we expect to be automatically + // enabled. + foreach (array_keys($module->requires) as $dependency) { + if (isset($modules[$dependency]) && empty($automatically_enabled[$dependency])) { + $modules_to_enable[] = $dependency; + $automatically_enabled[$dependency] = TRUE; + } + } - // Disable aggregator, check tables, uninstall aggregator, check tables. - $edit = array(); - $edit['modules[Core][aggregator][enable]'] = FALSE; - $this->drupalPost('admin/modules', $edit, t('Save configuration')); - $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); + // Check that each module is not yet enabled and does not have any + // database tables yet. + foreach ($modules_to_enable as $module_to_enable) { + $this->assertModules(array($module_to_enable), FALSE); + $this->assertModuleTablesDoNotExist($module_to_enable); + } - // Check that hook_modules_disabled hook was invoked and check tables. - $this->assertText(t('hook_modules_disabled fired for aggregator'), t('hook_modules_disabled fired.')); - $this->assertModules(array('aggregator'), FALSE); - $this->assertTableCount('aggregator', TRUE); - $this->assertLogMessage('system', "%module module disabled.", array('%module' => 'aggregator'), WATCHDOG_INFO); + // Install and enable the module. + $edit = array(); + $edit['modules[Core][' . $name . '][enable]'] = $name; + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + // Handle the case where modules were installed along with this one and + // where we therefore hit a confirmation screen. + if (count($modules_to_enable) > 1) { + $this->drupalPost(NULL, array(), t('Continue')); + } + $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); + + // Check that hook_modules_installed() and hook_modules_enabled() were + // invoked with the expected list of modules, that each module's + // database tables now exist, and that appropriate messages appear in + // the logs. + foreach ($modules_to_enable as $module_to_enable) { + $this->assertText(t('hook_modules_installed fired for @module', array('@module' => $module_to_enable))); + $this->assertText(t('hook_modules_enabled fired for @module', array('@module' => $module_to_enable))); + $this->assertModules(array($module_to_enable), TRUE); + $this->assertModuleTablesExist($module_to_enable); + $this->assertLogMessage('system', "%module module installed.", array('%module' => $module_to_enable), WATCHDOG_INFO); + $this->assertLogMessage('system', "%module module enabled.", array('%module' => $module_to_enable), WATCHDOG_INFO); + } - // Uninstall the module. - $edit = array(); - $edit['uninstall[aggregator]'] = 'aggregator'; - $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall')); + // Disable and uninstall the original module, and check appropriate + // hooks, tables, and log messages. (Later, we'll go back and do the + // same thing for modules that were enabled automatically.) Skip this + // for the dblog module, because that is needed for the test; we'll go + // back and do that one at the end also. + if ($name != 'dblog') { + $this->assertSuccessfulDisableAndUninstall($name); + } + } + } - $this->drupalPost(NULL, NULL, t('Uninstall')); - $this->assertText(t('The selected modules have been uninstalled.'), t('Modules status has been updated.')); + // Go through all modules that were automatically enabled, and try to + // disable and uninstall them one by one. + while (!empty($automatically_enabled)) { + $initial_count = count($automatically_enabled); + foreach (array_keys($automatically_enabled) as $name) { + // If the module can't be disabled due to dependencies, skip it and try + // again the next time. Otherwise, try to disable it. + $this->drupalGet('admin/modules'); + $disabled_checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Core][' . $name . '][enable]"]'); + if (empty($disabled_checkbox) && $name != 'dblog') { + unset($automatically_enabled[$name]); + $this->assertSuccessfulDisableAndUninstall($name); + } + } + $final_count = count($automatically_enabled); + // If all checkboxes were disabled, something is really wrong with the + // test. Throw a failure and avoid an infinite loop. + if ($initial_count == $final_count) { + $this->fail(t('Remaining modules could not be disabled.')); + break; + } + } - // Check that hook_modules_uninstalled hook was invoked and check tables. - $this->assertText(t('hook_modules_uninstalled fired for aggregator'), t('hook_modules_uninstalled fired.')); - $this->assertModules(array('aggregator'), FALSE); - $this->assertTableCount('aggregator', FALSE); - $this->assertLogMessage('system', "%module module uninstalled.", array('%module' => 'aggregator'), WATCHDOG_INFO); + // Disable and uninstall the dblog module last, since we needed it for + // assertions in all the above tests. + if (isset($modules['dblog'])) { + $this->assertSuccessfulDisableAndUninstall('dblog'); + } - // Reinstall (and enable) aggregator module. + // Now that all modules have been tested, go back and try to enable them + // all again at once. This tests two things: + // - That each module can be successfully enabled again after being + // uninstalled. + // - That enabling more than one module at the same time does not lead to + // any errors. $edit = array(); - $edit['modules[Core][aggregator][enable]'] = 'aggregator'; + foreach (array_keys($modules) as $name) { + $edit['modules[Core][' . $name . '][enable]'] = $name; + } $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); } @@ -174,6 +283,49 @@ class EnableDisableTestCase extends ModuleTestCase { $this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.'); $this->assertEqual($info['controller class'], 'DrupalDefaultEntityController', 'Entity controller class info is correct.'); } + + /** + * Disables and uninstalls a module and asserts that it was done correctly. + * + * @param $module + * The name of the module to disable and uninstall. + */ + function assertSuccessfulDisableAndUninstall($module) { + // Disable the module. + $edit = array(); + $edit['modules[Core][' . $module . '][enable]'] = FALSE; + $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); + $this->assertModules(array($module), FALSE); + + // Check that the appropriate hook was fired and the appropriate log + // message appears. + $this->assertText(t('hook_modules_disabled fired for @module', array('@module' => $module))); + $this->assertLogMessage('system', "%module module disabled.", array('%module' => $module), WATCHDOG_INFO); + + // Check that the module's database tables still exist. + $this->assertModuleTablesExist($module); + + // Uninstall the module. + $edit = array(); + $edit['uninstall[' . $module . ']'] = $module; + $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall')); + $this->drupalPost(NULL, NULL, t('Uninstall')); + $this->assertText(t('The selected modules have been uninstalled.'), t('Modules status has been updated.')); + $this->assertModules(array($module), FALSE); + + // Check that the appropriate hook was fired and the appropriate log + // message appears. (But don't check for the log message if the dblog + // module was just uninstalled, since the {watchdog} table won't be there + // anymore.) + $this->assertText(t('hook_modules_uninstalled fired for @module', array('@module' => $module))); + if ($module != 'dblog') { + $this->assertLogMessage('system', "%module module uninstalled.", array('%module' => $module), WATCHDOG_INFO); + } + + // Check that the module's database tables no longer exist. + $this->assertModuleTablesDoNotExist($module); + } } /** @@ -196,7 +348,7 @@ class HookRequirementsTestCase extends ModuleTestCase { // Attempt to install the requirements1_test module. $edit = array(); - $edit['modules[Core][requirements1_test][enable]'] = 'requirements1_test'; + $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); // Makes sure the module was NOT installed. @@ -278,8 +430,8 @@ class ModuleDependencyTestCase extends ModuleTestCase { // Attempt to install both modules at the same time. $edit = array(); - $edit['modules[Core][requirements1_test][enable]'] = 'requirements1_test'; - $edit['modules[Core][requirements2_test][enable]'] = 'requirements2_test'; + $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test'; + $edit['modules[Testing][requirements2_test][enable]'] = 'requirements2_test'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); // Makes sure the modules were NOT installed. @@ -1210,6 +1362,8 @@ class FrontPageTestCase extends DrupalWebTestCase { } class SystemBlockTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Block functionality', @@ -1219,17 +1373,17 @@ class SystemBlockTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp(); + parent::setUp('block'); // Create and login user - $admin_user = $this->drupalCreateUser(array('administer blocks')); + $admin_user = $this->drupalCreateUser(array('administer blocks', 'access administration pages')); $this->drupalLogin($admin_user); } /** - * Test displaying and hiding the powered-by block. + * Test displaying and hiding the powered-by and help blocks. */ - function testPoweredByBlock() { + function testSystemBlocks() { // Set block title and some settings to confirm that the interface is available. $this->drupalPost('admin/structure/block/manage/system/powered-by/configure', array('title' => $this->randomName(8)), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); @@ -1237,6 +1391,7 @@ class SystemBlockTestCase extends DrupalWebTestCase { // Set the powered-by block to the footer region. $edit = array(); $edit['blocks[system_powered-by][region]'] = 'footer'; + $edit['blocks[system_main][region]'] = 'content'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to footer region.')); @@ -1257,6 +1412,18 @@ class SystemBlockTestCase extends DrupalWebTestCase { $edit['blocks[system_powered-by][region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->drupalPost('admin/structure/block/manage/system/powered-by/configure', array('title' => ''), t('Save block')); + + // Set the help block to the help region. + $edit = array(); + $edit['blocks[system_help][region]'] = 'help'; + $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); + + // Test displaying the help block with block caching enabled. + variable_set('block_cache', TRUE); + $this->drupalGet('admin/structure/block/add'); + $this->assertRaw(t('Use this page to create a new custom block.')); + $this->drupalGet('admin/index'); + $this->assertRaw(t('This page shows you all available administration tasks for each module.')); } } |