diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 51f4446..a2aed9d 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
@@ -572,11 +528,11 @@ function drupal_settings_initialize() {
   // 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());
+
   $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
 
   if (isset($base_url)) {
@@ -1956,7 +1912,15 @@ function _drupal_bootstrap_configuration() {
   drupal_settings_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;
+  }
 
   // Activate the class loader.
   drupal_classloader();
@@ -2046,39 +2010,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 +2146,56 @@ 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().
+    require DRUPAL_ROOT . '/sites/simpletest/' . substr($prefix, 10) . '/key.php';
     // 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;
 
   if (!isset($key)) {
-    // 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.
+    // 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 = "<?php\n\$private_key = '$private_key';\n";
+    file_put_contents(DRUPAL_ROOT . '/sites/simpletest/' . substr($prefix, 10) . '/key.php', $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__);
   }
   // Generate a moderately secure HMAC based on the database credentials.
   $salt = uniqid('', TRUE);
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 6dfc270..2ef6b95 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();
@@ -1154,9 +1151,9 @@ function install_settings_form_validate($form, &$form_state) {
   $database = $form_state['values'][$driver];
   // When testing the interactive installer, copy the database password and
   // the test prefix.
+  // @todo Remove this; pass prefix in POST data instead.
   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]);
@@ -1167,7 +1164,7 @@ function install_settings_form_validate($form, &$form_state) {
 
   // @todo PIFR uses 'db_prefix' instead of 'prefix'. Remove this when it gets
   //   fixed.
-  if (!$test_prefix) {
+  if (!$test_prefix && isset($database['db_prefix'])) {
     $database['prefix'] = $database['db_prefix'];
   }
   unset($database['db_prefix']);
@@ -1229,35 +1226,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 +2357,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/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 0160119..f6a1810 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -342,10 +342,7 @@ public function updateModules(array $module_list, array $module_filenames = arra
   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()) {
+    if ($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/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 560afb1..2209555 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
@@ -946,13 +950,14 @@ 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';
+    $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 the directories
+    file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    // @todo Do not create these for WebTestBase; rely on installer.
     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);
@@ -960,7 +965,14 @@ protected function prepareEnvironment() {
     $this->generatedTestFiles = FALSE;
 
     // Create and set new configuration directories.
-    $this->prepareConfigDirectories();
+    // @todo Only valid for tests not executing the installer (in any way).
+    if (!$this instanceof WebTestBase) {
+      $this->prepareConfigDirectories();
+    }
+    else {
+      $GLOBALS['config_directories'] = array();
+      $this->configDirectories = array();
+    }
 
     // Reset statics before the old container is replaced so that objects with a
     // __destruct() method still have access to it.
@@ -1013,7 +1025,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)) {
@@ -1055,6 +1067,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 +1148,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 +1177,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..0d73cfa 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -726,32 +726,14 @@ 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.
+    // Add the parent profile's search path to the child site's search paths.
     // @see drupal_system_listing()
-    $conf['simpletest_parent_profile'] = $this->originalProfile;
+    $conf['simpletest.settings']['parent_profile'] = $this->originalProfile;
 
     // Define information about the user 1 account.
     $this->root_user = new UserSession(array(
@@ -764,43 +746,24 @@ 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,
-      ),
-    );
-    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);
     // 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);
+    // 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.
+    chmod(DRUPAL_ROOT . '/' . $this->siteDirectory, 0777);
+
     // Override install profile in Settings to so the correct profile is used by
     // tests.
     $this->settingsSet('install_profile', $this->profile);
 
-    $this->settingsSet('cache', array());
     $this->rebuildContainer();
 
     // Restore the original Simpletest batch.
@@ -833,16 +796,6 @@ protected function setUp() {
     // 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();
-    }
-
     // 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/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/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..2598cea 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -327,9 +327,8 @@ 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'];
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';
 
