upgradedSite = FALSE; $this->upgradeErrors = array(); // Store necessary current values before switching to prefixed database. $this->originalLanguage = $language; $this->originalLanguageDefault = variable_get('language_default'); $this->originalPrefix = $db_prefix; $this->originalFileDirectory = file_directory_path(); $this->originalProfile = drupal_get_profile(); $clean_url_original = variable_get('clean_url', 0); // Generate temporary prefixed database to ensure that tests have a clean starting point. $db_prefix_new = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}'); db_update('simpletest_test_id') ->fields(array('last_prefix' => $db_prefix_new)) ->condition('test_id', $this->testId) ->execute(); $db_prefix = $db_prefix_new; // Unregister the registry. // This is required to make sure that the database layer works properly. spl_autoload_unregister('drupal_autoload_class'); spl_autoload_unregister('drupal_autoload_interface'); // Create test directories ahead of installation so fatal errors and debug // information can be logged during installation process. // Use mock files directories with the same prefix as the database. $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10); $private_files_directory = $public_files_directory . '/private'; $temp_files_directory = $private_files_directory . '/temp'; // Create the directories. file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY); file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY); $this->generatedTestFiles = FALSE; // Log fatal errors. ini_set('log_errors', 1); ini_set('error_log', $public_files_directory . '/error.log'); // Reset all statics and variables to perform tests in a clean environment. $conf = array(); // Load the database from the portable PHP dump. require $this->databaseDumpFile; // Set path variables. $this->variable_set('file_public_path', $public_files_directory); $this->variable_set('file_private_path', $private_files_directory); $this->variable_set('file_temporary_path', $temp_files_directory); $this->pass('Finished loading the dump.'); // Load user 1. $this->originalUser = $user; drupal_save_session(FALSE); $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject(); // Generate and set a session cookie. $this->curlInitialize(); $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55)); curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($this->session_name) . '=' . rawurlencode($sid)); // Force our way into the session of the child site. drupal_save_session(TRUE); _drupal_session_write($sid, ''); drupal_save_session(FALSE); // Restore necessary variables. $this->variable_set('clean_url', $clean_url_original); $this->variable_set('site_mail', 'simpletest@example.com'); drupal_set_time_limit($this->timeLimit); } /** * Override of DrupalWebTestCase::tearDown() specialized for upgrade testing. */ protected function tearDown() { global $db_prefix, $user, $language; // In case a fatal error occured that was not in the test process read the // log to pick up any fatal errors. $db_prefix_temp = $db_prefix; $db_prefix = $this->originalPrefix; simpletest_log_read($this->testId, $db_prefix, get_class($this), TRUE); $db_prefix = $db_prefix_temp; if (preg_match('/simpletest\d+/', $db_prefix)) { // Delete temporary files directory. file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10)); // Remove all prefixed tables (all the tables in the schema). $tables = db_find_tables($this->originalPrefix . '%'); foreach ($tables as $table) { db_drop_table($table); } // Return the database prefix to the original. $db_prefix = $this->originalPrefix; // Return the user to the original one. $user = $this->originalUser; drupal_save_session(TRUE); // Ensure that internal logged in variable and cURL options are reset. $this->loggedInUser = FALSE; $this->additionalCurlOptions = array(); // Reload module list and implementations to ensure that test module hooks // aren't called after tests. module_list(TRUE); module_implements('', FALSE, TRUE); // Reset the Field API. field_cache_clear(); // Rebuild caches. parent::refreshVariables(); // Reset language. $language = $this->originalLanguage; if ($this->originalLanguageDefault) { $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault; } // Close the CURL handler. $this->curlClose(); } } /** * Specialized variable_set() that works even if the child site is not upgraded. * * @param $name * The name of the variable to set. * @param $value * The value to set. This can be any PHP data type; these functions take care * of serialization as necessary. */ protected function variable_set($name, $value) { db_delete('variable') ->condition('name', $name) ->execute(); db_insert('variable') ->fields(array( 'name' => $name, 'value' => serialize($value), )) ->execute(); try { cache_clear_all('variables', 'cache'); cache_clear_all('variables', 'cache_bootstrap'); } // Since cache_bootstrap won't exist in a Drupal 6 site, ignore the // exception if the above fails. catch (Exception $e) {} } /** * Specialized refreshVariables(). */ protected function refreshVariables() { // No operation if the child has not been upgraded yet. if (!$this->upgradedSite) { return parent::refreshVariables(); } } /** * Perform the upgrade. * * @param $register_errors * Register the errors during the upgrade process as failures. * @return * TRUE if the upgrade succeeded, FALSE otherwise. */ protected function performUpgrade($register_errors = TRUE) { $update_url = $GLOBALS['base_url'] . '/update.php'; // Load the first update screen. $this->drupalGet($update_url, array('external' => TRUE)); if (!$this->assertResponse(200)) { return FALSE; } // Continue. $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertResponse(200)) { return FALSE; } // Go! $this->drupalPost(NULL, array(), t('Apply pending updates')); if (!$this->assertResponse(200)) { return FALSE; } // Check for errors during the update process. foreach ($this->xpath('//li[@class=:class]', array(':class' => 'failure')) as $element) { $message = strip_tags($element->asXML()); $this->upgradeErrors[] = $message; if ($register_errors) { $this->fail($message); } } if (!empty($this->upgradeErrors)) { // Upgrade failed, the installation might be in an inconsistent state, // don't process. return FALSE; } // Check if there still are pending updates. $this->drupalGet($update_url, array('external' => TRUE)); $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) { return FALSE; } // Upgrade succeed, rebuild the environment so that we can call the API // of the child site directly from this request. $this->upgradedSite = TRUE; // Reload module list and implementations. module_list(TRUE); module_implements('', FALSE, TRUE); // Rebuild caches. drupal_static_reset(); drupal_flush_all_caches(); // Register actions declared by any modules. actions_synchronize(); // Reload global $conf array and permissions. $this->refreshVariables(); $this->checkPermissions(array(), TRUE); return TRUE; } } /** * Perform basic upgrade tests. * * Load a bare installation of Drupal 6 and run the upgrade process on it. * * The install only contains dblog (although it's optional, it's only so that * another hook_watchdog module can take its place, the site is not functional * without watchdog) and update. */ class BasicUpgradePath extends UpgradePathTestCase { public static function getInfo() { return array( 'name' => 'Basic upgrade path', 'description' => 'Basic upgrade path tests.', 'group' => 'Upgrade path', ); } public function setUp() { // Path to the database dump. $this->databaseDumpFile = drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.bare.database.php'; parent::setUp(); } /** * Test a failed upgrade, and verify that the failure is reported. */ public function testFailedUpgrade() { // Destroy a table that the upgrade process needs. db_drop_table('access'); // Assert that the upgrade fails. $this->assertFalse($this->performUpgrade(FALSE), t('A failed upgrade should return messages.')); } /** * Test a successful upgrade. */ public function testBasicUpgrade() { $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); // Hit the frontpage. $this->drupalGet(''); $this->assertResponse(200); // Verify that we are still logged in. $this->drupalGet('user'); $this->clickLink(t('Edit')); $this->assertEqual($this->getUrl(), url('user/1/edit', array('absolute' => TRUE)), t('We are still logged in as admin at the end of the upgrade.')); // Logout and verify that we can login back in with our initial password. $this->drupalLogout(); $this->drupalLogin((object) array( 'uid' => 1, 'name' => 'admin', 'pass_raw' => 'admin', )); // Test that the site name is correctly displayed. $this->assertText('Drupal 6', t('The site name is correctly displayed.')); // Verify that the main admin sections are available. $this->drupalGet('admin'); $this->assertText(t('Content')); $this->assertText(t('Appearance')); $this->assertText(t('People')); $this->assertText(t('Configuration')); $this->assertText(t('Reports')); $this->assertText(t('Structure')); $this->assertText(t('Modules')); } }