Newer
Older
Angie Byron
committed
<?php
/**
* @file
* Definition of Drupal\system\Tests\Upgrade\UpgradePathTestBase.
*/
namespace Drupal\system\Tests\Upgrade;
use Drupal\Core\Database\Database;
use Drupal\simpletest\WebTestBase;
Angie Byron
committed
use Exception;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
Angie Byron
committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* Perform end-to-end tests of the upgrade path.
*/
abstract class UpgradePathTestBase extends WebTestBase {
/**
* The file path(s) to the dumped database(s) to load into the child site.
*
* @var array
*/
var $databaseDumpFiles = array();
/**
* Flag that indicates whether the child site has been upgraded.
*/
var $upgradedSite = FALSE;
/**
* Array of errors triggered during the upgrade process.
*/
var $upgradeErrors = array();
/**
* Flag to indicate whether there are pending updates or not.
*/
var $pendingUpdates = TRUE;
/**
* Prepares the appropriate session for the release of Drupal being upgraded.
*/
protected function prepareD8Session() {
// Generate and set a D7-compatible session cookie.
$this->curlInitialize();
$sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode(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);
}
/**
* Checks that zlib is enabled in order to run the upgrade tests.
*/
protected function checkRequirements() {
if (!function_exists('gzopen')) {
return array(
'Missing zlib requirement for upgrade tests.',
);
}
return parent::checkRequirements();
}
/**
* Overrides Drupal\simpletest\WebTestBase::setUp() for upgrade testing.
*
* @see Drupal\simpletest\WebTestBase::prepareDatabasePrefix()
* @see Drupal\simpletest\WebTestBase::changeDatabasePrefix()
* @see Drupal\simpletest\WebTestBase::prepareEnvironment()
*/
protected function setUp() {
global $user, $conf;
// Load the Update API.
require_once DRUPAL_ROOT . '/core/includes/update.inc';
// Reset flags.
$this->upgradedSite = FALSE;
$this->upgradeErrors = array();
// Create the database prefix for this test.
$this->prepareDatabasePrefix();
// Prepare the environment for running tests.
$this->prepareEnvironment();
if (!$this->setupEnvironment) {
return FALSE;
}
Angie Byron
committed
// Reset all statics and variables to perform tests in a clean environment.
$conf = array();
drupal_static_reset();
// Change the database prefix.
// All static variables need to be reset before the database prefix is
// changed, since Drupal\Core\Utility\CacheArray implementations attempt to
// write back to persistent caches when they are destructed.
$this->changeDatabasePrefix();
if (!$this->setupDatabasePrefix) {
return FALSE;
}
Angie Byron
committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Load the database from the portable PHP dump.
// The files may be gzipped.
foreach ($this->databaseDumpFiles as $file) {
if (substr($file, -3) == '.gz') {
$file = "compress.zlib://$file";
}
require $file;
}
// Set path variables.
$this->variable_set('file_public_path', $this->public_files_directory);
$this->variable_set('file_private_path', $this->private_files_directory);
$this->variable_set('file_temporary_path', $this->temp_files_directory);
$this->pass('Finished loading the dump.');
// Ensure that the session is not written to the new environment and replace
// the global $user session with uid 1 from the new test site.
drupal_save_session(FALSE);
// Login as uid 1.
$user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject();
// Generate and set a D8-compatible session cookie.
$this->prepareD8Session();
// Restore necessary variables.
$this->variable_set('site_mail', 'simpletest@example.com');
drupal_set_time_limit($this->timeLimit);
$this->setup = TRUE;
}
/**
* Overrides \Drupal\simpletest\TestBase::prepareConfigDirectories().
*/
protected function prepareConfigDirectories() {
// The configuration directories are prepared as part of the first access to
// update.php.
}
Angie Byron
committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* 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.
*
* @todo Update for D8 configuration system.
*/
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()->delete('variables');
cache('bootstrap')->delete('variables');
}
// 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() {
// Refresh the variables only if the site was already upgraded.
if ($this->upgradedSite) {
global $conf;
cache('bootstrap')->delete('variables');
$conf = variable_initialize();
$container = drupal_container();
if ($container->has('config.factory')) {
$container->get('config.factory')->reset();
}
Angie Byron
committed
}
}
/**
* 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) {
// Load the first update screen.
$this->getUpdatePhp();
Angie Byron
committed
if (!$this->assertResponse(200)) {
throw new Exception('Initial GET to update.php did not return HTTP 200 status.');
Angie Byron
committed
}
// Ensure that the first update screen appeared correctly.
if (!$this->assertFieldByXPath('//input[@type="submit"]')) {
throw new Exception('An error was encountered during the first access to update.php.');
}
// Initialize config directories and rebuild the service container after
// creating them in the first step.
parent::prepareConfigDirectories();
$this->rebuildContainer();
Angie Byron
committed
// Continue.
$this->drupalPost(NULL, array(), t('Continue'));
if (!$this->assertResponse(200)) {
throw new Exception('POST to continue update.php did not return HTTP 200 status.');
Angie Byron
committed
}
// The test should pass if there are no pending updates.
$content = $this->drupalGetContent();
if (strpos($content, t('No pending updates.')) !== FALSE) {
$this->pass('No pending updates and therefore no upgrade process to test.');
Angie Byron
committed
$this->pendingUpdates = FALSE;
return TRUE;
}
// Go!
$this->drupalPost(NULL, array(), t('Apply pending updates'));
if (!$this->assertResponse(200)) {
throw new Exception('POST to update.php to apply pending updates did not return HTTP 200 status.');
Angie Byron
committed
}
// 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.
throw new Exception('Errors during update process.');
Angie Byron
committed
}
// Check if there still are pending updates.
$this->getUpdatePhp();
Angie Byron
committed
$this->drupalPost(NULL, array(), t('Continue'));
if (!$this->assertText(t('No pending updates.'), 'No pending updates at the end of the update process.')) {
throw new Exception('update.php still shows pending updates after execution.');
Angie Byron
committed
}
// Upgrade succeed, rebuild the environment so that we can call the API
// of the child site directly from this request.
$this->upgradedSite = TRUE;
// Force a variable refresh as we only just enabled it.
$this->refreshVariables();
// Reload module list for modules that are enabled in the test database
// but not on the test client.
drupal_container()->get('module_handler')->resetImplementations();
drupal_container()->get('module_handler')->reload();
Angie Byron
committed
// Rebuild the container and all caches.
$this->rebuildContainer();
$this->resetAll();
Angie Byron
committed
return TRUE;
}
/**
* Gets update.php without calling url().
Angie Byron
committed
*
* Required since WebTestBase::drupalGet() calls t(), which calls into
* system_list(), from the parent site/test runner, before update.php is even
* executed.
*
* @see WebTestBase::drupalGet()
Angie Byron
committed
*/
protected function getUpdatePhp() {
$path = $GLOBALS['base_url'] . '/core/update.php';
$out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $path, CURLOPT_NOBODY => FALSE));
// Ensure that any changes to variables in the other thread are picked up.
$this->refreshVariables();
Angie Byron
committed
// Replace original page output with new output from redirected page(s).
if ($new = $this->checkForMetaRefresh()) {
$out = $new;
}
$this->verbose('GET request to: ' . $path .
'<hr />Ending URL: ' . $this->getUrl() .
'<hr />' . $out);
return $out;