Mercurial > hg > cmmr2012-drupal-site
diff core/tests/Drupal/TestSite/Commands/TestSiteInstallCommand.php @ 4:a9cd425dd02b
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:11:55 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/tests/Drupal/TestSite/Commands/TestSiteInstallCommand.php Thu Feb 28 13:11:55 2019 +0000 @@ -0,0 +1,249 @@ +<?php + +namespace Drupal\TestSite\Commands; + +use Drupal\Core\Database\Database; +use Drupal\Core\Test\FunctionalTestSetupTrait; +use Drupal\Core\Test\TestDatabase; +use Drupal\Core\Test\TestSetupTrait; +use Drupal\TestSite\TestSetupInterface; +use Drupal\Tests\RandomGeneratorTrait; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Command to create a test Drupal site. + * + * @internal + */ +class TestSiteInstallCommand extends Command { + + use FunctionalTestSetupTrait { + installParameters as protected installParametersTrait; + } + use RandomGeneratorTrait; + use TestSetupTrait { + changeDatabasePrefix as protected changeDatabasePrefixTrait; + } + + /** + * The install profile to use. + * + * @var string + */ + protected $profile = 'testing'; + + /** + * Time limit in seconds for the test. + * + * Used by \Drupal\Core\Test\FunctionalTestSetupTrait::prepareEnvironment(). + * + * @var int + */ + protected $timeLimit = 500; + + /** + * The database prefix of this test run. + * + * @var string + */ + protected $databasePrefix; + + /** + * The language to install the site in. + * + * @var string + */ + protected $langcode = 'en'; + + /** + * {@inheritdoc} + */ + protected function configure() { + $this->setName('install') + ->setDescription('Creates a test Drupal site') + ->setHelp('The details to connect to the test site created will be displayed upon success. It will contain the database prefix and the user agent.') + ->addOption('setup-file', NULL, InputOption::VALUE_OPTIONAL, 'The path to a PHP file containing a class to setup configuration used by the test, for example, core/tests/Drupal/TestSite/TestSiteInstallTestScript.php.') + ->addOption('db-url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database. Defaults to the environment variable SIMPLETEST_DB.', getenv('SIMPLETEST_DB')) + ->addOption('base-url', NULL, InputOption::VALUE_OPTIONAL, 'Base URL for site under test. Defaults to the environment variable SIMPLETEST_BASE_URL.', getenv('SIMPLETEST_BASE_URL')) + ->addOption('install-profile', NULL, InputOption::VALUE_OPTIONAL, 'Install profile to install the site in. Defaults to testing.', 'testing') + ->addOption('langcode', NULL, InputOption::VALUE_OPTIONAL, 'The language to install the site in. Defaults to en.', 'en') + ->addOption('json', NULL, InputOption::VALUE_NONE, 'Output test site connection details in JSON.') + ->addUsage('--setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --json') + ->addUsage('--install-profile demo_umami --langcode fr') + ->addUsage('--base-url "http://example.com" --db-url "mysql://username:password@localhost/databasename#table_prefix"'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + // Determines and validates the setup class prior to installing a database + // to avoid creating unnecessary sites. + $root = dirname(dirname(dirname(dirname(dirname(__DIR__))))); + chdir($root); + $class_name = $this->getSetupClass($input->getOption('setup-file')); + // Ensure we can install a site in the sites/simpletest directory. + $this->ensureDirectory($root); + + $db_url = $input->getOption('db-url'); + $base_url = $input->getOption('base-url'); + putenv("SIMPLETEST_DB=$db_url"); + putenv("SIMPLETEST_BASE_URL=$base_url"); + + // Manage site fixture. + $this->setup($input->getOption('install-profile'), $class_name, $input->getOption('langcode')); + + $user_agent = drupal_generate_test_ua($this->databasePrefix); + if ($input->getOption('json')) { + $output->writeln(json_encode([ + 'db_prefix' => $this->databasePrefix, + 'user_agent' => $user_agent, + 'site_path' => $this->siteDirectory, + ])); + } + else { + $output->writeln('<info>Successfully installed a test site</info>'); + $io = new SymfonyStyle($input, $output); + $io->table([], [ + ['Database prefix', $this->databasePrefix], + ['User agent', $user_agent], + ['Site path', $this->siteDirectory], + ]); + } + } + + /** + * Gets the setup class. + * + * @param string|null $file + * The file to get the setup class from. + * + * @return string|null + * The setup class contained in the provided $file. + * + * @throws \InvalidArgumentException + * Thrown if the file does not exist, does not contain a class or the class + * does not implement \Drupal\TestSite\TestSetupInterface. + */ + protected function getSetupClass($file) { + if ($file === NULL) { + return; + } + if (!file_exists($file)) { + throw new \InvalidArgumentException("The file $file does not exist."); + } + + $classes = get_declared_classes(); + include_once $file; + $new_classes = array_values(array_diff(get_declared_classes(), $classes)); + if (empty($new_classes)) { + throw new \InvalidArgumentException("The file $file does not contain a class."); + } + $class = array_pop($new_classes); + + if (!is_subclass_of($class, TestSetupInterface::class)) { + throw new \InvalidArgumentException("The class $class contained in $file needs to implement \Drupal\TestSite\TestSetupInterface"); + } + return $class; + } + + /** + * Ensures that the sites/simpletest directory exists and is writable. + * + * @param string $root + * The Drupal root. + */ + protected function ensureDirectory($root) { + if (!is_writable($root . '/sites/simpletest')) { + if (!@mkdir($root . '/sites/simpletest')) { + throw new \RuntimeException($root . '/sites/simpletest must exist and be writable to install a test site'); + } + } + } + + /** + * Creates a test drupal installation. + * + * @param string $profile + * (optional) The installation profile to use. + * @param string $setup_class + * (optional) Setup class. A PHP class to setup configuration used by the + * test. + * @param string $langcode + * (optional) The language to install the site in. + */ + public function setup($profile = 'testing', $setup_class = NULL, $langcode = 'en') { + $this->profile = $profile; + $this->langcode = $langcode; + $this->setupBaseUrl(); + $this->prepareEnvironment(); + $this->installDrupal(); + + if ($setup_class) { + $this->executeSetupClass($setup_class); + } + } + + /** + * Installs Drupal into the test site. + */ + protected function installDrupal() { + $this->initUserSession(); + $this->prepareSettings(); + $this->doInstall(); + $this->initSettings(); + $container = $this->initKernel(\Drupal::request()); + $this->initConfig($container); + $this->installModulesFromClassProperty($container); + $this->rebuildAll(); + } + + /** + * Uses the setup file to configure Drupal. + * + * @param string $class + * The fully qualified class name, which should set up Drupal for tests. For + * example this class could create content types and fields or install + * modules. The class needs to implement TestSetupInterface. + * + * @see \Drupal\TestSite\TestSetupInterface + */ + protected function executeSetupClass($class) { + /** @var \Drupal\TestSite\TestSetupInterface $instance */ + $instance = new $class(); + $instance->setup(); + } + + /** + * {@inheritdoc} + */ + protected function installParameters() { + $parameters = $this->installParametersTrait(); + $parameters['parameters']['langcode'] = $this->langcode; + return $parameters; + } + + /** + * {@inheritdoc} + */ + protected function changeDatabasePrefix() { + // Ensure that we use the database from SIMPLETEST_DB environment variable. + Database::removeConnection('default'); + $this->changeDatabasePrefixTrait(); + } + + /** + * {@inheritdoc} + */ + protected function prepareDatabasePrefix() { + // Override this method so that we can force a lock to be created. + $test_db = new TestDatabase(NULL, TRUE); + $this->siteDirectory = $test_db->getTestSitePath(); + $this->databasePrefix = $test_db->getDatabasePrefix(); + } + +}