Newer
Older
<?php
/**
catch
committed
* @file
* Definition of Drupal\simpletest\WebTestBase.
*/
catch
committed
namespace Drupal\simpletest;
Angie Byron
committed
Dries Buytaert
committed
use Drupal\Component\Utility\NestedArray;
Angie Byron
committed
use Drupal\Core\DrupalKernel;
catch
committed
use Drupal\Core\Database\Database;
use Drupal\Core\Database\ConnectionNotDefinedException;
use PDO;
use stdClass;
use DOMDocument;
use DOMXPath;
use SimpleXMLElement;
Dries Buytaert
committed
/**
* Test case for typical Drupal tests.
*/
catch
committed
abstract class WebTestBase extends TestBase {
Dries Buytaert
committed
/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'testing';
Dries Buytaert
committed
Dries Buytaert
committed
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
/**
* The URL currently loaded in the internal browser.
*
* @var string
*/
protected $url;
/**
* The handle of the current cURL connection.
*
* @var resource
*/
protected $curlHandle;
/**
* The headers of the page currently loaded in the internal browser.
*
* @var Array
*/
protected $headers;
/**
* The content of the page currently loaded in the internal browser.
*
* @var string
*/
protected $content;
/**
* The content of the page currently loaded in the internal browser (plain text version).
*
* @var string
*/
protected $plainTextContent;
Dries Buytaert
committed
/**
* The value of the Drupal.settings JavaScript variable for the page currently loaded in the internal browser.
*
* @var Array
*/
protected $drupalSettings;
Dries Buytaert
committed
/**
* The parsed version of the page.
*
* @var SimpleXMLElement
*/
protected $elements = NULL;
/**
* The current user logged in using the internal browser.
*
* @var bool
*/
protected $loggedInUser = FALSE;
/**
* The current cookie file used by cURL.
*
* We do not reuse the cookies in further runs, so we do not need a file
* but we still need cookie handling, so we set the jar to NULL.
*/
protected $cookieFile = NULL;
/**
* Additional cURL options.
*
catch
committed
* Drupal\simpletest\WebTestBase itself never sets this but always obeys what is
* set.
Dries Buytaert
committed
*/
protected $additionalCurlOptions = array();
/**
* The original user, before it was changed to a clean uid = 1 for testing purposes.
*
* @var object
*/
protected $originalUser = NULL;
/**
* The original shutdown handlers array, before it was cleaned for testing purposes.
*
* @var array
*/
protected $originalShutdownCallbacks = array();
Dries Buytaert
committed
/**
* HTTP authentication method
*/
protected $httpauth_method = CURLAUTH_BASIC;
Dries Buytaert
committed
/**
* HTTP authentication credentials (<username>:<password>).
*/
protected $httpauth_credentials = NULL;
/**
* The current session name, if available.
*/
protected $session_name = NULL;
/**
* The current session ID, if available.
*/
protected $session_id = NULL;
Dries Buytaert
committed
/**
* Whether the files were copied to the test files directory.
*/
protected $generatedTestFiles = FALSE;
/**
* The maximum number of redirects to follow when handling responses.
*/
protected $maximumRedirects = 5;
Dries Buytaert
committed
/**
* The number of redirects followed during the handling of a request.
*/
protected $redirect_count;
Angie Byron
committed
/**
* The kernel used in this test.
*/
protected $kernel;
Dries Buytaert
committed
/**
catch
committed
* Constructor for Drupal\simpletest\WebTestBase.
Dries Buytaert
committed
*/
function __construct($test_id = NULL) {
parent::__construct($test_id);
$this->skipClasses[__CLASS__] = TRUE;
}
/**
* Get a node from the database based on its title.
*
Angie Byron
committed
* @param $title
* A node title, usually generated by $this->randomName().
Dries Buytaert
committed
* @param $reset
* (optional) Whether to reset the entity cache.
*
* @return
* A node entity matching $title.
*/
Dries Buytaert
committed
function drupalGetNodeByTitle($title, $reset = FALSE) {
if ($reset) {
Angie Byron
committed
drupal_container()->get('plugin.manager.entity')->getStorageController('node')->resetCache();
}
$nodes = entity_load_multiple_by_properties('node', array('title' => $title));
// Load the first node returned from the database.
$returned_node = reset($nodes);
return $returned_node;
}
/**
* Creates a node based on default settings.
*
Angie Byron
committed
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
* @param array $settings
* (optional) An associative array of settings for the node, as used in
* entity_create(). Override the defaults by specifying the key and value
* in the array, for example:
* @code
* $this->drupalCreateNode(array(
* 'title' => t('Hello, world!'),
* 'type' => 'article',
* ));
* @endcode
* The following defaults are provided:
* - body: Random string using the default filter format:
* @code
* $settings['body'][LANGUAGE_NOT_SPECIFIED][0] = array(
* 'value' => $this->randomName(32),
* 'format' => filter_default_format(),
* );
* @endcode
* - title: Random string.
* - comment: COMMENT_NODE_OPEN.
* - changed: REQUEST_TIME.
* - promote: NODE_NOT_PROMOTED.
* - log: Empty string.
* - status: NODE_PUBLISHED.
* - sticky: NODE_NOT_STICKY.
* - type: 'page'.
Angie Byron
committed
* - langcode: LANGUAGE_NOT_SPECIFIED. (If a 'langcode' key is provided in
Angie Byron
committed
* the array, this language code will also be used for a randomly
* generated body field for that language, and the body for
* LANGUAGE_NOT_SPECIFIED will remain empty.)
* - uid: The currently logged in user, or the user running test.
* - revision: 1. (Backwards-compatible binary flag indicating whether a
* new revision should be created; use 1 to specify a new revision.)
*
* @return Drupal\node\Node
* The created node entity.
*/
protected function drupalCreateNode(array $settings = array()) {
// Populate defaults array.
Dries Buytaert
committed
$settings += array(
'body' => array(LANGUAGE_NOT_SPECIFIED => array(array())),
'title' => $this->randomName(8),
Dries Buytaert
committed
'changed' => REQUEST_TIME,
Angie Byron
committed
'promote' => NODE_NOT_PROMOTED,
'revision' => 1,
'log' => '',
Angie Byron
committed
'status' => NODE_PUBLISHED,
'sticky' => NODE_NOT_STICKY,
'type' => 'page',
'langcode' => LANGUAGE_NOT_SPECIFIED,
);
Dries Buytaert
committed
// Add in comment settings for nodes.
if (module_exists('comment')) {
$settings += array(
'comment' => COMMENT_NODE_OPEN,
);
}
Dries Buytaert
committed
// Use the original node's created time for existing nodes.
if (isset($settings['created']) && !isset($settings['date'])) {
$settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O');
}
Dries Buytaert
committed
// If the node's user uid is not specified manually, use the currently
// logged in user if available, or else the user running the test.
if (!isset($settings['uid'])) {
if ($this->loggedInUser) {
$settings['uid'] = $this->loggedInUser->uid;
}
else {
global $user;
$settings['uid'] = $user->uid;
}
}
Dries Buytaert
committed
// Merge body field value and format separately.
$body = array(
'value' => $this->randomName(32),
'format' => filter_default_format(),
Dries Buytaert
committed
);
Angie Byron
committed
if (empty($settings['body'][$settings['langcode']])) {
$settings['body'][$settings['langcode']][0] = array();
}
$settings['body'][$settings['langcode']][0] += $body;
Dries Buytaert
committed
$node = entity_create('node', $settings);
Angie Byron
committed
if (!empty($settings['revision'])) {
$node->setNewRevision();
}
$node->save();
// Small hack to link revisions to our test user.
Dries Buytaert
committed
db_update('node_revision')
->fields(array('uid' => $node->uid))
->condition('vid', $node->vid)
->execute();
return $node;
}
/**
* Creates a custom content type based on default settings.
*
Dries Buytaert
committed
* @param $settings
* An array of settings to change from the defaults.
* Example: 'type' => 'foo'.
Dries Buytaert
committed
* @return
* Created content type.
*/
protected function drupalCreateContentType($settings = array()) {
// Find a non-existent random type name.
do {
Dries Buytaert
committed
$name = strtolower($this->randomName(8));
} while (node_type_load($name));
// Populate defaults array.
$defaults = array(
'type' => $name,
'name' => $name,
Dries Buytaert
committed
'base' => 'node_content',
'description' => '',
'help' => '',
'title_label' => 'Title',
'body_label' => 'Body',
'has_title' => 1,
'has_body' => 1,
);
// Imposed values for a custom type.
$forced = array(
'orig_type' => '',
'old_type' => '',
'module' => 'node',
'custom' => 1,
'modified' => 1,
'locked' => 0,
);
$type = $forced + $settings + $defaults;
$type = (object) $type;
Dries Buytaert
committed
$saved_type = node_type_save($type);
node_types_rebuild();
menu_router_rebuild();
node_add_body_field($type);
Dries Buytaert
committed
$this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type)));
Dries Buytaert
committed
// Reset permissions so that permissions for this content type are available.
$this->checkPermissions(array(), TRUE);
return $type;
}
Angie Byron
committed
/**
* Creates a block instance based on default settings.
*
* Note: Until this can be done programmatically, the active user account
* must have permission to administer blocks.
*
* @param string $plugin_id
* The plugin ID of the block type for this block instance.
Angie Byron
committed
* @param array $values
* (optional) An associative array of values for the block entity.
* Override the defaults by specifying the key and value in the array, for
Angie Byron
committed
* example:
* @code
* $this->drupalPlaceBlock('system_powered_by_block', array(
Angie Byron
committed
* 'label' => t('Hello, world!'),
Angie Byron
committed
* ));
* @endcode
* The following defaults are provided:
Angie Byron
committed
* - label: Random string.
Angie Byron
committed
* - machine_name: Random string.
* - region: 'sidebar_first'.
Angie Byron
committed
* - theme: The default theme.
* @param array $settings
* (optional) An associative array of plugin-specific settings.
Angie Byron
committed
*
Angie Byron
committed
* @return \Drupal\block\Plugin\Core\Entity\Block
* The block entity.
Angie Byron
committed
*
* @todo
* Add support for creating custom block instances.
*/
Angie Byron
committed
protected function drupalPlaceBlock($plugin_id, array $values = array(), array $settings = array()) {
$values += array(
'plugin' => $plugin_id,
'label' => $this->randomName(8),
Angie Byron
committed
'region' => 'sidebar_first',
Angie Byron
committed
'theme' => variable_get('theme_default', 'stark'),
'machine_name' => strtolower($this->randomName(8)),
'settings' => $settings,
Angie Byron
committed
);
Angie Byron
committed
// Build the ID out of the theme and machine_name.
$values['id'] = $values['theme'] . '.' . $values['machine_name'];
$block = entity_create('block', $values);
$block->save();
return $block;
Angie Byron
committed
}
/**
* Get a list files that can be used in tests.
*
Dries Buytaert
committed
* @param $type
* File type, possible values: 'binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'.
* @param $size
* File size in bytes to match. Please check the tests/files folder.
* @return
* List of files that match filter.
*/
protected function drupalGetTestFiles($type, $size = NULL) {
Dries Buytaert
committed
if (empty($this->generatedTestFiles)) {
// Generate binary test files.
$lines = array(64, 1024);
$count = 0;
foreach ($lines as $line) {
simpletest_generate_file('binary-' . $count++, 64, $line, 'binary');
}
// Generate text test files.
$lines = array(16, 256, 1024, 2048, 20480);
$count = 0;
foreach ($lines as $line) {
simpletest_generate_file('text-' . $count++, 64, $line);
}
// Copy other test files from simpletest.
$original = drupal_get_path('module', 'simpletest') . '/files';
$files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/');
foreach ($files as $file) {
file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files'));
Dries Buytaert
committed
}
Dries Buytaert
committed
$this->generatedTestFiles = TRUE;
}
$files = array();
// Make sure type is valid.
if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) {
Angie Byron
committed
$files = file_scan_directory('public://', '/' . $type . '\-.*/');
// If size is set then remove any files that are not of that size.
if ($size !== NULL) {
foreach ($files as $file) {
$stats = stat($file->uri);
if ($stats['size'] != $size) {
unset($files[$file->uri]);
}
}
}
}
usort($files, array($this, 'drupalCompareFiles'));
return $files;
}
/**
* Compare two files based on size and file name.
*/
protected function drupalCompareFiles($file1, $file2) {
$compare_size = filesize($file1->uri) - filesize($file2->uri);
if ($compare_size) {
// Sort by file size.
return $compare_size;
}
else {
// The files were the same size, so sort alphabetically.
return strnatcmp($file1->name, $file2->name);
}
}
/**
* Create a user with a given set of permissions.
*
* @param array $permissions
* Array of permission names to assign to user. Note that the user always
* has the default permissions derived from the "authenticated users" role.
*
* @return object|false
Dries Buytaert
committed
* A fully loaded user object with pass_raw property, or FALSE if account
* creation fails.
*/
protected function drupalCreateUser(array $permissions = array()) {
// Create a role with the given permission set, if any.
$rid = FALSE;
if ($permissions) {
$rid = $this->drupalCreateRole($permissions);
if (!$rid) {
return FALSE;
}
}
// Create a user assigned to that role.
$edit = array();
$edit['name'] = $this->randomName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['pass'] = user_password();
$edit['status'] = 1;
if ($rid) {
$edit['roles'] = array($rid => $rid);
}
$account = entity_create('user', $edit);
$account->save();
$this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
if (empty($account->uid)) {
return FALSE;
}
// Add the raw password so that we can log in as this user.
$account->pass_raw = $edit['pass'];
return $account;
}
/**
* Internal helper function; Create a role with specified permissions.
*
catch
committed
* @param array $permissions
Dries Buytaert
committed
* Array of permission names to assign to role.
catch
committed
* @param string $rid
* (optional) The role ID (machine name). Defaults to a random name.
* @param string $name
* (optional) The label for the role. Defaults to a random string.
*
* @return string
Dries Buytaert
committed
* Role ID of newly created role, or FALSE if role creation failed.
*/
catch
committed
protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NULL) {
// Generate a random, lowercase machine name if none was passed.
if (!isset($rid)) {
$rid = strtolower($this->randomName(8));
}
// Generate a random label.
if (!isset($name)) {
$name = $this->randomString(8);
Dries Buytaert
committed
}
Dries Buytaert
committed
// Check the all the permissions strings are valid.
Dries Buytaert
committed
if (!$this->checkPermissions($permissions)) {
return FALSE;
}
// Create new role.
Dries Buytaert
committed
$role = new stdClass();
catch
committed
$role->rid = $rid;
Dries Buytaert
committed
$role->name = $name;
catch
committed
$result = user_role_save($role);
$this->assertIdentical($result, SAVED_NEW, t('Created role ID @rid with name @name.', array(
'@name' => var_export($role->name, TRUE),
'@rid' => var_export($role->rid, TRUE),
)), t('Role'));
if ($result === SAVED_NEW) {
// Grant the specified permissions to the role, if any.
if (!empty($permissions)) {
user_role_grant_permissions($role->rid, $permissions);
$assigned_permissions = db_query('SELECT permission FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->rid))->fetchCol();
$missing_permissions = array_diff($permissions, $assigned_permissions);
if (!$missing_permissions) {
$this->pass(t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
}
else {
$this->fail(t('Failed to create permissions: @perms', array('@perms' => implode(', ', $missing_permissions))), t('Role'));
}
}
return $role->rid;
}
else {
return FALSE;
}
}
Dries Buytaert
committed
/**
* Check to make sure that the array of permissions are valid.
*
Dries Buytaert
committed
* @param $permissions
* Permissions to check.
* @param $reset
* Reset cached available permissions.
* @return
* TRUE or FALSE depending on whether the permissions are valid.
Dries Buytaert
committed
*/
protected function checkPermissions(array $permissions, $reset = FALSE) {
$available = &drupal_static(__FUNCTION__);
Dries Buytaert
committed
if (!isset($available) || $reset) {
$available = array_keys(module_invoke_all('permission'));
Dries Buytaert
committed
}
$valid = TRUE;
foreach ($permissions as $permission) {
if (!in_array($permission, $available)) {
$this->fail(t('Invalid permission %permission.', array('%permission' => $permission)), t('Role'));
$valid = FALSE;
}
}
return $valid;
}
/**
Dries Buytaert
committed
* Log in a user with the internal browser.
*
* If a user is already logged in, then the current user is logged out before
* logging in the specified user.
*
Dries Buytaert
committed
* Please note that neither the global $user nor the passed-in user object is
Dries Buytaert
committed
* populated with data of the logged in user. If you need full access to the
* user object after logging in, it must be updated manually. If you also need
* access to the plain-text password of the user (set by drupalCreateUser()),
Angie Byron
committed
* e.g. to log in the same user again, then it must be re-assigned manually.
Dries Buytaert
committed
* For example:
* @code
* // Create a user.
* $account = $this->drupalCreateUser(array());
* $this->drupalLogin($account);
* // Load real user object.
* $pass_raw = $account->pass_raw;
* $account = user_load($account->uid);
* $account->pass_raw = $pass_raw;
* @endcode
*
Dries Buytaert
committed
* @param $user
Angie Byron
committed
* User object representing the user to log in.
Dries Buytaert
committed
*
* @see drupalCreateUser()
*/
protected function drupalLogin($user) {
Dries Buytaert
committed
if ($this->loggedInUser) {
$this->drupalLogout();
}
$edit = array(
'name' => $user->name,
'pass' => $user->pass_raw
);
$this->drupalPost('user', $edit, t('Log in'));
Dries Buytaert
committed
// If a "log out" link appears on the page, it is almost certainly because
// the login was successful.
$pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $user->name)), t('User login'));
Dries Buytaert
committed
if ($pass) {
$this->loggedInUser = $user;
}
}
/**
* Generate a token for the currently logged in user.
*/
protected function drupalGetToken($value = '') {
$private_key = drupal_get_private_key();
Dries Buytaert
committed
return drupal_hmac_base64($value, $this->session_id . $private_key);
}
/*
* Logs a user out of the internal browser, then check the login page to confirm logout.
*/
protected function drupalLogout() {
Dries Buytaert
committed
// Make a request to the logout page, and redirect to the user page, the
// idea being if you were properly logged out you should be seeing a login
// screen.
Dries Buytaert
committed
$this->drupalGet('user/logout', array('query' => array('destination' => 'user')));
$this->assertResponse(200, t('User was logged out.'));
$pass = $this->assertField('name', t('Username field found.'), t('Logout'));
$pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
Dries Buytaert
committed
if ($pass) {
$this->loggedInUser = FALSE;
}
}
/**
* Sets up a Drupal site for running functional and integration tests.
*
* Generates a random database prefix and installs Drupal with the specified
catch
committed
* installation profile in Drupal\simpletest\WebTestBase::$profile into the
* prefixed database. Afterwards, installs any additional modules specified by
* the test.
*
* After installation all caches are flushed and several configuration values
* are reset to the values of the parent site executing the test, since the
* default values may be incompatible with the environment in which tests are
* being executed.
*
* @param ...
* List of modules to enable for the duration of the test. This can be
* either a single array or a variable number of string arguments.
*
catch
committed
* @see Drupal\simpletest\WebTestBase::prepareDatabasePrefix()
* @see Drupal\simpletest\WebTestBase::changeDatabasePrefix()
* @see Drupal\simpletest\WebTestBase::prepareEnvironment()
*/
protected function setUp() {
Dries Buytaert
committed
global $user, $conf;
// When running tests through the Simpletest UI (vs. on the command line),
// Simpletest's batch conflicts with the installer's batch. Batch API does
// not support the concept of nested batches (in which the nested is not
// progressive), so we need to temporarily pretend there was no batch.
// Backup the currently running Simpletest batch.
$this->originalBatch = batch_get();
// Create the database prefix for this test.
$this->prepareDatabasePrefix();
// Prepare the environment for running tests.
$this->prepareEnvironment();
if (!$this->setupEnvironment) {
return FALSE;
}
// 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;
}
Dries Buytaert
committed
// Set the 'simpletest_parent_profile' variable to add the parent profile's
// search path to the child site's search paths.
// @see drupal_system_listing()
$conf['simpletest_parent_profile'] = $this->originalProfile;
// Set installer parameters.
// @see install.php, install.core.inc
Dries Buytaert
committed
$connection_info = Database::getConnectionInfo();
$this->root_user = (object) array(
'name' => 'admin',
'mail' => 'admin@example.com',
'pass_raw' => $this->randomName(),
);
$settings = array(
'interactive' => FALSE,
'parameters' => array(
'profile' => $this->profile,
'langcode' => 'en',
),
'forms' => array(
Dries Buytaert
committed
'install_settings_form' => $connection_info['default'],
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
'install_configure_form' => array(
'site_name' => 'Drupal',
'site_mail' => 'simpletest@example.com',
'account' => array(
'name' => $this->root_user->name,
'mail' => $this->root_user->mail,
'pass' => array(
'pass1' => $this->root_user->pass_raw,
'pass2' => $this->root_user->pass_raw,
),
),
// form_type_checkboxes_value() requires NULL instead of FALSE values
// for programmatic form submissions to disable a checkbox.
'update_status_module' => array(
1 => NULL,
2 => NULL,
),
),
),
);
// Replace the global $user session with an anonymous user to resemble a
// regular installation.
$user = drupal_anonymous_user();
// Reset the static batch to remove Simpletest's batch operations.
$batch = &batch_get();
$batch = array();
catch
committed
$variables = array(
'file_public_path' => $this->public_files_directory,
'file_private_path' => $this->private_files_directory,
'file_temporary_path' => $this->temp_files_directory,
'locale_translate_file_directory' => $this->translation_files_directory,
);
foreach ($variables as $name => $value) {
$GLOBALS['conf'][$name] = $value;
}
// Execute the non-interactive installer.
require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
install_drupal($settings);
$this->rebuildContainer();
catch
committed
foreach ($variables as $name => $value) {
variable_set($name, $value);
}
// Restore the original Simpletest batch.
$batch = &batch_get();
$batch = $this->originalBatch;
// Revert install_begin_request() cache service overrides.
unset($conf['cache_classes']);
Dries Buytaert
committed
Dries Buytaert
committed
// Set path variables.
// Set 'parent_profile' of simpletest to add the parent profile's
// search path to the child site's search paths.
// @see drupal_system_listing()
config('simpletest.settings')->set('parent_profile', $this->originalProfile)->save();
// Collect modules to install.
$class = get_class($this);
$modules = array();
while ($class) {
if (property_exists($class, 'modules')) {
$modules = array_merge($modules, $class::$modules);
}
$class = get_parent_class($class);
}
Angie Byron
committed
if ($modules) {
Angie Byron
committed
$success = module_enable($modules, TRUE);
$this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $modules))));
$this->rebuildContainer();
Dries Buytaert
committed
}
Dries Buytaert
committed
// Reset/rebuild all data structures after enabling the modules.
$this->resetAll();
Angie Byron
committed
// Use the test mail class instead of the default mail handler class.
variable_set('mail_system', array('default-system' => 'Drupal\Core\Mail\VariableLog'));
Dries Buytaert
committed
drupal_set_time_limit($this->timeLimit);
Dries Buytaert
committed
// Temporary fix so that when running from run-tests.sh we don't get an
// empty current path which would indicate we're on the home page.
$path = current_path();
if (empty($path)) {
_current_path('run-tests');
}
$this->setup = TRUE;
}
/**
* Reset all data structures after having enabled new modules.
*
catch
committed
* This method is called by Drupal\simpletest\WebTestBase::setUp() after enabling
* the requested modules. It must be called again when additional modules
* are enabled later.
*/
protected function resetAll() {
// Clear all database and static caches and rebuild data structures.
drupal_flush_all_caches();
// Reload global $conf array and permissions.
$this->refreshVariables();
$this->checkPermissions(array(), TRUE);
}
Dries Buytaert
committed
/**
* Refresh the in-memory set of variables. Useful after a page request is made
* that changes a variable in a different thread.
*
* In other words calling a settings page with $this->drupalPost() with a changed
* value would update a variable to reflect that change, but in the thread that
* made the call (thread running the test) the changed variable would not be
* picked up.
*
* This method clears the variables cache and loads a fresh copy from the database
* to ensure that the most up-to-date set of variables is loaded.
*/
protected function refreshVariables() {
Dries Buytaert
committed
global $conf;
Dries Buytaert
committed
cache('bootstrap')->delete('variables');
$conf = variable_initialize();
drupal_container()->get('config.factory')->reset();
Dries Buytaert
committed
}
/**
* Delete created files and temporary files directory, delete the tables created by setUp(),
* and reset the database prefix.
*/
protected function tearDown() {
Angie Byron
committed
// Destroy the testing kernel.
if (isset($this->kernel)) {
$this->kernel->shutdown();
}
parent::tearDown();
Dries Buytaert
committed
Dries Buytaert
committed
// Ensure that internal logged in variable and cURL options are reset.
$this->loggedInUser = FALSE;
$this->additionalCurlOptions = array();
Dries Buytaert
committed
Dries Buytaert
committed
// Close the CURL handler.
$this->curlClose();
}
/**
Dries Buytaert
committed
* Initializes the cURL connection.
*
Angie Byron
committed
* If the simpletest_httpauth_credentials variable is set, this function will
* add HTTP authentication headers. This is necessary for testing sites that
* are protected by login credentials from public access.
* See the description of $curl_options for other options.
*/
Dries Buytaert
committed
protected function curlInitialize() {
Dries Buytaert
committed
global $base_url;
Dries Buytaert
committed
if (!isset($this->curlHandle)) {
$this->curlHandle = curl_init();
Dries Buytaert
committed
// Some versions/configurations of cURL break on a NULL cookie jar, so
// supply a real file.
if (empty($this->cookieFile)) {
$this->cookieFile = $this->public_files_directory . '/cookie.jar';
}
Dries Buytaert
committed
$curl_options = array(
CURLOPT_COOKIEJAR => $this->cookieFile,
CURLOPT_URL => $base_url,
Dries Buytaert
committed
CURLOPT_FOLLOWLOCATION => FALSE,
CURLOPT_RETURNTRANSFER => TRUE,
Angie Byron
committed
CURLOPT_SSL_VERIFYPEER => FALSE, // Required to make the tests run on HTTPS.
CURLOPT_SSL_VERIFYHOST => FALSE, // Required to make the tests run on HTTPS.
Dries Buytaert
committed
CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
Dries Buytaert
committed
CURLOPT_USERAGENT => $this->databasePrefix,
);
Angie Byron
committed
if (isset($this->httpauth_credentials)) {
Dries Buytaert
committed
$curl_options[CURLOPT_HTTPAUTH] = $this->httpauth_method;
Angie Byron
committed
$curl_options[CURLOPT_USERPWD] = $this->httpauth_credentials;
}
Dries Buytaert
committed
// curl_setopt_array() returns FALSE if any of the specified options
// cannot be set, and stops processing any further options.
$result = curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
if (!$result) {
throw new \UnexpectedValueException('One or more cURL options could not be set.');
}
// By default, the child session name should be the same as the parent.
$this->session_name = session_name();
}
Dries Buytaert
committed
// We set the user agent header on each request so as to use the current
// time and a new uniqid.
Dries Buytaert
committed
if (preg_match('/simpletest\d+/', $this->databasePrefix, $matches)) {
Dries Buytaert
committed
curl_setopt($this->curlHandle, CURLOPT_USERAGENT, drupal_generate_test_ua($matches[0]));
}
}
/**
Angie Byron
committed
* Initializes and executes a cURL request.
*
* @param $curl_options
Angie Byron
committed
* An associative array of cURL options to set, where the keys are constants
* defined by the cURL library. For a list of valid options, see
* http://www.php.net/manual/function.curl-setopt.php
Dries Buytaert
committed
* @param $redirect
Angie Byron
committed
* FALSE if this is an initial request, TRUE if this request is the result
* of a redirect.
*
Dries Buytaert
committed
* @return
Angie Byron
committed
* The content returned from the call to curl_exec().
*
* @see curlInitialize()
*/
Dries Buytaert
committed
protected function curlExec($curl_options, $redirect = FALSE) {
Dries Buytaert
committed
$this->curlInitialize();
Dries Buytaert
committed
// cURL incorrectly handles URLs with a fragment by including the
// fragment in the request to the server, causing some web servers
// to reject the request citing "400 - Bad Request". To prevent
// this, we strip the fragment from the request.
// TODO: Remove this for Drupal 8, since fixed in curl 7.20.0.
if (!empty($curl_options[CURLOPT_URL]) && strpos($curl_options[CURLOPT_URL], '#')) {
$original_url = $curl_options[CURLOPT_URL];
$curl_options[CURLOPT_URL] = strtok($curl_options[CURLOPT_URL], '#');
}
$url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL];
Dries Buytaert
committed
Dries Buytaert
committed
if (!empty($curl_options[CURLOPT_POST])) {
// This is a fix for the Curl library to prevent Expect: 100-continue
// headers in POST requests, that may cause unexpected HTTP response
// codes from some webservers (like lighttpd that returns a 417 error
// code). It is done by setting an empty "Expect" header field that is
// not overwritten by Curl.
$curl_options[CURLOPT_HTTPHEADER][] = 'Expect:';
}
curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
Dries Buytaert
committed
if (!$redirect) {
// Reset headers, the session ID and the redirect counter.
$this->session_id = NULL;
$this->headers = array();
$this->redirect_count = 0;
}
$content = curl_exec($this->curlHandle);
$status = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
// cURL incorrectly handles URLs with fragments, so instead of
// letting cURL handle redirects we take of them ourselves to
// to prevent fragments being sent to the web server as part
// of the request.
// TODO: Remove this for Drupal 8, since fixed in curl 7.20.0.
if (in_array($status, array(300, 301, 302, 303, 305, 307)) && $this->redirect_count < $this->maximumRedirects) {
Dries Buytaert
committed
if ($this->drupalGetHeader('location')) {
$this->redirect_count++;
$curl_options = array();
$curl_options[CURLOPT_URL] = $this->drupalGetHeader('location');
$curl_options[CURLOPT_HTTPGET] = TRUE;
return $this->curlExec($curl_options, TRUE);
}
}
Dries Buytaert
committed
$this->drupalSetContent($content, isset($original_url) ? $original_url : curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL));
$message_vars = array(
'!method' => !empty($curl_options[CURLOPT_NOBODY]) ? 'HEAD' : (empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST'),
Dries Buytaert
committed
'@url' => isset($original_url) ? $original_url : $url,