summaryrefslogtreecommitdiff
path: root/modules/simpletest/tests/upgrade/upgrade.test
diff options
context:
space:
mode:
Diffstat (limited to 'modules/simpletest/tests/upgrade/upgrade.test')
-rw-r--r--modules/simpletest/tests/upgrade/upgrade.test347
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'));
+ }
+}