#347959: modules can't safely fire hooks during simpletest installation phase.

From: Damien Tournoud <damien@tournoud.net>


---

 includes/registry.inc                       |    6 ++++
 modules/simpletest/drupal_web_test_case.php |   46 ++++++++++++++++++++++++---
 2 files changed, 47 insertions(+), 5 deletions(-)


diff --git includes/registry.inc includes/registry.inc
index 505228b..2c46928 100644
--- includes/registry.inc
+++ includes/registry.inc
@@ -37,6 +37,12 @@ function _registry_rebuild() {
   require_once DRUPAL_ROOT . '/includes/database/select.inc';
   require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
 
+  // When running in SimpleTest mode, we preload the child registry using what
+  // we already know from the parent registry.
+  if (class_exists('DrupalWebTestCase', FALSE) && ($instance = DrupalWebTestCase::currentlyRunning())) {
+    $instance->preloadRegistry();
+  }
+
   // Reset the resources cache.
   _registry_get_resource_name();
   // Get the list of files we are going to parse.
diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index 04ba135..8fd0858 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -99,6 +99,17 @@ class DrupalWebTestCase {
   protected $originalUser = NULL;
 
   /**
+   * The instance of the currently running DrupalWebTestCase.
+   */
+  protected static $instance = NULL;
+
+  /**
+   * Indicate to DrupalWebTestCase::preloadRegistry() that it is safe to
+   * preload the registry of the child site with information from the parent site.
+   */
+  protected $canPreloadRegistry = FALSE;
+
+  /**
    * Current results of this test case.
    *
    * @var Array
@@ -117,6 +128,13 @@ class DrupalWebTestCase {
   protected $assertions = array();
 
   /**
+   * Get the currently running DrupalWebTestCase, if any.
+   */
+  public static function currentlyRunning() {
+    return self::$instance;
+  }
+
+  /**
    * Constructor for DrupalWebTestCase.
    *
    * @param $test_id
@@ -795,16 +813,30 @@ class DrupalWebTestCase {
     // Generate temporary prefixed database to ensure that tests have a clean starting point.
     $db_prefix = Database::getActiveConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
 
+    // Register this instance as the currently running one.
+    self::$instance = $this;
+
     include_once DRUPAL_ROOT . '/includes/install.inc';
     drupal_install_system();
 
-    $this->preloadRegistry();
+    // Refresh the module list and the implementation cache so that modules
+    // are installed in a clean environment and can safely fire hooks.
+    module_list(TRUE);
+    registry_rebuild();
+
+    // It is now useful to preload the child registry site with information
+    // from the parent registry.
+    $this->canPreloadRegistry = TRUE;
 
     // Add the specified modules to the list of modules in the default profile.
     $args = func_get_args();
     $modules = array_unique(array_merge(drupal_get_profile_modules('default', 'en'), $args));
     drupal_install_modules($modules);
 
+    // At this point, we don't want that latter calls to registry_rebuild use
+    // preloading, because it would be inefficient.
+    $this->canPreloadRegistry = FALSE;
+
     // Because the schema is static cached, we need to flush
     // it between each run. If we don't, then it will contain
     // stale data for the previous run's database prefix and all
@@ -840,13 +872,17 @@ class DrupalWebTestCase {
   }
 
   /**
-   * This method is called by DrupalWebTestCase::setUp, and preloads the
+   * This method is called by _registry_rebuild(), and preloads the
    * registry from the testing site to cut down on the time it takes to
    * setup a clean environment for the current test run.
    */
-  protected function preloadRegistry() {
-    db_query('INSERT INTO {registry} SELECT * FROM ' . $this->originalPrefix . 'registry');
-    db_query('INSERT INTO {registry_file} SELECT * FROM ' . $this->originalPrefix . 'registry_file');
+  public function preloadRegistry() {
+    if ($this->canPreloadRegistry) {
+      db_delete('registry')->execute();
+      db_delete('registry_file')->execute();
+      db_query('INSERT INTO {registry} SELECT * FROM ' . $this->originalPrefix . 'registry');
+      db_query('INSERT INTO {registry_file} SELECT * FROM ' . $this->originalPrefix . 'registry_file');
+    }
   }
 
   /**
