/** * @return array */ public function getDatabaseTypes() { $this->loadLegacyFile('/core/includes/install.inc'); $this->setMinimalContainerPreKernel(); $finder = new Finder(); $finder->directories()->in($this->appRoot . '/core/lib/Drupal/Core/Database/Driver')->depth('== 0'); $databases = []; foreach ($finder as $driver_folder) { if (file_exists($driver_folder->getRealpath() . '/Install/Tasks.php')) { $driver = $driver_folder->getBasename(); $installer = db_installer_object($driver); // Verify is database is installable if ($installer->installable()) { $reflection = new \ReflectionClass($installer); $install_namespace = $reflection->getNamespaceName(); // Cut the trailing \Install from namespace. $driver_class = substr($install_namespace, 0, strrpos($install_namespace, '\\')); $databases[$driver] = ['namespace' => $driver_class, 'name' => $installer->name()]; } } } return $databases; }
/** * {@inheritdoc} */ protected function setUp() { $this->keyValueFactory = new KeyValueMemoryFactory(); // Back up settings from TestBase::prepareEnvironment(). $settings = Settings::getAll(); // Allow for test-specific overrides. $directory = DRUPAL_ROOT . '/' . $this->siteDirectory; $settings_services_file = DRUPAL_ROOT . '/' . $this->originalSite . '/testing.services.yml'; $container_yamls = []; if (file_exists($settings_services_file)) { // Copy the testing-specific service overrides in place. $testing_services_file = $directory . '/services.yml'; copy($settings_services_file, $testing_services_file); $container_yamls[] = $testing_services_file; } $settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSite . '/settings.testing.php'; if (file_exists($settings_testing_file)) { // Copy the testing-specific settings.php overrides in place. copy($settings_testing_file, $directory . '/settings.testing.php'); } if (file_exists($directory . '/settings.testing.php')) { // Add the name of the testing class to settings.php and include the // testing specific overrides $hash_salt = Settings::getHashSalt(); $test_class = get_class($this); $container_yamls_export = Variable::export($container_yamls); $php = <<<EOD <?php \$settings['hash_salt'] = '{$hash_salt}'; \$settings['container_yamls'] = {$container_yamls_export}; \$test_class = '{$test_class}'; include DRUPAL_ROOT . '/' . \$site_path . '/settings.testing.php'; EOD; file_put_contents($directory . '/settings.php', $php); } // Add this test class as a service provider. // @todo Remove the indirection; implement ServiceProviderInterface instead. $GLOBALS['conf']['container_service_providers']['TestServiceProvider'] = 'Drupal\\simpletest\\TestServiceProvider'; // Bootstrap a new kernel. $class_loader = (require DRUPAL_ROOT . '/autoload.php'); $this->kernel = new DrupalKernel('testing', $class_loader, FALSE); $request = Request::create('/'); $site_path = DrupalKernel::findSitePath($request); $this->kernel->setSitePath($site_path); if (file_exists($directory . '/settings.testing.php')) { Settings::initialize(DRUPAL_ROOT, $site_path, $class_loader); } $this->kernel->boot(); // Ensure database install tasks have been run. require_once __DIR__ . '/../../../includes/install.inc'; $connection = Database::getConnection(); $errors = db_installer_object($connection->driver())->runTasks(); if (!empty($errors)) { $this->fail('Failed to run installer database tasks: ' . implode(', ', $errors)); } // Reboot the kernel because the container might contain a connection to the // database that has been closed during the database install tasks. This // prevents any services created during the first boot from having stale // database connections, for example, \Drupal\Core\Config\DatabaseStorage. $this->kernel->shutdown(); $this->kernel->boot(); // Save the original site directory path, so that extensions in the // site-specific directory can still be discovered in the test site // environment. // @see \Drupal\Core\Extension\ExtensionDiscovery::scan() $settings['test_parent_site'] = $this->originalSite; // Restore and merge settings. // DrupalKernel::boot() initializes new Settings, and the containerBuild() // method sets additional settings. new Settings($settings + Settings::getAll()); // Create and set new configuration directories. $this->prepareConfigDirectories(); // Set the request scope. $this->container = $this->kernel->getContainer(); $this->container->get('request_stack')->push($request); // Re-inject extension file listings into state, unless the key/value // service was overridden (in which case its storage does not exist yet). if ($this->container->get('keyvalue') instanceof KeyValueMemoryFactory) { $this->container->get('state')->set('system.module.files', $this->moduleFiles); $this->container->get('state')->set('system.theme.files', $this->themeFiles); } // Create a minimal core.extension configuration object so that the list of // enabled modules can be maintained allowing // \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work. // Write directly to active storage to avoid early instantiation of // the event dispatcher which can prevent modules from registering events. \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array())); // Collect and set a fixed module list. $class = get_class($this); $modules = array(); while ($class) { if (property_exists($class, 'modules')) { // Only add the modules, if the $modules property was not inherited. $rp = new \ReflectionProperty($class, 'modules'); if ($rp->class == $class) { $modules[$class] = $class::$modules; } } $class = get_parent_class($class); } // Modules have been collected in reverse class hierarchy order; modules // defined by base classes should be sorted first. Then, merge the results // together. $modules = array_reverse($modules); $modules = call_user_func_array('array_merge_recursive', $modules); if ($modules) { $this->enableModules($modules); } // Tests based on this class are entitled to use Drupal's File and // StreamWrapper APIs. // @todo Move StreamWrapper management into DrupalKernel. // @see https://www.drupal.org/node/2028109 file_prepare_directory($this->publicFilesDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); $this->settingsSet('file_public_path', $this->publicFilesDirectory); $this->streamWrappers = array(); $this->registerStreamWrapper('public', 'Drupal\\Core\\StreamWrapper\\PublicStream'); // The temporary stream wrapper is able to operate both with and without // configuration. $this->registerStreamWrapper('temporary', 'Drupal\\Core\\StreamWrapper\\TemporaryStream'); }
/** * Bootstraps a kernel for a test. */ private function bootKernel() { $this->setSetting('container_yamls', []); // Allow for test-specific overrides. $settings_services_file = $this->root . '/sites/default' . '/testing.services.yml'; if (file_exists($settings_services_file)) { // Copy the testing-specific service overrides in place. $testing_services_file = $this->root . '/' . $this->siteDirectory . '/services.yml'; copy($settings_services_file, $testing_services_file); $this->setSetting('container_yamls', [$testing_services_file]); } // Allow for global test environment overrides. if (file_exists($test_env = $this->root . '/sites/default/testing.services.yml')) { $GLOBALS['conf']['container_yamls']['testing'] = $test_env; } // Add this test class as a service provider. $GLOBALS['conf']['container_service_providers']['test'] = $this; $modules = self::getModulesToEnable(get_class($this)); // Prepare a precompiled container for all tests of this class. // Substantially improves performance, since ContainerBuilder::compile() // is very expensive. Encourages testing best practices (small tests). // Normally a setUpBeforeClass() operation, but object scope is required to // inject $this test class instance as a service provider (see above). $rc = new \ReflectionClass(get_class($this)); $test_method_count = count(array_filter($rc->getMethods(), function ($method) { // PHPUnit's @test annotations are intentionally ignored/not supported. return strpos($method->getName(), 'test') === 0; })); if ($test_method_count > 1 && !$this->isTestInIsolation()) { // Clone a precompiled, empty ContainerBuilder instance for each test. $container = $this->getCompiledContainerBuilder($modules); } // Bootstrap the kernel. Do not use createFromRequest() to retain Settings. $kernel = new DrupalKernel('testing', $this->classLoader, FALSE); $kernel->setSitePath($this->siteDirectory); // Boot the precompiled container. The kernel will enhance it with synthetic // services. if (isset($container)) { $kernel->setContainer($container); unset($container); } elseif ($modules && ($extensions = $this->getExtensionsForModules($modules))) { $kernel->updateModules($extensions, $extensions); } // DrupalKernel::boot() is not sufficient as it does not invoke preHandle(), // which is required to initialize legacy global variables. $request = Request::create('/'); $kernel->prepareLegacyRequest($request); // register() is only called if a new container was built/compiled. $this->container = $kernel->getContainer(); // Ensure database tasks have been run. require_once __DIR__ . '/../../../includes/install.inc'; $connection = Database::getConnection(); $errors = db_installer_object($connection->driver())->runTasks(); if (!empty($errors)) { $this->fail('Failed to run installer database tasks: ' . implode(', ', $errors)); } if ($modules) { $this->container->get('module_handler')->loadAll(); } $this->container->get('request_stack')->push($request); // Setup the destion to the be frontpage by default. \Drupal::destination()->set('/'); // Write the core.extension configuration. // Required for ConfigInstaller::installDefaultConfig() to work. $this->container->get('config.storage')->write('core.extension', array('module' => array_fill_keys($modules, 0), 'theme' => array())); $settings = Settings::getAll(); $settings['php_storage']['default'] = ['class' => '\\Drupal\\Component\\PhpStorage\\FileStorage']; new Settings($settings); // Manually configure the test mail collector implementation to prevent // tests from sending out emails and collect them in state instead. // While this should be enforced via settings.php prior to installation, // some tests expect to be able to test mail system implementations. $GLOBALS['config']['system.mail']['interface']['default'] = 'test_mail_collector'; }
/** * Runs the install database tasks for the driver used by the test runner. */ protected function runDbTasks() { // Create a minimal container so that t() works. // @see install_begin_request() $container = new ContainerBuilder(); $container->setParameter('language.default_values', Language::$defaultValues); $container->register('language.default', 'Drupal\\Core\\Language\\LanguageDefault')->addArgument('%language.default_values%'); $container->register('language_manager', 'Drupal\\Core\\Language\\LanguageManager')->addArgument(new Reference('language.default')); $container->register('string_translation', 'Drupal\\Core\\StringTranslation\\TranslationManager')->addArgument(new Reference('language_manager')); \Drupal::setContainer($container); require_once __DIR__ . '/../../../../../includes/install.inc'; $connection = Database::getConnection(); $errors = db_installer_object($connection->driver())->runTasks(); if (!empty($errors)) { $this->fail('Failed to run installer database tasks: ' . implode(', ', $errors)); } }