Commit db460fe7 authored by catch's avatar catch

Issue #2171683 by sun, alexpott, tstoeckler, larowlan: Remove all Simpletest...

Issue #2171683 by sun, alexpott, tstoeckler, larowlan: Remove all Simpletest overrides and rely on native multi-site functionality instead.
parent 9c88b1af
This diff is collapsed.
......@@ -3071,14 +3071,6 @@ function _drupal_bootstrap_code() {
// Make sure all stream wrappers are registered.
file_get_stream_wrappers();
// Now that stream wrappers are registered, log fatal errors from a simpletest
// child site to a test specific file directory.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site'])) {
ini_set('log_errors', 1);
ini_set('error_log', 'public://error.log');
}
// Set the allowed protocols once we have the config available.
$allowed_protocols = \Drupal::config('system.filter')->get('protocols');
if (!isset($allowed_protocols)) {
......
......@@ -141,8 +141,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
// When running inside the testing framework, we relay the errors
// to the tested site by the way of HTTP headers.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
// $number does not use drupal_static as it should not be reset
// as it uniquely identifies each PHP error.
static $number = 0;
......
......@@ -276,19 +276,18 @@ function install_begin_request(&$install_state) {
// which will be used for installing Drupal.
conf_path(FALSE);
drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
// If the hash salt leaks, it becomes possible to forge a valid testing user
// agent, install a new copy of Drupal, and take over the original site. To
// avoid this yet allow for automated testing of the installer, make sure
// there is also a special test-specific settings.php overriding conf_path().
// _drupal_load_test_overrides() sets the simpletest_conf_path in-memory
// setting in this case.
if ($install_state['interactive'] && drupal_valid_test_ua() && !settings()->get('simpletest_conf_path')) {
// agent, install a new copy of Drupal, and take over the original site.
// The user agent header is used to pass a database prefix in the request when
// running tests. However, for security reasons, it is imperative that no
// installation be permitted using such a prefix.
if ($install_state['interactive'] && strpos($request->server->get('HTTP_USER_AGENT'), 'simpletest') !== FALSE && !drupal_valid_test_ua()) {
header($request->server->get('SERVER_PROTOCOL') . ' 403 Forbidden');
exit;
}
drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
// Ensure that procedural dependencies are loaded as early as possible,
// since the error/exception handlers depend on them.
require_once __DIR__ . '/../modules/system/system.install';
......@@ -1080,7 +1079,6 @@ function install_verify_database_settings() {
global $databases;
if (!empty($databases)) {
$database = $databases['default']['default'];
drupal_static_reset('conf_path');
$settings_file = './' . conf_path(FALSE) . '/settings.php';
$errors = install_database_errors($database, $settings_file);
if (empty($errors)) {
......@@ -1103,21 +1101,43 @@ function install_verify_database_settings() {
function install_settings_form($form, &$form_state, &$install_state) {
global $databases;
drupal_static_reset('conf_path');
$conf_path = './' . conf_path(FALSE);
$settings_file = $conf_path . '/settings.php';
$database = isset($databases['default']['default']) ? $databases['default']['default'] : array();
drupal_set_title(t('Database configuration'));
$drivers = drupal_get_database_types();
$drivers_keys = array_keys($drivers);
// If database connection settings have been prepared in settings.php already,
// then the existing values need to be taken over.
// Note: The installer even executes this form if there is a valid database
// connection already, since the submit handler of this form is responsible
// for writing all $settings to settings.php (not limited to $databases).
if (isset($databases['default']['default'])) {
$default_driver = $databases['default']['default']['driver'];
$default_options = $databases['default']['default'];
}
// Otherwise, use the database connection settings from the form input.
// For a non-interactive installation, this is derived from the original
// $settings array passed into install_drupal().
elseif (isset($form_state['input']['driver'])) {
$default_driver = $form_state['input']['driver'];
$default_options = $form_state['input'][$default_driver];
}
// If there is no database information at all yet, just suggest the first
// available driver as default value, so that its settings form is made
// visible via #states when JavaScript is enabled (see below).
else {
$default_driver = current($drivers_keys);
$default_options = array();
}
$form['driver'] = array(
'#type' => 'radios',
'#title' => t('Database type'),
'#required' => TRUE,
'#default_value' => !empty($database['driver']) ? $database['driver'] : current($drivers_keys),
'#default_value' => $default_driver,
);
if (count($drivers) == 1) {
$form['driver']['#disabled'] = TRUE;
......@@ -1127,7 +1147,7 @@ function install_settings_form($form, &$form_state, &$install_state) {
foreach ($drivers as $key => $driver) {
$form['driver']['#options'][$key] = $driver->name();
$form['settings'][$key] = $driver->getFormOptions($database);
$form['settings'][$key] = $driver->getFormOptions($default_options);
$form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . t('@driver_name settings', array('@driver_name' => $driver->name())) . '</h2>';
$form['settings'][$key]['#type'] = 'container';
$form['settings'][$key]['#tree'] = TRUE;
......@@ -1146,7 +1166,7 @@ function install_settings_form($form, &$form_state, &$install_state) {
'#button_type' => 'primary',
'#limit_validation_errors' => array(
array('driver'),
array(isset($form_state['input']['driver']) ? $form_state['input']['driver'] : current($drivers_keys)),
array($default_driver),
),
'#submit' => array('install_settings_form_submit'),
);
......@@ -1165,12 +1185,6 @@ function install_settings_form($form, &$form_state, &$install_state) {
function install_settings_form_validate($form, &$form_state) {
$driver = $form_state['values']['driver'];
$database = $form_state['values'][$driver];
// When testing the interactive installer, copy the database password and
// the test prefix.
if ($test_prefix = drupal_valid_test_ua()) {
$database['prefix'] = $test_prefix;
$database['password'] = $GLOBALS['databases']['default']['default']['password'];
}
$drivers = drupal_get_database_types();
$reflection = new \ReflectionClass($drivers[$driver]);
$install_namespace = $reflection->getNamespaceName();
......@@ -1237,33 +1251,14 @@ function install_settings_form_submit($form, &$form_state) {
// Update global settings array and save.
$settings = array();
$database = $form_state['storage']['database'];
// Ideally, there is no difference between the code executed by the
// automated test browser and an ordinary browser. However, the database
// settings need a different format and also need to skip the password
// when testing. The hash salt also needs to be skipped because the original
// salt is used to verify the validity of the automated test browser.
// Because of these, there's a little difference in the code following but
// it is small and self-contained.
if ($test_prefix = drupal_valid_test_ua()) {
foreach ($form_state['storage']['database'] as $k => $v) {
if ($k != 'password') {
$settings['databases']['default']['default'][$k] = (object) array(
'value' => $v,
'required' => TRUE,
);
}
}
}
else {
$settings['databases']['default']['default'] = (object) array(
'value' => $database,
'required' => TRUE,
);
$settings['drupal_hash_salt'] = (object) array(
'value' => Crypt::randomStringHashed(55),
'required' => TRUE,
);
}
$settings['databases']['default']['default'] = (object) array(
'value' => $database,
'required' => TRUE,
);
$settings['drupal_hash_salt'] = (object) array(
'value' => Crypt::randomStringHashed(55),
'required' => TRUE,
);
// Remember the profile which was used.
$settings['settings'] = array(
......@@ -2381,7 +2376,7 @@ function install_check_requirements($install_state) {
if (!$install_state['settings_verified']) {
$readable = FALSE;
$writable = FALSE;
$conf_path = './' . conf_path(FALSE, TRUE);
$conf_path = './' . conf_path(FALSE);
$settings_file = $conf_path . '/settings.php';
$default_settings_file = './sites/default/default.settings.php';
$file = $conf_path;
......
......@@ -292,8 +292,7 @@ public function on500Html(FlattenException $exception, Request $request) {
// When running inside the testing framework, we relay the errors
// to the tested site by the way of HTTP headers.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
// $number does not use drupal_static as it should not be reset
// as it uniquely identifies each PHP error.
static $number = 0;
......
......@@ -49,7 +49,7 @@ public function getFormOptions(array $database) {
// Make the text more accurate for SQLite.
$form['database']['#title'] = t('Database file');
$form['database']['#description'] = t('The absolute path to the file where @drupal data will be stored. This must be writable by the web server and should exist outside of the web root.', array('@drupal' => drupal_install_profile_distribution_name()));
$default_database = conf_path(FALSE, TRUE) . '/files/.ht.sqlite';
$default_database = conf_path(FALSE) . '/files/.ht.sqlite';
$form['database']['#default_value'] = empty($database['database']) ? $default_database : $database['database'];
return $form;
}
......
......@@ -335,20 +335,13 @@ public function updateModules(array $module_list, array $module_filenames = arra
}
/**
* Returns the classname based on environment and testing prefix.
* Returns the classname based on environment.
*
* @return string
* The class name.
*/
protected function getClassName() {
$parts = array('service_container', $this->environment);
// Make sure to use a testing-specific container even in the parent site.
if (!empty($GLOBALS['drupal_test_info']['test_run_id'])) {
$parts[] = $GLOBALS['drupal_test_info']['test_run_id'];
}
elseif ($prefix = drupal_valid_test_ua()) {
$parts[] = $prefix;
}
return implode('_', $parts);
}
......
......@@ -34,9 +34,8 @@ public function onBeforeSendRequest(Event $event) {
// user-agent is used to ensure that multiple testing sessions running at the
// same time won't interfere with each other as they would if the database
// prefix were stored statically in a file or database variable.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['test_run_id'])) {
$event['request']->setHeader('User-Agent', drupal_generate_test_ua($test_info['test_run_id']));
if ($test_prefix = drupal_valid_test_ua()) {
$event['request']->setHeader('User-Agent', drupal_generate_test_ua($test_prefix));
}
}
}
......@@ -38,13 +38,6 @@ public function getExternalUrl() {
*/
public static function basePath() {
$base_path = settings()->get('file_public_path', conf_path() . '/files');
if ($test_prefix = drupal_valid_test_ua()) {
// Append the testing suffix unless already given.
// @see \Drupal\simpletest\WebTestBase::setUp()
if (strpos($base_path, '/simpletest/' . substr($test_prefix, 10)) === FALSE) {
return $base_path . '/simpletest/' . substr($test_prefix, 10);
}
}
return $base_path;
}
......
......@@ -95,11 +95,12 @@ function testImportCreate() {
// Add the new files to the staging directory.
$src_dir = drupal_get_path('module', 'field_test_config') . '/staging';
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name.yml", "public://config_staging/$field_config_name.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$instance_config_name.yml", "public://config_staging/$instance_config_name.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name_2.yml", "public://config_staging/$field_config_name_2.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$instance_config_name_2a.yml", "public://config_staging/$instance_config_name_2a.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$instance_config_name_2b.yml", "public://config_staging/$instance_config_name_2b.yml"));
$target_dir = $this->configDirectories[CONFIG_STAGING_DIRECTORY];
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name.yml", "$target_dir/$field_config_name.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$instance_config_name.yml", "$target_dir/$instance_config_name.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name_2.yml", "$target_dir/$field_config_name_2.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$instance_config_name_2a.yml", "$target_dir/$instance_config_name_2a.yml"));
$this->assertTrue(file_unmanaged_copy("$src_dir/$instance_config_name_2b.yml", "$target_dir/$instance_config_name_2b.yml"));
// Import the content of the staging directory.
$this->configImporter()->import();
......
......@@ -16,7 +16,7 @@
*/
class DummyReadOnlyStreamWrapper extends LocalReadOnlyStream {
function getDirectoryPath() {
return 'sites/default/files';
return conf_path() . '/files';
}
/**
......
......@@ -16,7 +16,7 @@
*/
class DummyStreamWrapper extends LocalStream {
function getDirectoryPath() {
return 'sites/default/files';
return conf_path() . '/files';
}
/**
......
......@@ -23,13 +23,6 @@ class LanguageNegotiationInfoTest extends WebTestBase {
*/
public static $modules = array('language');
/**
* The language manager.
*
* @var \Drupal\language\ConfigurableLanguageManagerInterface
*/
protected $languageManager;
/**
* {@inheritdoc}
*/
......@@ -46,31 +39,60 @@ public static function getInfo() {
*/
function setUp() {
parent::setUp();
$this->languageManager = $this->container->get('language_manager');
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'view the administration theme'));
$this->drupalLogin($admin_user);
$this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'it'), t('Add language'));
}
/**
* Returns the configurable language manager.
*
* @return \Drupal\language\ConfigurableLanguageManager
*/
protected function languageManager() {
return $this->container->get('language_manager');
}
/**
* Sets state flags for language_test module.
*
* Ensures to correctly update data both in the child site and the test runner
* environment.
*
* @param array $values
* The key/value pairs to set in state.
*/
protected function stateSet(array $values) {
// Set the new state values.
$this->container->get('state')->setMultiple($values);
// Refresh in-memory static state/config caches and static variables.
$this->refreshVariables();
// Refresh/rewrite language negotiation configuration, in order to pick up
// the manipulations performed by language_test module's info alter hooks.
$this->container->get('language_negotiator')->purgeConfiguration();
}
/**
* Tests alterations to language types/negotiation info.
*/
function testInfoAlterations() {
// Enable language type/negotiation info alterations.
\Drupal::state()->set('language_test.language_types', TRUE);
\Drupal::state()->set('language_test.language_negotiation_info', TRUE);
$this->languageNegotiationUpdate();
$this->stateSet(array(
// Enable language_test type info.
'language_test.language_types' => TRUE,
// Enable language_test negotiation info (not altered yet).
'language_test.language_negotiation_info' => TRUE,
// Alter Language::TYPE_CONTENT to be configurable.
'language_test.content_language_type' => TRUE,
));
$this->container->get('module_handler')->install(array('language_test'));
$this->rebuildContainer();
// Check that fixed language types are properly configured without the need
// of saving the language negotiation settings.
$this->checkFixedLanguageTypes();
// Make the content language type configurable by updating the language
// negotiation settings with the proper flag enabled.
\Drupal::state()->set('language_test.content_language_type', TRUE);
$this->languageNegotiationUpdate();
$type = Language::TYPE_CONTENT;
$language_types = $this->languageManager->getLanguageTypes();
$language_types = $this->languageManager()->getLanguageTypes();
$this->assertTrue(in_array($type, $language_types), 'Content language type is configurable.');
// Enable some core and custom language negotiation methods. The test
......@@ -87,30 +109,34 @@ function testInfoAlterations() {
);
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
// Remove the interface language negotiation method by updating the language
// negotiation settings with the proper flag enabled.
\Drupal::state()->set('language_test.language_negotiation_info_alter', TRUE);
$this->languageNegotiationUpdate();
$negotiation = \Drupal::config('language.types')->get('negotiation.' . $type . '.enabled') ?: array();
// Alter language negotiation info to remove interface language negotiation
// method.
$this->stateSet(array(
'language_test.language_negotiation_info_alter' => TRUE,
));
$negotiation = $this->container->get('config.factory')->get('language.types')->get('negotiation.' . $type . '.enabled');
$this->assertFalse(isset($negotiation[$interface_method_id]), 'Interface language negotiation method removed from the stored settings.');
$this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, 'Interface language negotiation method unavailable.');
$this->drupalGet('admin/config/regional/language/detection');
$this->assertNoFieldByName($form_field, NULL, 'Interface language negotiation method unavailable.');
// Check that type-specific language negotiation methods can be assigned
// only to the corresponding language types.
foreach ($this->languageManager->getLanguageTypes() as $type) {
foreach ($this->languageManager()->getLanguageTypes() as $type) {
$form_field = $type . '[enabled][test_language_negotiation_method_ts]';
if ($type == $test_type) {
$this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language negotiation method available for %type.', array('%type' => $type)));
$this->assertFieldByName($form_field, NULL, format_string('Type-specific test language negotiation method available for %type.', array('%type' => $type)));
}
else {
$this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language negotiation method unavailable for %type.', array('%type' => $type)));
$this->assertNoFieldByName($form_field, NULL, format_string('Type-specific test language negotiation method unavailable for %type.', array('%type' => $type)));
}
}
// Check language negotiation results.
$this->drupalGet('');
$last = \Drupal::state()->get('language_test.language_negotiation_last');
foreach ($this->languageManager->getDefinedLanguageTypes() as $type) {
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
foreach ($this->languageManager()->getDefinedLanguageTypes() as $type) {
$langcode = $last[$type];
$value = $type == Language::TYPE_CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en';
$this->assertEqual($langcode, $value, format_string('The negotiated language for %type is %language', array('%type' => $type, '%language' => $value)));
......@@ -118,10 +144,11 @@ function testInfoAlterations() {
// Uninstall language_test and check that everything is set back to the
// original status.
$this->languageNegotiationUpdate('uninstall');
$this->container->get('module_handler')->uninstall(array('language_test'));
$this->rebuildContainer();
// Check that only the core language types are available.
foreach ($this->languageManager->getDefinedLanguageTypes() as $type) {
foreach ($this->languageManager()->getDefinedLanguageTypes() as $type) {
$this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type)));
}
......@@ -131,7 +158,7 @@ function testInfoAlterations() {
// Check that unavailable language negotiation methods are not present in
// the negotiation settings.
$negotiation = \Drupal::config('language.types')->get('negotiation.' . $type . '.enabled') ?: array();
$negotiation = $this->container->get('config.factory')->get('language.types')->get('negotiation.' . $type . '.enabled');
$this->assertFalse(isset($negotiation[$test_method_id]), 'The disabled test language negotiation method is not part of the content language negotiation settings.');
// Check that configuration page presents the correct options and settings.
......@@ -139,41 +166,14 @@ function testInfoAlterations() {
$this->assertNoRaw(t('This is a test language negotiation method'), 'No test language negotiation method available.');
}
/**
* Update language types/negotiation information.
*
* Manually invoke language_modules_installed()/language_modules_uninstalled()
* since they would not be invoked after installing/uninstalling language_test
* the first time.
*/
protected function languageNegotiationUpdate($op = 'install') {
static $last_op = NULL;
$modules = array('language_test');
// Install/uninstall language_test only if we did not already before.
if ($last_op != $op) {
call_user_func(array($this->container->get('module_handler'), $op), $modules);
$last_op = $op;
}
else {
$function = "language_modules_{$op}ed";
if (function_exists($function)) {
$function($modules);
}
}
$this->languageManager->reset();
$this->drupalGet('admin/config/regional/language/detection');
}
/**
* Check that language negotiation for fixed types matches the stored one.
*/
protected function checkFixedLanguageTypes() {
$configurable = $this->languageManager->getLanguageTypes();
foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) {
$configurable = $this->languageManager()->getLanguageTypes();
foreach ($this->languageManager()->getDefinedLanguageTypesInfo() as $type => $info) {
if (!in_array($type, $configurable) && isset($info['fixed'])) {
$negotiation = \Drupal::config('language.types')->get('negotiation.' . $type . '.enabled') ?: array();
$negotiation = $this->container->get('config.factory')->get('language.types')->get('negotiation.' . $type . '.enabled');
$equal = count($info['fixed']) == count($negotiation);
while ($equal && list($id) = each($negotiation)) {
list(, $info_id) = each($info['fixed']);
......
......@@ -71,7 +71,8 @@ public function testImportCreate() {
$this->copyConfig($active, $staging);
// Manually add new node type.
$src_dir = drupal_get_path('module', 'node_test_config') . '/staging';
$this->assertTrue(file_unmanaged_copy("$src_dir/$node_type_config_name.yml", "public://config_staging/$node_type_config_name.yml"));
$target_dir = $this->configDirectories[CONFIG_STAGING_DIRECTORY];
$this->assertTrue(file_unmanaged_copy("$src_dir/$node_type_config_name.yml", "$target_dir/$node_type_config_name.yml"));
// Import the content of the staging directory.
$this->configImporter()->import();
......
......@@ -57,6 +57,13 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
private $themeFiles;
private $themeData;
/**
* The configuration directories for this test run.
*
* @var array
*/
protected $configDirectories = array();
/**
* A KeyValueMemoryFactory instance to use when building the container.
*
......@@ -93,6 +100,27 @@ protected function beforePrepareEnvironment() {
}
}
/**
* Create and set new configuration directories.
*
* @see config_get_config_directory()
*/
protected function prepareConfigDirectories() {
$this->configDirectories = array();
include_once DRUPAL_ROOT . '/core/includes/install.inc';
foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) {
// Assign the relative path to the global variable.
$path = $this->siteDirectory . '/config_' . $type;
$GLOBALS['config_directories'][$type] = $path;
// Ensure the directory can be created and is writeable.
if (!install_ensure_config_directory($type)) {
throw new \RuntimeException("Failed to create '$type' config directory $path");
}
// Provide the already resolved path for tests.
$this->configDirectories[$type] = $path;
}
}
/**
* Sets up Drupal unit test environment.
*/
......@@ -101,6 +129,9 @@ protected function setUp() {
parent::setUp();
// Create and set new configuration directories.
$this->prepareConfigDirectories();
// Build a minimal, partially mocked environment for unit tests.
$this->containerBuild(\Drupal::getContainer());
// Make sure it survives kernel rebuilds.
......@@ -157,7 +188,9 @@ protected function setUp() {
}
protected function tearDown() {
$this->kernel->shutdown();
if ($this->kernel instanceof DrupalKernel) {
$this->kernel->shutdown();
}
// Before tearing down the test environment, ensure that no stream wrapper
// of this test leaks into the parent environment. Unlike all other global
// state variables in Drupal, stream wrappers are a global state construct
......
......@@ -36,6 +36,13 @@ abstract class TestBase {
*/
protected $testId;
/**
* The site directory of this test run.
*
* @var string
*/
protected $siteDirectory = NULL;
/**
* The database prefix of this test run.
*
......@@ -151,6 +158,13 @@ abstract class TestBase {
*/
public $dieOnFail = FALSE;
/**
* The DrupalKernel instance used in the test.
*
* @var \Drupal\Core\DrupalKernel
*/
protected $kernel;
/**
* The dependency injection container used in the test.
*
......@@ -608,7 +622,20 @@ protected function assertIdenticalObject($object1, $object2, $message = '', $gro
return $this->assertTrue($identical, $message, $group);
}
/**
* Asserts that no errors have been logged to the PHP error.log thus far.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*
* @see TestBase::prepareEnvironment()
* @see _drupal_bootstrap_configuration()
*/
protected function assertNoErrorsLogged() {
// Since PHP only creates the error.log file when an actual error is
// triggered, it is sufficient to check whether the file exists.
return $this->assertFalse(file_exists(DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log'), 'PHP error.log is empty.');
}
/**
* Fire an assertion that is always positive.
......@@ -857,7 +884,9 @@ public function run(array $methods = array()) {
* @see drupal_valid_test_ua()
*/
private function prepareDatabasePrefix() {
$this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
$suffix = mt_rand(1000, 1000000);
$this->siteDirectory = 'sites/simpletest/' . $suffix;
$this->databasePrefix = 'simpletest' . $suffix;
// As soon as the database prefix is set, the test might start to execute.
// All assertions as well as the SimpleTest batch operations are associated
......@@ -885,19 +914,9 @@ private function changeDatabasePrefix() {
$connection_info = Database::getConnectionInfo('default');
Database::renameConnection('default', 'simpletest_original_default');
foreach ($connection_info as $target => $value) {
$connection_info[$target]['prefix'] = array(
'default' => $value['prefix']['default'] . $this->databasePrefix,
);
$connection_info[$target]['prefix'] = $value['prefix']['default'] . $this->databasePrefix;
}
Database::addConnectionInfo('default', 'default', $connection_info['default']);
// Additionally override global $databases, since the installer does not use
// the Database connection info.
// @see install_verify_database_settings()
// @see install_database_errors()
// @todo Fix installer to use Database connection info.
global $databases;
$databases['default']['default'] = $connection_info['default'];
}
/**
......@@ -938,10 +957,9 @@ private function prepareEnvironment() {
$language_interface = language(Language::TYPE_INTERFACE);
// When running the test runner within a test, back up the original database
// prefix and re-set the new/nested prefix in drupal_valid_test_ua().
if (drupal_valid_test_ua()) {
// prefix.
if (DRUPAL_TEST_IN_CHILD_SITE) {
$this->originalPrefix = drupal_valid_test_ua();
drupal_valid_test_ua($this->databasePrefix);
}
// Backup current in-memory configuration.
......@@ -981,21 +999,15 @@ private function prepareEnvironment() {
// Create test directory ahead of installation so fatal errors and debug
// information can be logged during installation process.
// Use temporary files directory with the same prefix as the database.
$this->public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10);
$this->private_files_directory = $this->public_files_directory . '/private';
$this->temp_files_directory = $this->private_files_directory . '/temp';
$this->translation_files_directory = $this->public_files_directory . '/translations';
// Create the directories
file_prepare_directory($this->public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY);
file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY);
file_prepare_directory($this->translation_files_directory, FILE_CREATE_DIRECTORY);
$this->generatedTestFiles = FALSE;
file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);