diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 51f4446..471d5c4 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -269,15 +269,15 @@ function timer_stop($name) { * @see default.settings.php */ function conf_path($require_settings = TRUE, $reset = FALSE) { - $conf_path = &drupal_static(__FUNCTION__, ''); + static $conf_path; - if ($conf_path && !$reset) { + if (isset($conf_path) && !$reset) { return $conf_path; } // Check for a simpletest override. - if ($simpletest_conf_path = _drupal_simpletest_conf_path()) { - $conf_path = $simpletest_conf_path; + if ($test_prefix = drupal_valid_test_ua()) { + $conf_path = 'sites/simpletest/' . substr($test_prefix, 10); return $conf_path; } @@ -292,50 +292,6 @@ function conf_path($require_settings = TRUE, $reset = FALSE) { } /** - * Determines whether to use an overridden value for conf_path(). - * - * Simpletest may provide a secondary, test-specific settings.php file to load - * after the primary one used by the parent site and override its variables. - * - If the child settings.php does not override $conf_path, then this function - * returns FALSE and conf_path() returns the directory of the primary - * settings.php. - * - If the child settings.php does override $conf_path, then - * _drupal_load_test_overrides() sets the 'simpletest_conf_path' setting, and - * this function returns that to conf_path(), causing installations and - * upgrades to act on that one. - * - * @return string|false - * The overridden $conf_path, or FALSE if the $conf_path should not currently - * be overridden. - * - * @see conf_path() - * @see _drupal_load_test_overrides() - */ -function _drupal_simpletest_conf_path() { - // Ensure that the settings object is available. conf_path() is called once - // before the Settings class is included, and at that point it should still - // load the primary $conf_path. See drupal_settings_initialize(). - if (!class_exists('Drupal\Component\Utility\Settings', FALSE)) { - return FALSE; - } - - // If no $simpletest_conf_path is set, use the normal $conf_path. - if (!($simpletest_conf_path = settings()->get('simpletest_conf_path'))) { - return FALSE; - } - - // Ensure that this is actually a simpletest request. We can't check this - // before settings.php is loaded. - if (!drupal_valid_test_ua()) { - return FALSE; - } - - // When the $simpletest_conf_path is set in a valid test request, - // return that path. - return $simpletest_conf_path; -} - -/** * Finds the appropriate configuration directory for a given host and path. * * Finds a matching configuration directory file by stripping the website's @@ -563,20 +519,32 @@ function drupal_valid_http_host($host) { * Sets the base URL, cookie domain, and session name from configuration. */ function drupal_settings_initialize() { - global $base_url, $base_path, $base_root, $script_path; - // Export these settings.php variables to the global namespace. - global $databases, $cookie_domain, $conf, $db_prefix, $drupal_hash_salt, $base_secure_url, $base_insecure_url, $config_directories; + global $base_url, $databases, $cookie_domain, $conf, $drupal_hash_salt, $config_directories; $conf = array(); // Make conf_path() available as local variable in settings.php. $conf_path = conf_path(); if (is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) { - include_once DRUPAL_ROOT . '/' . $conf_path . '/settings.php'; + require DRUPAL_ROOT . '/' . $conf_path . '/settings.php'; } - require_once __DIR__ . '../../lib/Drupal/Component/Utility/Settings.php'; new Settings(isset($settings) ? $settings : array()); +} + +/** + * Initializes global request variables. + * + * @todo D8: Eliminate this entirely in favor of Request object. + */ +function drupal_request_initialize() { + // Provided by settings.php. + // @see drupal_settings_initialize() + global $base_url, $cookie_domain; + // Set and derived from $base_url by this function. + global $base_path, $base_root, $script_path; + global $base_secure_url, $base_insecure_url; + $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; if (isset($base_url)) { @@ -1954,9 +1922,22 @@ function _drupal_bootstrap_configuration() { drupal_environment_initialize(); // Initialize the configuration, including variables from settings.php. drupal_settings_initialize(); + drupal_request_initialize(); // Make sure we are using the test database prefix in child Drupal sites. - _drupal_initialize_db_test_prefix(); + if ($test_prefix = drupal_valid_test_ua()) { + if (!empty($GLOBALS['databases']) && strpos($GLOBALS['databases']['default']['default']['prefix'], $test_prefix) === FALSE) { + throw new Exception('Bad prefix.'); + } + // @todo Move into initial (unset) getter logic of drupal_valid_test_ua()? + $test_info = &$GLOBALS['drupal_test_info']; + $test_info['test_run_id'] = $test_prefix; + $test_info['in_child_site'] = TRUE; + + // Log fatal errors to the test site directory. + ini_set('log_errors', 1); + ini_set('error_log', DRUPAL_ROOT . '/sites/simpletest/' . substr($test_prefix, 10) . '/error.log'); + } // Activate the class loader. drupal_classloader(); @@ -2046,39 +2027,6 @@ function _drupal_bootstrap_page_cache() { } /** - * In a test environment, get the test db prefix and set it in $databases. - */ -function _drupal_initialize_db_test_prefix() { - // 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 we - // validate we ourselves made the request. - if ($test_prefix = drupal_valid_test_ua()) { - // Set the test run id for use in other parts of Drupal. - $test_info = &$GLOBALS['drupal_test_info']; - $test_info['test_run_id'] = $test_prefix; - $test_info['in_child_site'] = TRUE; - - foreach ($GLOBALS['databases']['default'] as &$value) { - // Extract the current default database prefix. - if (!isset($value['prefix'])) { - $current_prefix = ''; - } - elseif (is_array($value['prefix'])) { - $current_prefix = $value['prefix']['default']; - } - else { - $current_prefix = $value['prefix']; - } - - // Remove the current database prefix and replace it by our own. - $value['prefix'] = array( - 'default' => $current_prefix . $test_prefix, - ); - } - } -} - -/** * Loads system variables and all enabled bootstrap modules. */ function _drupal_bootstrap_variables() { @@ -2215,84 +2163,66 @@ function drupal_valid_test_ua($new_prefix = NULL) { if (isset($new_prefix)) { $test_prefix = $new_prefix; + // A new prefix is only explicitly set within the parent site/test runner. + // Set the global test run ID and info accordingly. + if ($new_prefix) { + $test_info = &$GLOBALS['drupal_test_info']; + $test_info['test_run_id'] = $test_prefix; + if (!isset($test_info['in_child_site'])) { + $test_info['in_child_site'] = FALSE; + } + } + else { + unset($GLOBALS['drupal_test_info']); + } } if (isset($test_prefix)) { return $test_prefix; } - + $test_prefix = FALSE; if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) { list(, $prefix, $time, $salt, $hmac) = $matches; $check_string = $prefix . ';' . $time . ';' . $salt; - // We use the salt from settings.php to make the HMAC key, since - // the database is not yet initialized and we can't access any Drupal variables. + // Read the hash salt prepared by drupal_generate_test_ua(). + // This function is called before settings.php is read and Drupal's error + // handlers are set up. While Drupal's error handling may be properly + // configured on production sites, the server's PHP error_reporting may not. + // Ensure that no information leaks on production sites. + $key_file = DRUPAL_ROOT . '/sites/simpletest/' . substr($prefix, 10) . '/key.php'; + if (!is_readable($key_file)) { + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); + exit; + } + require $key_file; // The file properties add more entropy not easily accessible to others. - $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__); + $key = $private_key . filectime(__FILE__) . fileinode(__FILE__); $time_diff = REQUEST_TIME - $time; - // We can't use Crypt::hmacBase64() yet because this can be called in very - // early bootstrap when autoloader has not been initialized yet. - $test_hmac = base64_encode(hash_hmac('sha256', $check_string, $key, TRUE)); - $test_hmac = strtr($test_hmac, array('+' => '-', '/' => '_', '=' => '')); + $test_hmac = Crypt::hmacBase64($check_string, $key); // Since we are making a local request a 5 second time window is allowed, // and the HMAC must match. - if ($time_diff >= 0 && $time_diff <= 5 && $hmac == $test_hmac) { + if ($time_diff >= 0 && $time_diff <= 5 && $hmac === $test_hmac) { $test_prefix = $prefix; - _drupal_load_test_overrides($test_prefix); - return $test_prefix; } } - - $test_prefix = FALSE; return $test_prefix; } /** - * Overrides low-level and environment-specific configuration. - * - * Very strictly for internal use only. - * - * Loads settings.php from the simpletest public files directory. These files - * can change the global $conf, the global $config_directories, the return - * value of conf_path(), and settings(). - * - * @param string $test_prefix - * The simpletest prefix. - */ -function _drupal_load_test_overrides($test_prefix) { - global $conf, $config_directories; - - // Do not use the parent site's config directories. Use only the child site's. - // @see \Drupal\simpletest\TestBase::prepareConfigDirectories() - $path_prefix = 'simpletest/' . substr($test_prefix, 10); - $config_directories = array(); - foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { - $config_directories[$type] = conf_path() . '/files/' . $path_prefix . '/config_' . $type; - } - - // Check for and load a settings.php file in the simpletest files directory. - $filename = conf_path() . '/files/' . $path_prefix . '/settings.php'; - if (file_exists($filename)) { - $settings = settings()->getAll(); - $conf_path = &drupal_static('conf_path'); - // This can override $conf, $conf_path, $settings, and $config_directories. - include $filename; - // Keep the overriden $conf_path alive across drupal_static_reset() calls. - // @see conf_path() - $settings['simpletest_conf_path'] = $conf_path; - new Settings($settings); - } -} - -/** * Generates a user agent string with a HMAC and timestamp for simpletest. */ function drupal_generate_test_ua($prefix) { - static $key; + static $key, $last_prefix; + + if (!isset($key) || $last_prefix != $prefix) { + $last_prefix = $prefix; + // Generate and save a hash salt for a test run. + // Consumed by drupal_valid_test_ua() before settings.php is loaded. + $private_key = Crypt::randomStringHashed(55); + $key_file = "get('protocols'); if (!isset($allowed_protocols)) { diff --git a/core/includes/file.inc b/core/includes/file.inc index 545fca3..622a6ca 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -575,12 +575,12 @@ function file_ensure_htaccess() { */ function file_save_htaccess($directory, $private = TRUE) { if (file_uri_scheme($directory)) { - $directory = file_stream_wrapper_uri_normalize($directory); + $htaccess_path = file_stream_wrapper_uri_normalize($directory . '/.htaccess'); } else { $directory = rtrim($directory, '/\\'); + $htaccess_path = $directory . '/.htaccess'; } - $htaccess_path = $directory . '/.htaccess'; if (file_exists($htaccess_path)) { // Short circuit if the .htaccess file already exists. diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 6dfc270..4897ef2 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -287,19 +287,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); + // If we have a language selected and it is not yet saved in the system // (eg. pre-database data screens we are unable to persistently store // the default language), we should set language_default so the proper @@ -1067,7 +1066,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)) { @@ -1090,7 +1088,6 @@ 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(); @@ -1151,13 +1148,13 @@ 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']; + + // @todo Remove when PIFR submits 'prefix' instead of 'db_prefix'. + if (!empty($form_state['input'][$driver]['db_prefix'])) { + $form_state['values'][$driver]['prefix'] = $form_state['input'][$driver]['db_prefix']; } + $database = $form_state['values'][$driver]; + $drivers = drupal_get_database_types(); $reflection = new \ReflectionClass($drivers[$driver]); $install_namespace = $reflection->getNamespaceName(); @@ -1165,13 +1162,6 @@ function install_settings_form_validate($form, &$form_state) { $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\')); $database['driver'] = $driver; - // @todo PIFR uses 'db_prefix' instead of 'prefix'. Remove this when it gets - // fixed. - if (!$test_prefix) { - $database['prefix'] = $database['db_prefix']; - } - unset($database['db_prefix']); - $form_state['storage']['database'] = $database; $errors = install_database_errors($database, $form_state['values']['settings_file']); foreach ($errors as $name => $message) { @@ -1229,35 +1219,15 @@ function install_settings_form_submit($form, &$form_state) { global $install_state, $conf; // 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( @@ -2380,7 +2350,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; diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php index 633edc6..97eab54 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php @@ -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; } diff --git a/core/lib/Drupal/Core/Database/Install/Tasks.php b/core/lib/Drupal/Core/Database/Install/Tasks.php index e80dd86..f950647 100644 --- a/core/lib/Drupal/Core/Database/Install/Tasks.php +++ b/core/lib/Drupal/Core/Database/Install/Tasks.php @@ -251,14 +251,14 @@ public function getFormOptions(array $database) { '#weight' => 10, ); - $profile = drupal_get_profile(); - $db_prefix = ($profile == 'standard') ? 'drupal_' : $profile . '_'; - $form['advanced_options']['db_prefix'] = array( + $form['advanced_options']['prefix'] = array( '#type' => 'textfield', '#title' => t('Table name prefix'), '#default_value' => '', '#size' => 45, - '#description' => t('If more than one application will be sharing this database, a unique table name prefix–such as %prefix–will prevent collisions.', array('%prefix' => $db_prefix)), + '#description' => t('If more than one application will be sharing this database, a unique table name prefix–such as %prefix–will prevent collisions.', array( + '%prefix' => preg_replace('@[^a-z0-9]@', '_', strtolower(drupal_install_profile_distribution_name())) . '_', + )), '#weight' => 10, ); diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 0160119..e7a18b9 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -334,20 +334,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); } diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 90f17b3..760a1f8 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -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; } diff --git a/core/lib/Drupal/Core/SystemListingInfo.php b/core/lib/Drupal/Core/SystemListingInfo.php index d302699..3575adc 100644 --- a/core/lib/Drupal/Core/SystemListingInfo.php +++ b/core/lib/Drupal/Core/SystemListingInfo.php @@ -28,6 +28,7 @@ protected function profiles($directory) { // For SimpleTest to be able to test modules packaged together with a // distribution we need to include the profile of the parent site (in // which test runs are triggered). + // @todo !drupal_installation_attempted() defeats the whole purpose? if (drupal_valid_test_ua() && !drupal_installation_attempted()) { $testing_profile = \Drupal::config('simpletest.settings')->get('parent_profile'); if ($testing_profile && $testing_profile != $profile) { diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php index b427d3f..d230bc5 100644 --- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php +++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyReadOnlyStreamWrapper.php @@ -16,7 +16,7 @@ */ class DummyReadOnlyStreamWrapper extends LocalReadOnlyStream { function getDirectoryPath() { - return 'sites/default/files'; + return conf_path() . '/files'; } /** diff --git a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php index 4836f09..cbea40f 100644 --- a/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php +++ b/core/modules/file/tests/file_test/lib/Drupal/file_test/DummyStreamWrapper.php @@ -16,7 +16,7 @@ */ class DummyStreamWrapper extends LocalStream { function getDirectoryPath() { - return 'sites/default/files'; + return conf_path() . '/files'; } /** diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 560afb1..0be7bd9 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -36,6 +36,13 @@ protected $testId; /** + * The site directory of this test run. + * + * @var string + */ + protected $siteDirectory = NULL; + + /** * The database prefix of this test run. * * @var string @@ -217,11 +224,6 @@ public static function getInfo() { } /** - * Performs setup tasks before each individual test method is run. - */ - abstract protected function setUp(); - - /** * Checks the matching requirements for Test. * * @return @@ -837,7 +839,9 @@ public function run(array $methods = array()) { * @see WebTestBase::setUp() */ protected 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 @@ -868,20 +872,10 @@ protected 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']; - // Indicate the database prefix was set up correctly. $this->setupDatabasePrefix = TRUE; } @@ -946,22 +940,31 @@ protected 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); + file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + + // Prepare filesystem directory paths. + $this->public_files_directory = $this->siteDirectory . '/files'; + $this->private_files_directory = $this->siteDirectory . '/private'; + $this->temp_files_directory = $this->siteDirectory . '/temp'; + $this->translation_files_directory = $this->siteDirectory . '/translations'; + + // Create filesystem directories, unless the installer will be executed. + // @todo Move into DrupalUnitTestBase::setUp(). + if (!$this instanceof WebTestBase) { + 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); + + // Create and set new configuration directories. + $this->prepareConfigDirectories(); + } + else { + $GLOBALS['config_directories'] = array(); + $this->configDirectories = array(); + } $this->generatedTestFiles = FALSE; - // Create and set new configuration directories. - $this->prepareConfigDirectories(); - // Reset statics before the old container is replaced so that objects with a // __destruct() method still have access to it. // @todo: Remove once they have been converted to services. @@ -987,7 +990,7 @@ protected function prepareEnvironment() { // Log fatal errors. ini_set('log_errors', 1); - ini_set('error_log', $this->public_files_directory . '/error.log'); + ini_set('error_log', $this->siteDirectory . '/error.log'); // Set the test information for use in other parts of Drupal. $test_info = &$GLOBALS['drupal_test_info']; @@ -1013,7 +1016,7 @@ protected function prepareConfigDirectories() { 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 = conf_path() . '/files/simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type; + $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)) { @@ -1040,11 +1043,11 @@ protected function prepareConfigDirectories() { * tests can invoke this workaround when requiring services from newly * enabled modules to be immediately available in the same request. */ - protected function rebuildContainer() { + protected function rebuildContainer($environment = 'testing') { // Preserve the request object after the container rebuild. $request = \Drupal::request(); - $this->kernel = new DrupalKernel('testing', drupal_classloader(), FALSE); + $this->kernel = new DrupalKernel($environment, drupal_classloader(), FALSE); $this->kernel->boot(); // DrupalKernel replaces the container in \Drupal::getContainer() with a // different object, so we need to replace the instance on this test class. @@ -1055,6 +1058,36 @@ protected function rebuildContainer() { } /** + * Performs setup tasks before each individual test method is run. + */ + protected function setUp() { + // Create the database prefix for this test. + $this->prepareDatabasePrefix(); + + // Prepare the environment for running tests. + $this->prepareEnvironment(); + if (!$this->setupEnvironment) { + throw new \RuntimeException('Failed to set up test environment.'); + } + + // 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) { + throw new \RuntimeException('Failed to set up database prefix.'); + } + + // After preparing the environment and changing the database prefix, we are + // in a valid test environment. + drupal_valid_test_ua($this->databasePrefix); + conf_path(FALSE, TRUE); + + $this->setup = TRUE; + } + + /** * Performs cleanup tasks after each individual test method has been run. * * Deletes created files, database tables, and reverts environment changes. @@ -1106,8 +1139,8 @@ protected function tearDown() { // log to pick up any fatal errors. simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); - // Delete temporary files directory. - file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10), array($this, 'filePreDeleteCallback')); + // Delete test site directory. + file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback')); // Restore original database connection. Database::removeConnection('default'); @@ -1135,9 +1168,14 @@ protected function tearDown() { // Restore original statics and globals. \Drupal::setContainer($this->originalContainer); $GLOBALS['config_directories'] = $this->originalConfigDirectories; + if (isset($this->originalPrefix)) { drupal_valid_test_ua($this->originalPrefix); } + else { + drupal_valid_test_ua(FALSE); + } + conf_path(TRUE, TRUE); // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/MissingCheckedRequirementsTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/MissingCheckedRequirementsTest.php index 61d6401..8725e88 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/MissingCheckedRequirementsTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/MissingCheckedRequirementsTest.php @@ -39,7 +39,7 @@ function setUp() { * Overrides checkRequirements(). */ protected function checkRequirements() { - if (drupal_valid_test_ua()) { + if (!empty($GLOBALS['drupal_test_info']['in_child_site'])) { return array( 'Test is not allowed to run.' ); @@ -53,7 +53,7 @@ protected function checkRequirements() { protected function testCheckRequirements() { // If this is the main request, run the web test script and then assert // that the child tests did not run. - if (!drupal_valid_test_ua()) { + if (empty($GLOBALS['drupal_test_info']['in_child_site'])) { // Run this test from web interface. $edit['Drupal\simpletest\Tests\MissingCheckedRequirementsTest'] = TRUE; $this->drupalPostForm('admin/config/development/testing', $edit, t('Run tests')); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php index 2a19a5c1..e830297 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php @@ -339,6 +339,6 @@ function asText(\SimpleXMLElement $element) { * Check if the test is being run from inside a CURL request. */ function inCURL() { - return (bool) drupal_valid_test_ua(); + return (bool) !empty($GLOBALS['drupal_test_info']['in_child_site']); } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php index 5510aa4..733d51f 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php @@ -43,30 +43,11 @@ function __construct($test_id = NULL) { protected function setUp() { global $conf; - // Create the database prefix for this test. - $this->prepareDatabasePrefix(); + parent::setUp(); - // 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. + // Reset all variables to perform tests in a clean environment. $conf = array(); - drupal_static_reset(); $this->settingsSet('file_public_path', $this->public_files_directory); - - // 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; - } - - $this->setup = TRUE; } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 265012e..3f13782 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\NestedArray; +use Drupal\Component\Utility\Settings; use Drupal\Component\Utility\String; use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; @@ -47,7 +48,21 @@ protected $curlHandle; /** - * The headers of the page currently loaded in the internal browser. + * The request headers of all requests performed. + * + * @var array + */ + protected $requestHeaders; + + /** + * The response headers of all requests performed. + * + * @var array + */ + protected $responseHeaders; + + /** + * The response headers of the page currently loaded in the internal browser. * * @var Array */ @@ -726,32 +741,10 @@ protected function setUp() { // Backup the currently running Simpletest batch. $this->originalBatch = batch_get(); - // Create the database prefix for this test. - $this->prepareDatabasePrefix(); + parent::setUp(); - // 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. + // Reset all 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; - } - - // 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; // Define information about the user 1 account. $this->root_user = new UserSession(array( @@ -764,56 +757,92 @@ protected function setUp() { // 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, - ), + + // Get parameters for install_drupal() before removing global variables. + $parameters = $this->installParameters(); + + // Prepare installer settings that are not install_drupal() parameters. + // Copy and prepare an actual settings.php, so as to resemble a regular + // installation. + copy(DRUPAL_ROOT . '/sites/default/default.settings.php', DRUPAL_ROOT . '/' . $this->siteDirectory . '/settings.php'); + + // All file system paths are set up by System module during installation. + // @see system_requirements() + // @see TestBase::prepareEnvironment() + $settings['settings']['file_public_path'] = (object) array( + 'value' => $this->public_files_directory, + 'required' => TRUE, ); - foreach ($variable_groups as $config_base => $variables) { - foreach ($variables as $name => $value) { - NestedArray::setValue($GLOBALS['conf'], array_merge(array($config_base), explode('.', $name)), $value); - } - } - $this->settingsSet('file_public_path', $this->public_files_directory); + // @todo While private/temporary filesystem paths can be preset in the + // installer via global configuration overrides, some tests expect the + // values to be configurable through the UI. By setting them in + // settings.php, they are no longer configurable... Why are these not + // settings like the public filesystem path to begin with? (consistency) + /* + $settings['conf']['system.file']['path']['private'] = (object) array( + 'value' => $this->private_files_directory, + 'required' => TRUE, + ); + $settings['conf']['system.file']['path']['temporary'] = (object) array( + 'value' => $this->temp_files_directory, + 'required' => TRUE, + ); + */ + // Use the test mail class instead of the default mail handler class. + $settings['conf']['system.mail']['interface']['default'] = (object) array( + 'value' => 'Drupal\Core\Mail\TestMailCollector', + 'required' => TRUE, + ); + // Add the parent profile's search path to the child site's search paths. + // @see drupal_system_listing() + $settings['conf']['simpletest.settings']['parent_profile'] = (object) array( + 'value' => $this->originalProfile, + 'required' => TRUE, + ); + $this->writeSettings($settings); + + // Since Drupal is bootstrapped already, install_begin_request() will not + // bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to + // reload the new settings.php manually. + drupal_settings_initialize(); + // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; - $this->settingsSet('cache', array('default' => 'cache.backend.memory')); - $parameters = $this->installParameters(); install_drupal($parameters); - // 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); + // Import new settings.php. + global $databases, $config_directories, $settings, $conf; + $settings = array(); + include DRUPAL_ROOT . '/' . $this->siteDirectory . '/settings.php'; + foreach ($config_directories as $type => $path) { + $this->configDirectories[$type] = $this->siteDirectory . '/' . $path; + } + // Consume new settings written by the installer. + new Settings($settings); + + // After writing settings.php, the installer removes write permissions + // from the site directory. To allow drupal_generate_test_ua() to write + // a key.php file containing the private key for drupal_valid_test_ua(), + // the site directory has to be writable. + // Use chmod() without a Drupal wrapper, so potential errors are visible. + // WebTestBase::tearDown() will delete the entire test site directory. + chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777); + + // Manually provide private and temporary files directories. + // (see @todo above) + file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY); + file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); + \Drupal::config('system.file') + ->set('path.private', $this->private_files_directory) + ->set('path.temporary', $this->temp_files_directory) + ->save(); - $this->settingsSet('cache', array()); $this->rebuildContainer(); // Restore the original Simpletest batch. $batch = &batch_get(); $batch = $this->originalBatch; - // 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() - \Drupal::config('simpletest.settings')->set('parent_profile', $this->originalProfile)->save(); - // Collect modules to install. $class = get_class($this); $modules = array(); @@ -828,24 +857,12 @@ protected function setUp() { $success = \Drupal::moduleHandler()->install($modules, TRUE); $this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); $this->rebuildContainer(); - } - // 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) { - $config = \Drupal::config($config_base); - foreach ($variables as $name => $value) { - $config->set($name, $value); - } - $config->save(); + // Reset static variables after enabling additional test modules. + // Affects e.g. file_get_stream_wrappers(). + drupal_static_reset(); } - // Use the test mail class instead of the default mail handler class. - \Drupal::config('system.mail')->set('interface.default', 'Drupal\Core\Mail\TestMailCollector')->save(); - drupal_set_time_limit($this->timeLimit); // 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. @@ -864,6 +881,10 @@ protected function setUp() { */ protected function installParameters() { $connection_info = Database::getConnectionInfo(); + $driver = $connection_info['default']['driver']; + unset($connection_info['default']['driver']); + unset($connection_info['default']['pdo']); + unset($connection_info['default']['init_commands']); $parameters = array( 'interactive' => FALSE, 'parameters' => array( @@ -871,7 +892,10 @@ protected function installParameters() { 'langcode' => 'en', ), 'forms' => array( - 'install_settings_form' => $connection_info['default'], + 'install_settings_form' => array( + 'driver' => $driver, + $driver => $connection_info['default'], + ), 'install_configure_form' => array( 'site_name' => 'Drupal', 'site_mail' => 'simpletest@example.com', @@ -915,8 +939,7 @@ protected function writeSettings($settings) { } include_once DRUPAL_ROOT . '/core/includes/install.inc'; - $filename = $this->public_files_directory . '/settings.php'; - file_put_contents($filename, "siteDirectory . '/settings.php'; drupal_rewrite_settings($settings, $filename); // Restore the original globals. @@ -966,8 +989,8 @@ protected function writeCustomTranslations() { /** * Overrides \Drupal\simpletest\TestBase::rebuildContainer(). */ - protected function rebuildContainer() { - parent::rebuildContainer(); + protected function rebuildContainer($environment = 'prod') { + parent::rebuildContainer($environment); // Make sure the url generator has a request object, otherwise calls to // $this->drupalGet() will fail. $this->prepareRequestForGenerator(); @@ -1068,6 +1091,7 @@ protected function curlInitialize() { CURLOPT_SSL_VERIFYHOST => FALSE, CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'), CURLOPT_USERAGENT => $this->databasePrefix, + CURLINFO_HEADER_OUT => TRUE, ); if (isset($this->httpauth_credentials)) { $curl_options[CURLOPT_HTTPAUTH] = $this->httpauth_method; @@ -1179,13 +1203,25 @@ protected function curlExec($curl_options, $redirect = FALSE) { if (!$redirect) { // Reset headers, the session ID and the redirect counter. $this->session_id = NULL; - $this->headers = array(); + $this->requestId = 0; + $this->requestHeaders = array(); + $this->responseHeaders = array(); $this->redirect_count = 0; } + $this->headers = array(); $content = curl_exec($this->curlHandle); $status = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); + $this->requestHeaders[$this->requestId] = curl_getinfo($this->curlHandle, CURLINFO_HEADER_OUT); + + $response_headers = $this->headers; + $this->responseHeaders[$this->requestId] = array_shift($response_headers); + sort($response_headers); + $this->responseHeaders[$this->requestId] .= implode('', $response_headers); + + $this->requestId++; + // 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 @@ -1333,7 +1369,10 @@ protected function drupalGet($path, array $options = array(), array $headers = a $verbose = 'GET request to: ' . $path . '
Ending URL: ' . $this->getUrl(); if ($this->dumpHeaders) { - $verbose .= '
Headers:
' . check_plain(var_export(array_map('trim', $this->headers), TRUE)) . '
'; + for ($i = 0; $i < $this->requestId; $i++) { + $verbose .= '
Request headers:
' . check_plain($this->requestHeaders[$i]) . '
'; + $verbose .= '
Response headers:
' . check_plain($this->responseHeaders[$i]) . '
'; + } } $verbose .= '
' . $out; @@ -1523,7 +1562,10 @@ protected function drupalPostForm($path, $edit, $submit, array $options = array( $verbose = 'POST request to: ' . $path; $verbose .= '
Ending URL: ' . $this->getUrl(); if ($this->dumpHeaders) { - $verbose .= '
Headers:
' . check_plain(var_export(array_map('trim', $this->headers), TRUE)) . '
'; + for ($i = 0; $i < $this->requestId; $i++) { + $verbose .= '
Request headers:
' . check_plain($this->requestHeaders[$i]) . '
'; + $verbose .= '
Response headers:
' . check_plain($this->responseHeaders[$i]) . '
'; + } } $verbose .= '
Fields: ' . highlight_string('' . $out; @@ -1887,14 +1929,16 @@ protected function drupalHead($path, array $options = array(), array $headers = $options['absolute'] = TRUE; $url = $this->container->get('url_generator')->generateFromPath($path, $options); $out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => $url, CURLOPT_HTTPHEADER => $headers)); - // Ensure that any changes to variables in the other thread are picked up. - $this->refreshVariables(); + $verbose = 'HEAD request to: ' . $path; + $verbose .= '
Ending URL: ' . $this->getUrl(); if ($this->dumpHeaders) { - $this->verbose('GET request to: ' . $path . - '
Ending URL: ' . $this->getUrl() . - '
Headers:
' . check_plain(var_export(array_map('trim', $this->headers), TRUE)) . '
'); + for ($i = 0; $i < $this->requestId; $i++) { + $verbose .= '
Request headers:
' . check_plain($this->requestHeaders[$i]) . '
'; + $verbose .= '
Response headers:
' . check_plain($this->responseHeaders[$i]) . '
'; + } } + $this->verbose($verbose); return $out; } diff --git a/core/modules/simpletest/simpletest.install b/core/modules/simpletest/simpletest.install index 4b940ed..eb73920 100644 --- a/core/modules/simpletest/simpletest.install +++ b/core/modules/simpletest/simpletest.install @@ -58,6 +58,20 @@ function simpletest_requirements($phase) { $requirements['php_memory_limit']['description'] = t('The testing framework requires the PHP memory limit to be at least %memory_minimum_limit. The current value is %memory_limit. Follow these steps to continue.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, '@url' => 'http://drupal.org/node/207036')); } + if ($phase == 'runtime') { + $site_directory = 'sites/simpletest'; + if (!drupal_verify_install_file(DRUPAL_ROOT . '/' . $site_directory, FILE_EXIST|FILE_WRITABLE, 'dir')) { + $requirements['simpletest_site_directory'] = array( + 'title' => t('Simpletest site directory'), + 'value' => is_dir(DRUPAL_ROOT . '/' . $site_directory) ? t('Not writable') : t('Missing'), + 'severity' => REQUIREMENT_ERROR, + 'description' => t('The testing framework requires the !test-sites-directory to exist and be writable in order to run tests.', array( + '!test-sites-directory' => './' . $site_directory . '', + )), + ); + } + } + return $requirements; } @@ -154,6 +168,20 @@ function simpletest_schema() { } /** + * Implements hook_install(). + */ +function simpletest_install() { + // Attempt to automatically create the sites directory for tests. + // @see simpletest_requirements() + $site_directory = 'sites/simpletest'; + if (!drupal_install_mkdir(DRUPAL_ROOT . '/' . $site_directory, FILE_READABLE|FILE_WRITABLE|FILE_EXECUTABLE)) { + drupal_set_message(t('The test sites directory !directory could not be created automatically. Create it manually and make it writable for the web server.', array( + '!directory' => './' . $site_directory . '', + )), 'warning'); + } +} + +/** * Implements hook_uninstall(). */ function simpletest_uninstall() { diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index bd50676..ba3e9d5 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -367,7 +367,7 @@ function simpletest_last_test_get($test_id) { * * @param $test_id * The test ID to which the log relates. - * @param $prefix + * @param $database_prefix * The database prefix to which the log relates. * @param $test_class * The test class to which the log relates. @@ -377,8 +377,8 @@ function simpletest_last_test_get($test_id) { * @return * Found any entries in log. */ -function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) { - $log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log'; +function simpletest_log_read($test_id, $database_prefix, $test_class, $during_test = FALSE) { + $log = 'sites/simpletest/' . substr($database_prefix, 10) . '/error.log'; $found = FALSE; if (file_exists($log)) { foreach (file($log) as $line) { @@ -632,14 +632,12 @@ function simpletest_clean_database() { */ function simpletest_clean_temporary_directories() { $count = 0; - if (is_dir('public://simpletest')) { - $files = scandir('public://simpletest'); + if (is_dir('sites/simpletest')) { + $files = scandir('sites/simpletest'); foreach ($files as $file) { - $path = 'public://simpletest/' . $file; - if (is_dir($path) && (is_numeric($file) || strpos($file, 'config_simpletest') !== FALSE)) { - file_unmanaged_delete_recursive($path, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback')); - $count++; - } + $path = 'sites/simpletest/' . $file; + file_unmanaged_delete_recursive($path, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback')); + $count++; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/File/FileTestBase.php b/core/modules/system/lib/Drupal/system/Tests/File/FileTestBase.php index 540c19a..70fa934 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/FileTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/FileTestBase.php @@ -16,16 +16,6 @@ */ abstract class FileTestBase extends WebTestBase { - function setUp() { - parent::setUp(); - // Make sure that custom stream wrappers are registered. - // @todo This has the potential to be a major bug deeply buried in File API; - // file_unmanaged_*() API functions and test functions are invoking native - // PHP functions directly, whereas Drupal's custom stream wrappers are not - // registered yet. - file_get_stream_wrappers(); - } - /** * Check that two files have the same values for all fields other than the * timestamp. diff --git a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php index ef84796..5f3ea59 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php @@ -30,14 +30,9 @@ public static function getInfo() { ); } - function setUp() { - parent::setUp(); - drupal_static_reset('file_get_stream_wrappers'); - } - function tearDown() { - parent::tearDown(); stream_wrapper_unregister($this->scheme); + parent::tearDown(); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php index 16bc592..6ce6568 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/StreamWrapperTest.php @@ -33,14 +33,9 @@ public static function getInfo() { ); } - function setUp() { - parent::setUp(); - drupal_static_reset('file_get_stream_wrappers'); - } - function tearDown() { - parent::tearDown(); stream_wrapper_unregister($this->scheme); + parent::tearDown(); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php index 69b3352..afda79d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php @@ -33,23 +33,17 @@ protected function setUp() { // Back up the currently running SimpleTest batch. $this->originalBatch = batch_get(); - // Add the translations directory so we can retrieve German translations. - $conf['locale.settings']['translation.path'] = drupal_get_path('module', 'simpletest') . '/files/translations'; - $conf['language_default']['name'] = 'German'; - $conf['language_default']['id'] = 'de'; - // Create the database prefix for this test. $this->prepareDatabasePrefix(); // Prepare the environment for running tests. $this->prepareEnvironment(); if (!$this->setupEnvironment) { - return FALSE; + throw new \RuntimeException('Failed to set up test environment.'); } // 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 @@ -57,34 +51,18 @@ protected function setUp() { // write back to persistent caches when they are destructed. $this->changeDatabasePrefix(); if (!$this->setupDatabasePrefix) { - return FALSE; - } - $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, - ), - ); - foreach ($variable_groups as $config_base => $variables) { - foreach ($variables as $name => $value) { - NestedArray::setValue($GLOBALS['conf'], array_merge(array($config_base), explode('.', $name)), $value); - } + throw new \RuntimeException('Failed to set up database prefix.'); } - $settings['conf_path'] = (object) array( - 'value' => $this->public_files_directory, - 'required' => TRUE, - ); - $settings['config_directories'] = (object) array( - 'value' => array(), - 'required' => TRUE, - ); - $this->writeSettings($settings); + + // After preparing the environment and changing the database prefix, we are + // in a valid test environment. + drupal_valid_test_ua($this->databasePrefix); // Submit the installer with German language. - $this->drupalPostForm($GLOBALS['base_url'] . '/core/install.php', array('langcode' => 'de'), 'Save and continue'); + $edit = array( + 'langcode' => 'de', + ); + $this->drupalPostForm($GLOBALS['base_url'] . '/core/install.php', $edit, 'Save and continue'); // On the following page where installation profile is being selected the // interface should be already translated, so there is no "Set up database" @@ -99,28 +77,17 @@ protected function setUp() { // Get the "Save and continue" submit button translated value from the // translated interface. $submit_value = (string) current($this->xpath('//input[@type="submit"]/@value')); + $this->assertNotEqual($submit_value, 'Save and continue'); - // Submit the standard profile installation. - $this->drupalPostForm(NULL, array('profile' => 'standard'), $submit_value); + // Submit the Minimal profile installation. + $edit = array( + 'profile' => 'minimal', + ); + $this->drupalPostForm(NULL, $edit, $submit_value); // Submit the next step. $this->drupalPostForm(NULL, array(), $submit_value); - // Reload config directories. - include $this->public_files_directory . '/settings.php'; - foreach ($config_directories as $type => $path) { - $GLOBALS['config_directories'][$type] = $path; - } - $this->rebuildContainer(); - - foreach ($variable_groups as $config_base => $variables) { - $config = \Drupal::config($config_base); - foreach ($variables as $name => $value) { - $config->set($name, $value); - } - $config->save(); - } - // Submit site configuration form. $this->drupalPostForm(NULL, array( 'site_mail' => 'admin@test.de', diff --git a/core/modules/system/lib/Drupal/system/Tests/InstallerTest.php b/core/modules/system/lib/Drupal/system/Tests/InstallerTest.php index 5bbaab8..923424f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/InstallerTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/InstallerTest.php @@ -39,7 +39,7 @@ protected function setUp() { // Prepare the environment for running tests. $this->prepareEnvironment(); if (!$this->setupEnvironment) { - return FALSE; + throw new \RuntimeException('Failed to set up test environment.'); } // Reset all statics and variables to perform tests in a clean environment. @@ -52,34 +52,35 @@ protected function setUp() { // write back to persistent caches when they are destructed. $this->changeDatabasePrefix(); if (!$this->setupDatabasePrefix) { - return FALSE; + throw new \RuntimeException('Failed to set up database prefix.'); } - $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, + + // After preparing the environment and changing the database prefix, we are + // in a valid test environment. + drupal_valid_test_ua($this->databasePrefix); + + $settings['conf']['system.file'] = (object) array( + 'value' => array( + 'path' => array( + 'private' => $this->private_files_directory, + 'temporary' => $this->temp_files_directory, + ), ), - ); - foreach ($variable_groups as $config_base => $variables) { - foreach ($variables as $name => $value) { - NestedArray::setValue($GLOBALS['conf'], array_merge(array($config_base), explode('.', $name)), $value); - } - } - $settings['conf_path'] = (object) array( - 'value' => $this->public_files_directory, 'required' => TRUE, ); - $settings['config_directories'] = (object) array( - 'value' => array(), + $settings['conf']['locale.settings'] = (object) array( + 'value' => array( + 'translation' => array( + 'path' => $this->translation_files_directory, + ), + ), 'required' => TRUE, ); $this->writeSettings($settings); $this->drupalGet($GLOBALS['base_url'] . '/core/install.php?langcode=en&profile=minimal'); $this->drupalPostForm(NULL, array(), 'Save and continue'); + // Reload config directories. include $this->public_files_directory . '/settings.php'; foreach ($config_directories as $type => $path) { @@ -87,14 +88,6 @@ protected function setUp() { } $this->rebuildContainer(); - foreach ($variable_groups as $config_base => $variables) { - $config = \Drupal::config($config_base); - foreach ($variables as $name => $value) { - $config->set($name, $value); - } - $config->save(); - } - // Use the test mail class instead of the default mail handler class. \Drupal::config('system.mail')->set('interface.default', 'Drupal\Core\Mail\TestMailCollector')->save(); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 4fd03af..ab6fd81 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -327,15 +327,14 @@ function system_requirements($phase) { } else { // If we are installing Drupal, the settings.php file might not exist yet - // in the intended conf_path() directory, so don't require it. The - // conf_path() cache must also be reset in this case. - $directories[] = conf_path(FALSE, TRUE) . '/files'; + // in the intended site directory, so don't require it. + $directories[] = conf_path(FALSE) . '/files'; } - if (!empty($conf['system.file']['path.private'])) { - $directories[] = $conf['system.file']['path.private']; + if (!empty($conf['system.file']['path']['private'])) { + $directories[] = $conf['system.file']['path']['private']; } - if (!empty($conf['system.file']['path.temporary'])) { - $directories[] = $conf['system.file']['path.temporary']; + if (!empty($conf['system.file']['path']['temporary'])) { + $directories[] = $conf['system.file']['path']['temporary']; } else { // If the temporary directory is not overridden use an appropriate diff --git a/core/rebuild.php b/core/rebuild.php index 1b94601..b408c43 100644 --- a/core/rebuild.php +++ b/core/rebuild.php @@ -15,6 +15,7 @@ // Change the directory to the Drupal root. chdir('..'); +require_once __DIR__ . '/vendor/autoload.php'; require_once dirname(__DIR__) . '/core/includes/bootstrap.inc'; require_once dirname(__DIR__) . '/core/includes/utility.inc'; diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index e9f0ffe..6af207a 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -585,10 +585,9 @@ function simpletest_script_cleanup($test_id, $test_class, $exitcode) { // Read the log file in case any fatal errors caused the test to crash. simpletest_log_read($test_id, $db_prefix, $test_class); - // Check whether a test file directory was setup already. - // @see prepareEnvironment() - $public_files = PublicStream::basePath(); - $test_directory = $public_files . '/simpletest/' . substr($db_prefix, 10); + // Check whether a test site directory was setup already. + // @see \Drupal\simpletest\TestBase::prepareEnvironment() + $test_directory = DRUPAL_ROOT . '/sites/simpletest/' . substr($db_prefix, 10); if (is_dir($test_directory)) { // Output the error_log. if (is_file($test_directory . '/error.log')) { @@ -597,8 +596,7 @@ function simpletest_script_cleanup($test_id, $test_class, $exitcode) { $messages[] = $errors; } } - - // Delete the test files directory. + // Delete the test site directory. // simpletest_clean_temporary_directories() cannot be used here, since it // would also delete file directories of other tests that are potentially // running concurrently.