diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 217d4dc..9ff23cd 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -1038,7 +1038,7 @@ public static function bootEnvironment($app_root = NULL) { * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException * In case the host name in the request is not trusted. */ - protected function initializeSettings(Request $request) { + public function initializeSettings(Request $request) { $site_path = static::findSitePath($request); $this->setSitePath($site_path); $class_loader_class = get_class($this->classLoader); diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 2324316..2c5f6fa 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -12,6 +12,7 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Database\Database; +use Drupal\Core\DrupalKernel; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AnonymousUserSession; use Drupal\Core\Test\FunctionalTestSetupTrait; @@ -27,6 +28,7 @@ use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Symfony\Component\CssSelector\CssSelectorConverter; +use Symfony\Component\HttpFoundation\Request; /** * Provides a test case for functional Drupal tests. @@ -257,6 +259,13 @@ protected $metaRefreshCount = 0; /** + * Path to SQL dump file. + * + * @var + */ + protected $dumpFile; + + /** * The app root. * * @var string @@ -970,6 +979,44 @@ protected function getOptions($select, Element $container = NULL) { * Installs Drupal into the Simpletest site. */ public function installDrupal() { + if (getenv('BROWSERTEST_CACHE_DB')) { + $this->initDumpFile(); + if (file_exists($this->dumpFile)) { + $this->installDrupalFromDump(); + } + else { + $this->installDrupalFromProfile(); + $this->dumpDatabase(); + } + } + else { + $this->installDrupalFromProfile(); + } + } + + + /** + * Determines a proper file name for SQL dump. + */ + protected function initDumpFile() { + $class = get_class($this); + $modules = []; + while ($class) { + if (property_exists($class, 'modules')) { + $modules = array_merge($modules, $class::$modules); + } + $class = get_parent_class($class); + } + sort($modules); + $cache_dir = getenv('BROWSERTEST_CACHE_DIR') ?: sys_get_temp_dir() . '/test_dumps/' . \Drupal::VERSION; + is_dir($cache_dir) || mkdir($cache_dir, 0777, TRUE); + $this->dumpFile = $cache_dir . '/_' . implode('-', $modules) . '.sql'; + } + + /** + * Installs Drupal using installation profile. + */ + protected function installDrupalFromProfile() { $this->initUserSession(); $this->prepareSettings(); $this->doInstall(); @@ -981,6 +1028,94 @@ public function installDrupal() { } /** + * Installs Drupal using SQL dump. + */ + protected function installDrupalFromDump() { + + $this->restoreDatabase(); + + $this->initUserSession(); + $this->prepareSettings(); + + $connection_info = Database::getConnectionInfo('default'); + + $databases['default']['default'] = (object) [ + 'value' => $connection_info['default'], + 'required' => TRUE, + ]; + + $settings['databases'] = $databases; + $settings['settings']['hash_salt'] = (object) [ + 'value' => $this->databasePrefix, + 'required' => TRUE, + ]; + + $settings['settings']['hash_salt'] = (object) [ + 'value' => $this->databasePrefix, + 'required' => TRUE, + ]; + $this->writeSettings($settings); + + $autoloader = require './autoload.php'; + $this->kernel = new DrupalKernel('prod', $autoloader); + DrupalKernel::bootEnvironment(); + $request = Request::createFromGlobals(); + $this->kernel->initializeSettings($request); + $this->kernel->boot()->preHandle($request); + $this->container = $this->kernel->getContainer(); + + // The value comes with the dump from previous installation. + \Drupal::configFactory()->getEditable('system.file') + ->set('path.temporary', $this->tempFilesDirectory) + ->save(); + file_prepare_directory($this->tempFilesDirectory, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY); + } + + /** + * Dumps database structure and contents of test site. + */ + protected function dumpDatabase() { + $connection_info = Database::getConnectionInfo('default'); + + $user = $connection_info['default']['username']; + $pass = $connection_info['default']['password']; + $db = $connection_info['default']['database']; + + switch ($connection_info['default']['driver']) { + case 'mysql': + $tables = \Drupal::database() + ->query("SHOW TABLES LIKE '$this->databasePrefix%'") + ->fetchCol(); + $tables_param = implode(' ', $tables); + exec("mysqldump -u$user -p$pass $db $tables_param | sed 's/$this->databasePrefix/default_db_prefix_/' > {$this->dumpFile}"); + break; + + default: + throw new \LogicException('This database driver is not supported yet.'); + } + } + + /** + * Restores database structure and contents of test site. + */ + protected function restoreDatabase() { + $connection_info = Database::getConnectionInfo('default'); + + $user = $connection_info['default']['username']; + $pass = $connection_info['default']['password']; + $db = $connection_info['default']['database']; + + switch ($connection_info['default']['driver']) { + case 'mysql': + exec("sed 's/default_db_prefix_/$this->databasePrefix/' $this->dumpFile | mysql -u$user -p$pass $db"); + break; + + default: + throw new \LogicException('This database driver is not supported yet.'); + } + } + + /** * Returns whether a given user account is logged in. * * @param \Drupal\Core\Session\AccountInterface $account