diff options
Diffstat (limited to 'modules/simpletest/tests/upgrade/upgrade.test')
-rw-r--r-- | modules/simpletest/tests/upgrade/upgrade.test | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test new file mode 100644 index 000000000..f02321ce6 --- /dev/null +++ b/modules/simpletest/tests/upgrade/upgrade.test @@ -0,0 +1,347 @@ +<?php +// $Id$ + +/** + * Perform end-to-end tests of the upgrade path. + */ +abstract class UpgradePathTestCase extends DrupalWebTestCase { + + /** + * The file path to the dumped database to load into the child site. + */ + var $databaseDumpFile = NULL; + + /** + * Flag that indicates whether the child site has been upgraded. + */ + var $upgradedSite = FALSE; + + /** + * Array of errors triggered during the upgrade process. + */ + var $upgradeErrors = array(); + + /** + * Override of DrupalWebTestCase::setUp() specialized for upgrade testing. + */ + protected function setUp() { + global $db_prefix, $user, $language, $conf, $databases; + + // Reset flags. + $this->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')); + } +} |