Newer
Older
<?php
/**
catch
committed
* @file
Jennifer Hodgdon
committed
* Definition of \Drupal\simpletest\WebTestBase.
*/
catch
committed
namespace Drupal\simpletest;
Angie Byron
committed
Angie Byron
committed
use Drupal\Component\Utility\Crypt;
Dries Buytaert
committed
use Drupal\Component\Utility\NestedArray;
Jennifer Hodgdon
committed
use Drupal\Component\Utility\String;
Angie Byron
committed
use Drupal\Core\DrupalKernel;
catch
committed
use Drupal\Core\Database\Database;
use Drupal\Core\Database\ConnectionNotDefinedException;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\UserSession;
use Drupal\Core\StreamWrapper\PublicStream;
Dries Buytaert
committed
use Drupal\Core\Datetime\DrupalDateTime;
use Symfony\Component\HttpFoundation\Request;
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
/**
* 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;
/**
* Indicates that headers should be dumped if verbose output is enabled.
*
* Headers are dumped to verbose by drupalGet(), drupalHead(), and
* drupalPostForm().
*
* @var bool
*/
protected $dumpHeaders = FALSE;
Dries Buytaert
committed
/**
* The content of the page currently loaded in the internal browser.
*
* @var string
*/
protected $content;
/**
Jennifer Hodgdon
committed
* The plain-text content of the currently-loaded page.
Dries Buytaert
committed
*
* @var string
*/
protected $plainTextContent;
Dries Buytaert
committed
/**
* The value of drupalSettings for the currently-loaded page.
Jennifer Hodgdon
committed
*
* drupalSettings refers to the drupalSettings JavaScript variable.
Dries Buytaert
committed
*
* @var Array
*/
protected $drupalSettings;
Dries Buytaert
committed
/**
* The parsed version of the page.
*
Angie Byron
committed
* @var \SimpleXMLElement
Dries Buytaert
committed
*/
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.
*
Jennifer Hodgdon
committed
* \Drupal\simpletest\WebTestBase itself never sets this but always obeys what
* is set.
Dries Buytaert
committed
*/
protected $additionalCurlOptions = array();
/**
Jennifer Hodgdon
committed
* The original user, before it was changed to a clean uid = 1 for testing.
Dries Buytaert
committed
*
* @var object
*/
protected $originalUser = NULL;
/**
Jennifer Hodgdon
committed
* The original shutdown handlers array, before it was cleaned for testing.
*
* @var array
*/
protected $originalShutdownCallbacks = array();
Dries Buytaert
committed
/**
Jennifer Hodgdon
committed
* HTTP authentication method.
Dries Buytaert
committed
*/
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;
catch
committed
/**
* Cookies to set on curl requests.
*
* @var array
*/
protected $curlCookies = array();
/**
* An array of custom translations suitable for drupal_rewrite_settings().
*
* @var array
*/
protected $customTranslations;
Dries Buytaert
committed
/**
Jennifer Hodgdon
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.
*
Angie Byron
committed
* @return \Drupal\Core\Entity\EntityInterface
* A node entity matching $title.
*/
Dries Buytaert
committed
function drupalGetNodeByTitle($title, $reset = FALSE) {
if ($reset) {
\Drupal::entityManager()->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
* @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
Angie Byron
committed
* $settings['body'][0] = array(
Angie Byron
committed
* 'value' => $this->randomName(32),
* 'format' => filter_default_format(),
* );
* @endcode
* - title: Random string.
* - comment: COMMENT_OPEN.
Angie Byron
committed
* - changed: REQUEST_TIME.
* - promote: NODE_NOT_PROMOTED.
* - log: Empty string.
* - status: NODE_PUBLISHED.
* - sticky: NODE_NOT_STICKY.
* - type: 'page'.
* - langcode: Language::LANGCODE_NOT_SPECIFIED.
Angie Byron
committed
* - 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\Entity\Node
Angie Byron
committed
* The created node entity.
*/
protected function drupalCreateNode(array $settings = array()) {
// Populate defaults array.
Dries Buytaert
committed
$settings += array(
Angie Byron
committed
'body' => 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::LANGCODE_NOT_SPECIFIED,
);
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) {
Dries Buytaert
committed
$settings['uid'] = $this->loggedInUser->id();
Dries Buytaert
committed
}
else {
$user = \Drupal::currentUser() ?: $GLOBALS['user'];
Dries Buytaert
committed
$settings['uid'] = $user->id();
Dries Buytaert
committed
}
}
Dries Buytaert
committed
// Merge body field value and format separately.
Angie Byron
committed
$settings['body'][0] += array(
Dries Buytaert
committed
'value' => $this->randomName(32),
'format' => filter_default_format(),
Dries Buytaert
committed
);
$node = entity_create('node', $settings);
Angie Byron
committed
if (!empty($settings['revision'])) {
$node->setNewRevision();
}
$node->save();
return $node;
}
/**
* Creates a custom content type based on default settings.
*
* @param array $values
* An array of settings to change from the defaults.
* Example: 'type' => 'foo'.
*
* @return \Drupal\node\Entity\NodeType
Dries Buytaert
committed
* Created content type.
*/
protected function drupalCreateContentType(array $values = array()) {
// Find a non-existent random type name.
if (!isset($values['type'])) {
do {
$id = strtolower($this->randomName(8));
} while (node_type_load($id));
}
else {
$id = $values['type'];
}
$values += array(
'type' => $id,
'name' => $id,
);
$type = entity_create('node_type', $values);
$status = $type->save();
menu_router_rebuild();
$this->assertEqual($status, SAVED_NEW, t('Created content type %type.', array('%type' => $type->id())));
Jennifer Hodgdon
committed
// Reset permissions so that permissions for this content type are
// available.
Dries Buytaert
committed
$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.
* @param array $settings
* (optional) An associative array of settings for the block entity.
Angie Byron
committed
* 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.
* - id: Random string.
Angie Byron
committed
* - region: 'sidebar_first'.
Angie Byron
committed
* - theme: The default theme.
* - visibility: Empty array.
Angie Byron
committed
*
* @return \Drupal\block\Entity\Block
Angie Byron
committed
* The block entity.
Angie Byron
committed
*
* @todo
* Add support for creating custom block instances.
*/
protected function drupalPlaceBlock($plugin_id, array $settings = array()) {
$settings += array(
Angie Byron
committed
'plugin' => $plugin_id,
Angie Byron
committed
'region' => 'sidebar_first',
'id' => strtolower($this->randomName(8)),
Angie Byron
committed
'theme' => \Drupal::config('system.theme')->get('default'),
'label' => $this->randomName(8),
'visibility' => array(),
Alex Pott
committed
'weight' => 0,
Angie Byron
committed
);
foreach (array('region', 'id', 'theme', 'plugin', 'visibility', 'weight') as $key) {
$values[$key] = $settings[$key];
Alex Pott
committed
// Remove extra values that do not belong in the settings array.
unset($settings[$key]);
}
$values['settings'] = $settings;
Angie Byron
committed
$block = entity_create('block', $values);
$block->save();
return $block;
Angie Byron
committed
}
/**
Jennifer Hodgdon
committed
* Gets a list files that can be used in tests.
*
Dries Buytaert
committed
* @param $type
Jennifer Hodgdon
committed
* File type, possible values: 'binary', 'html', 'image', 'javascript',
* 'php', 'sql', 'text'.
Dries Buytaert
committed
* @param $size
* File size in bytes to match. Please check the tests/files folder.
*
Dries Buytaert
committed
* @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, PublicStream::basePath());
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.
* @param string $name
Angie Byron
committed
* The user name.
*
* @return \Drupal\user\Entity\User|false
Dries Buytaert
committed
* A fully loaded user object with pass_raw property, or FALSE if account
* creation fails.
*/
Angie Byron
committed
protected function drupalCreateUser(array $permissions = array(), $name = NULL) {
// 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();
Angie Byron
committed
$edit['name'] = !empty($name) ? $name : $this->randomName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['pass'] = user_password();
$edit['status'] = 1;
if ($rid) {
Alex Pott
committed
$edit['roles'] = array($rid);
}
$account = entity_create('user', $edit);
$account->save();
Dries Buytaert
committed
$this->assertTrue($account->id(), String::format('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), 'User login');
if (!$account->id()) {
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.
* @param integer $weight
* (optional) The weight for the role. Defaults NULL so that entity_create()
* sets the weight to maximum + 1.
catch
committed
*
* @return string
Dries Buytaert
committed
* Role ID of newly created role, or FALSE if role creation failed.
*/
protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NULL, $weight = NULL) {
catch
committed
// 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)) {
// In the role UI role names are trimmed and random string can start or
// end with a space.
$name = trim($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.
$role = entity_create('user_role', array(
'id' => $rid,
'label' => $name,
));
if (!is_null($weight)) {
$role->set('weight', $weight);
}
$result = $role->save();
catch
committed
$this->assertIdentical($result, SAVED_NEW, t('Created role ID @rid with name @name.', array(
'@name' => var_export($role->label(), TRUE),
'@rid' => var_export($role->id(), TRUE),
catch
committed
)), t('Role'));
if ($result === SAVED_NEW) {
// Grant the specified permissions to the role, if any.
if (!empty($permissions)) {
user_role_grant_permissions($role->id(), $permissions);
$assigned_permissions = entity_load('user_role', $role->id())->permissions;
catch
committed
$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->id();
}
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.
*
Dries Buytaert
committed
* @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;
Dries Buytaert
committed
* $account = user_load($account->id());
Dries Buytaert
committed
* $account->pass_raw = $pass_raw;
* @endcode
*
* @param \Drupal\Core\Session\AccountInterface $account
Angie Byron
committed
* User object representing the user to log in.
Dries Buytaert
committed
*
* @see drupalCreateUser()
*/
protected function drupalLogin(AccountInterface $account) {
Dries Buytaert
committed
if ($this->loggedInUser) {
$this->drupalLogout();
}
$edit = array(
'name' => $account->getUsername(),
Angie Byron
committed
'pass' => $account->pass_raw
);
$this->drupalPostForm('user', $edit, t('Log in'));
Dries Buytaert
committed
// @see WebTestBase::drupalUserIsLoggedIn()
if (isset($this->session_id)) {
Angie Byron
committed
$account->session_id = $this->session_id;
Dries Buytaert
committed
}
$pass = $this->assert($this->drupalUserIsLoggedIn($account), format_string('User %name successfully logged in.', array('%name' => $account->getUsername())), 'User login');
Dries Buytaert
committed
if ($pass) {
Angie Byron
committed
$this->loggedInUser = $account;
$this->container->set('current_user', $account);
Dries Buytaert
committed
}
}
Dries Buytaert
committed
/**
* Returns whether a given user account is logged in.
*
Alex Pott
committed
* @param \Drupal\user\UserInterface $account
Dries Buytaert
committed
* The user account object to check.
*/
protected function drupalUserIsLoggedIn($account) {
if (!isset($account->session_id)) {
return FALSE;
}
// @see _drupal_session_read()
return (bool) db_query("SELECT sid FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $account->session_id))->fetchField();
}
/**
* Generate a token for the currently logged in user.
*/
protected function drupalGetToken($value = '') {
$private_key = drupal_get_private_key();
Angie Byron
committed
return Crypt::hmacBase64($value, $this->session_id . $private_key);
}
Jennifer Hodgdon
committed
/**
* Logs a user out of the internal browser and confirms.
*
* Confirms logout by checking the login page.
*/
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')));
Jennifer Hodgdon
committed
$this->assertResponse(200, 'User was logged out.');
$pass = $this->assertField('name', 'Username field found.', 'Logout');
$pass = $pass && $this->assertField('pass', 'Password field found.', 'Logout');
Dries Buytaert
committed
if ($pass) {
Dries Buytaert
committed
// @see WebTestBase::drupalUserIsLoggedIn()
unset($this->loggedInUser->session_id);
Dries Buytaert
committed
$this->loggedInUser = FALSE;
$this->container->set('current_user', drupal_anonymous_user());
Dries Buytaert
committed
}
}
/**
* Sets up a Drupal site for running functional and integration tests.
*
* Generates a random database prefix and installs Drupal with the specified
Jennifer Hodgdon
committed
* installation profile in \Drupal\simpletest\WebTestBase::$profile into the
catch
committed
* 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.
*
Jennifer Hodgdon
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
Jennifer Hodgdon
committed
// 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;
Alex Pott
committed
// Define information about the user 1 account.
$this->root_user = new UserSession(array(
'uid' => 1,
'name' => 'admin',
'mail' => 'admin@example.com',
'pass_raw' => $this->randomName(),
// Reset the static batch to remove Simpletest's batch operations.
$batch = &batch_get();
$batch = array();
$variable_groups = array(
'system.file' => array(
'path.private' => $this->private_files_directory,
'path.temporary' => $this->temp_files_directory,
),
'locale.settings' => array(
'translation.path' => $this->translation_files_directory,
),
catch
committed
);
foreach ($variable_groups as $config_base => $variables) {
foreach ($variables as $name => $value) {
NestedArray::setValue($GLOBALS['conf'], array_merge(array($config_base), explode('.', $name)), $value);
}
catch
committed
}
$this->settingsSet('file_public_path', $this->public_files_directory);
// Execute the non-interactive installer.
require_once DRUPAL_ROOT . '/core/includes/install.core.inc';
$this->settingsSet('cache', array('default' => 'cache.backend.memory'));
Alex Pott
committed
$parameters = $this->installParameters();
install_drupal($parameters);
Dries Buytaert
committed
// Set the install_profile so that web requests to the requests to the child
// site have the correct profile.
$settings = array(
'settings' => array(
'install_profile' => (object) array(
'value' => $this->profile,
'required' => TRUE,
),
),
);
$this->writeSettings($settings);
// Override install profile in Settings to so the correct profile is used by
// tests.
$this->settingsSet('install_profile', $this->profile);
$this->settingsSet('cache', array());
$this->rebuildContainer();
// Restore the original Simpletest batch.
$batch = &batch_get();
$batch = $this->originalBatch;
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()
Angie Byron
committed
\Drupal::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
$modules = array_unique($modules);
Dries Buytaert
committed
$success = \Drupal::moduleHandler()->install($modules, TRUE);
Angie Byron
committed
$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();
// Now make sure that the file path configurations are saved. This is done
// after we install the modules to override default values.
foreach ($variable_groups as $config_base => $variables) {
Angie Byron
committed
$config = \Drupal::config($config_base);
foreach ($variables as $name => $value) {
$config->set($name, $value);
}
$config->save();
}
Angie Byron
committed
// Use the test mail class instead of the default mail handler class.
Angie Byron
committed
\Drupal::config('system.mail')->set('interface.default', 'Drupal\Core\Mail\VariableLog')->save();
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;
}
Alex Pott
committed
/**
* Returns the parameters that will be used when Simpletest installs Drupal.
*
* @see install_drupal()
* @see install_state_defaults()
*/
protected function installParameters() {
$connection_info = Database::getConnectionInfo();
$parameters = array(
'interactive' => FALSE,
'parameters' => array(
'profile' => $this->profile,
'langcode' => 'en',
),
'forms' => array(
'install_settings_form' => $connection_info['default'],
'install_configure_form' => array(
'site_name' => 'Drupal',
'site_mail' => 'simpletest@example.com',
'account' => array(
'name' => $this->root_user->name,
'mail' => $this->root_user->getEmail(),
Alex Pott
committed
'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,
),
),
),
);
return $parameters;
}
Angie Byron
committed
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
/**
* Writes a test-specific settings.php file for the child site.
*
* The child site loads this after the parent site's settings.php, so settings
* here override those.
*
* @param $settings An array of settings to write out, in the format expected
* by drupal_rewrite_settings().
*
* @see _drupal_load_test_overrides()
* @see drupal_rewrite_settings()
*/
protected function writeSettings($settings) {
// drupal_rewrite_settings() sets the in-memory global variables in addition
// to writing the file. We'll want to restore the original globals.
foreach (array_keys($settings) as $variable_name) {
$original_globals[$variable_name] = isset($GLOBALS[$variable_name]) ? $GLOBALS[$variable_name] : NULL;
}
include_once DRUPAL_ROOT . '/core/includes/install.inc';
$filename = $this->public_files_directory . '/settings.php';
file_put_contents($filename, "<?php\n");
drupal_rewrite_settings($settings, $filename);
// Restore the original globals.
foreach ($original_globals as $variable_name => $value) {
$GLOBALS[$variable_name] = $value;
}
}
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
/**
* Sets custom translations to the settings object and queues them to writing.
*
* In order for those custom translations to persist (being written in test
* site's settings.php) make sure to also call self::writeCustomTranslations()
*
* @param string $langcode
* The langcode to add translations for.
* @param array $values
* Array of values containing the untranslated string and its translation.
* For example:
* @code
* array(
* '' => array('Sunday' => 'domingo'),
* 'Long month name' => array('March' => 'marzo'),
* );
* @endcode
*/
protected function addCustomTranslations($langcode, array $values) {
$this->settingsSet('locale_custom_strings_' . $langcode, $values);
foreach ($values as $key => $translations) {
foreach ($translations as $label => $value) {
$this->customTranslations['locale_custom_strings_' . $langcode][$key][$label] = (object) array(
'value' => $value,
'required' => TRUE,
);
}
}
}
/**
* Writes custom translations to test site's settings.php.
*/
protected function writeCustomTranslations() {
$this->writeSettings(array('settings' => $this->customTranslations));
$this->customTranslations = array();
}
/**
Jennifer Hodgdon
committed
* Overrides \Drupal\simpletest\TestBase::rebuildContainer().
*/
protected function rebuildContainer() {
parent::rebuildContainer();
// Make sure the url generator has a request object, otherwise calls to
// $this->drupalGet() will fail.
$this->prepareRequestForGenerator();
}
Jennifer Hodgdon
committed
* Resets all data structures after having enabled new modules.
Jennifer Hodgdon
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();
Alex Pott
committed
$this->container = \Drupal::getContainer();
// Reload global $conf array and permissions.
$this->refreshVariables();
$this->checkPermissions(array(), TRUE);
}
Dries Buytaert
committed
/**
Jennifer Hodgdon
committed
* Refreshes the in-memory set of variables.
Dries Buytaert
committed
*
Jennifer Hodgdon
committed
* Useful after a page request is made that changes a variable in a different
* thread.
Dries Buytaert
committed
*
* In other words calling a settings page with $this->drupalPostForm() with a
Jennifer Hodgdon
committed
* 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.
*