Issue #1029606 follow-up by catch, David_Rothstein: Fixed Regression: Not loading the .module file causes a fatal error when uninstalling some modules (as does loading it).

8.0.x
webchick 2011-06-15 00:45:51 -04:00
parent 2c0b028d20
commit 5a38d9a9a4
8 changed files with 241 additions and 69 deletions

View File

@ -32,6 +32,7 @@ function shortcut_install() {
* Implements hook_uninstall(). * Implements hook_uninstall().
*/ */
function shortcut_uninstall() { function shortcut_uninstall() {
drupal_load('module', 'shortcut');
// Delete the menu links associated with each shortcut set. // Delete the menu links associated with each shortcut set.
foreach (shortcut_sets() as $shortcut_set) { foreach (shortcut_sets() as $shortcut_set) {
menu_delete_links($shortcut_set->set_name); menu_delete_links($shortcut_set->set_name);

View File

@ -167,7 +167,8 @@ function simpletest_schema() {
* Implements hook_uninstall(). * Implements hook_uninstall().
*/ */
function simpletest_uninstall() { function simpletest_uninstall() {
simpletest_clean_environment(); drupal_load('module', 'simpletest');
simpletest_clean_database();
// Remove settings variables. // Remove settings variables.
variable_del('simpletest_httpauth_method'); variable_del('simpletest_httpauth_method');

View File

@ -452,13 +452,15 @@ function simpletest_clean_database() {
* Find all leftover temporary directories and remove them. * Find all leftover temporary directories and remove them.
*/ */
function simpletest_clean_temporary_directories() { function simpletest_clean_temporary_directories() {
$files = scandir('public://simpletest');
$count = 0; $count = 0;
foreach ($files as $file) { if (is_dir('public://simpletest')) {
$path = 'public://simpletest/' . $file; $files = scandir('public://simpletest');
if (is_dir($path) && is_numeric($file)) { foreach ($files as $file) {
file_unmanaged_delete_recursive($path); $path = 'public://simpletest/' . $file;
$count++; if (is_dir($path) && is_numeric($file)) {
file_unmanaged_delete_recursive($path);
$count++;
}
} }
} }

View File

@ -1,6 +1,6 @@
name = Requirements 1 Test name = Requirements 1 Test
description = "Tests that a module is not installed when it fails hook_requirements('install')." description = "Tests that a module is not installed when it fails hook_requirements('install')."
package = Core package = Testing
version = VERSION version = VERSION
core = 8.x core = 8.x
hidden = TRUE hidden = TRUE

View File

@ -2,7 +2,7 @@ name = Requirements 2 Test
description = "Tests that a module is not installed when the one it depends on fails hook_requirements('install)." description = "Tests that a module is not installed when the one it depends on fails hook_requirements('install)."
dependencies[] = requirements1_test dependencies[] = requirements1_test
dependencies[] = comment dependencies[] = comment
package = Core package = Testing
version = VERSION version = VERSION
core = 8.x core = 8.x
hidden = TRUE hidden = TRUE

View File

@ -146,8 +146,10 @@ function system_test_redirect_invalid_scheme() {
* Implements hook_modules_installed(). * Implements hook_modules_installed().
*/ */
function system_test_modules_installed($modules) { function system_test_modules_installed($modules) {
if (in_array('aggregator', $modules)) { if (variable_get('test_verbose_module_hooks')) {
drupal_set_message(t('hook_modules_installed fired for aggregator')); foreach ($modules as $module) {
drupal_set_message(t('hook_modules_installed fired for @module', array('@module' => $module)));
}
} }
} }
@ -155,8 +157,10 @@ function system_test_modules_installed($modules) {
* Implements hook_modules_enabled(). * Implements hook_modules_enabled().
*/ */
function system_test_modules_enabled($modules) { function system_test_modules_enabled($modules) {
if (in_array('aggregator', $modules)) { if (variable_get('test_verbose_module_hooks')) {
drupal_set_message(t('hook_modules_enabled fired for aggregator')); foreach ($modules as $module) {
drupal_set_message(t('hook_modules_enabled fired for @module', array('@module' => $module)));
}
} }
} }
@ -164,8 +168,10 @@ function system_test_modules_enabled($modules) {
* Implements hook_modules_disabled(). * Implements hook_modules_disabled().
*/ */
function system_test_modules_disabled($modules) { function system_test_modules_disabled($modules) {
if (in_array('aggregator', $modules)) { if (variable_get('test_verbose_module_hooks')) {
drupal_set_message(t('hook_modules_disabled fired for aggregator')); foreach ($modules as $module) {
drupal_set_message(t('hook_modules_disabled fired for @module', array('@module' => $module)));
}
} }
} }
@ -173,8 +179,10 @@ function system_test_modules_disabled($modules) {
* Implements hook_modules_uninstalled(). * Implements hook_modules_uninstalled().
*/ */
function system_test_modules_uninstalled($modules) { function system_test_modules_uninstalled($modules) {
if (in_array('aggregator', $modules)) { if (variable_get('test_verbose_module_hooks')) {
drupal_set_message(t('hook_modules_uninstalled fired for aggregator')); foreach ($modules as $module) {
drupal_set_message(t('hook_modules_uninstalled fired for @module', array('@module' => $module)));
}
} }
} }

View File

@ -3267,6 +3267,14 @@ function hook_update_last_removed() {
* module's database tables are removed, allowing your module to query its own * module's database tables are removed, allowing your module to query its own
* tables during this routine. * 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_install()
* @see hook_schema() * @see hook_schema()
* @see hook_disable() * @see hook_disable()

View File

@ -36,6 +36,40 @@ class ModuleTestCase extends DrupalWebTestCase {
return $this->assertFalse($tables, t('Tables matching "@base_table" not found.', array('@base_table' => $base_table))); return $this->assertFalse($tables, t('Tables matching "@base_table" not found.', array('@base_table' => $base_table)));
} }
/**
* 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. * Assert the list of modules are enabled or disabled.
* *
@ -96,6 +130,8 @@ class ModuleTestCase extends DrupalWebTestCase {
* Test module enabling/disabling functionality. * Test module enabling/disabling functionality.
*/ */
class EnableDisableTestCase extends ModuleTestCase { class EnableDisableTestCase extends ModuleTestCase {
protected $profile = 'testing';
public static function getInfo() { public static function getInfo() {
return array( return array(
'name' => 'Enable/disable modules', 'name' => 'Enable/disable modules',
@ -105,59 +141,132 @@ class EnableDisableTestCase extends ModuleTestCase {
} }
/** /**
* Enable a module, check the database for related tables, disable module, * Test that all core modules can be enabled, disabled and uninstalled.
* check for related tables, uninstall module, check for related tables.
* Also check for invocation of the hook_module_action hook.
*/ */
function testEnableDisable() { function testEnableDisable() {
// Enable aggregator, and check tables. // Try to enable, disable and uninstall all core modules, unless they are
$this->assertModules(array('aggregator'), FALSE); // hidden or required.
$this->assertTableCount('aggregator', FALSE); $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))));
// Install (and enable) aggregator module. // 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;
}
}
// 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);
}
// 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), LOG_INFO);
$this->assertLogMessage('system', "%module module enabled.", array('%module' => $module_to_enable), LOG_INFO);
}
// 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);
}
}
}
// 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;
}
}
// 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');
}
// 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 = array();
$edit['modules[Core][aggregator][enable]'] = 'aggregator'; foreach (array_keys($modules) as $name) {
$edit['modules[Core][forum][enable]'] = 'forum'; $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.'));
// 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'), LOG_INFO);
$this->assertLogMessage('system', "%module module enabled.", array('%module' => 'aggregator'), LOG_INFO);
// 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 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'), LOG_INFO);
// Uninstall the module.
$edit = array();
$edit['uninstall[aggregator]'] = 'aggregator';
$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.'));
// 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'), LOG_INFO);
// Reinstall (and enable) aggregator module.
$edit = array();
$edit['modules[Core][aggregator][enable]'] = 'aggregator';
$this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->drupalPost('admin/modules', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); $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['label'], 'Entity Cache Test', 'Entity info label is correct.');
$this->assertEqual($info['controller class'], 'DrupalDefaultEntityController', 'Entity controller class info 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), LOG_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), LOG_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. // Attempt to install the requirements1_test module.
$edit = array(); $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')); $this->drupalPost('admin/modules', $edit, t('Save configuration'));
// Makes sure the module was NOT installed. // Makes sure the module was NOT installed.
@ -278,8 +430,8 @@ class ModuleDependencyTestCase extends ModuleTestCase {
// Attempt to install both modules at the same time. // Attempt to install both modules at the same time.
$edit = array(); $edit = array();
$edit['modules[Core][requirements1_test][enable]'] = 'requirements1_test'; $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test';
$edit['modules[Core][requirements2_test][enable]'] = 'requirements2_test'; $edit['modules[Testing][requirements2_test][enable]'] = 'requirements2_test';
$this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->drupalPost('admin/modules', $edit, t('Save configuration'));
// Makes sure the modules were NOT installed. // Makes sure the modules were NOT installed.