123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <?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\TestPreinstallInterface;
- 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/TestSiteMultilingualInstallTestScript.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/TestSiteMultilingualInstallTestScript.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],
- ]);
- }
- return 0;
- }
- /**
- * 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 or
- * \Drupal\TestSite\TestPreinstallInterface.
- */
- 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) && !is_subclass_of($class, TestPreinstallInterface::class)) {
- throw new \InvalidArgumentException("The class $class contained in $file needs to implement \Drupal\TestSite\TestSetupInterface or \Drupal\TestSite\TestPreinstallInterface");
- }
- 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->executePreinstallClass($setup_class);
- $this->installDrupal();
- $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);
- }
- /**
- * 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) {
- if (is_subclass_of($class, TestSetupInterface::class)) {
- /** @var \Drupal\TestSite\TestSetupInterface $instance */
- $instance = new $class();
- $instance->setup();
- }
- }
- /**
- * Uses the setup file to configure the environment prior to install.
- *
- * @param string $class
- * The fully qualified class name, which should set up the environment prior
- * to installing Drupal for tests. For example this class could create
- * translations that are used during the installer.
- *
- * @see \Drupal\TestSite\TestPreinstallInterface
- */
- protected function executePreinstallClass($class) {
- if (is_subclass_of($class, TestPreinstallInterface::class)) {
- /** @var \Drupal\TestSite\TestPreinstallInterface $instance */
- $instance = new $class();
- $instance->preinstall($this->databasePrefix, $this->siteDirectory);
- }
- }
- /**
- * {@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();
- }
- }
|