Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / tests / Drupal / TestSite / Commands / TestSiteTearDownCommand.php
diff --git a/web/core/tests/Drupal/TestSite/Commands/TestSiteTearDownCommand.php b/web/core/tests/Drupal/TestSite/Commands/TestSiteTearDownCommand.php
new file mode 100644 (file)
index 0000000..2b65d1c
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+
+namespace Drupal\TestSite\Commands;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Test\TestDatabase;
+use Drupal\Tests\BrowserTestBase;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+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 tear down a test Drupal site.
+ *
+ * @internal
+ */
+class TestSiteTearDownCommand extends Command {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this->setName('tear-down')
+      ->setDescription('Removes a test site added by the install command')
+      ->setHelp('All the database tables and files will be removed.')
+      ->addArgument('db-prefix', InputArgument::REQUIRED, 'The database prefix for the test site.')
+      ->addOption('db-url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database. Defaults to the environment variable SIMPLETEST_DB.', getenv('SIMPLETEST_DB'))
+      ->addOption('keep-lock', NULL, InputOption::VALUE_NONE, 'Keeps the database prefix lock. Useful for ensuring test isolation when running concurrent tests.')
+      ->addUsage('test12345678')
+      ->addUsage('test12345678 --db-url "mysql://username:password@localhost/databasename#table_prefix"')
+      ->addUsage('test12345678 --keep-lock');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $db_prefix = $input->getArgument('db-prefix');
+    // Validate the db_prefix argument.
+    try {
+      $test_database = new TestDatabase($db_prefix);
+    }
+    catch (\InvalidArgumentException $e) {
+      $io = new SymfonyStyle($input, $output);
+      $io->getErrorStyle()->error("Invalid database prefix: $db_prefix\n\nValid database prefixes match the regular expression '/test(\d+)$/'. For example, 'test12345678'.");
+      // Display the synopsis of the command like Composer does.
+      $output->writeln(sprintf('<info>%s</info>', sprintf($this->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
+      return 1;
+    }
+
+    $db_url = $input->getOption('db-url');
+    putenv("SIMPLETEST_DB=$db_url");
+
+    // Handle the cleanup of the test site.
+    $this->tearDown($test_database, $db_url);
+
+    // Release the test database prefix lock.
+    if (!$input->getOption('keep-lock')) {
+      $test_database->releaseLock();
+    }
+
+    $output->writeln("<info>Successfully uninstalled $db_prefix test site</info>");
+  }
+
+  /**
+   * Removes a given instance by deleting all the database tables and files.
+   *
+   * @param \Drupal\Core\Test\TestDatabase $test_database
+   *   The test database object.
+   * @param string $db_url
+   *   The database URL.
+   *
+   * @see \Drupal\Tests\BrowserTestBase::cleanupEnvironment()
+   */
+  protected function tearDown(TestDatabase $test_database, $db_url) {
+    // Connect to the test database.
+    $root = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
+    $database = Database::convertDbUrlToConnectionInfo($db_url, $root);
+    $database['prefix'] = ['default' => $test_database->getDatabasePrefix()];
+    Database::addConnectionInfo(__CLASS__, 'default', $database);
+
+    // Remove all the tables.
+    $schema = Database::getConnection('default', __CLASS__)->schema();
+    $tables = $schema->findTables('%');
+    array_walk($tables, [$schema, 'dropTable']);
+
+    // Delete test site directory.
+    $this->fileUnmanagedDeleteRecursive($root . DIRECTORY_SEPARATOR . $test_database->getTestSitePath(), [BrowserTestBase::class, 'filePreDeleteCallback']);
+  }
+
+  /**
+   * Deletes all files and directories in the specified path recursively.
+   *
+   * Note this method has no dependencies on Drupal core to ensure that the
+   * test site can be torn down even if something in the test site is broken.
+   *
+   * @param string $path
+   *   A string containing either an URI or a file or directory path.
+   * @param callable $callback
+   *   (optional) Callback function to run on each file prior to deleting it and
+   *   on each directory prior to traversing it. For example, can be used to
+   *   modify permissions.
+   *
+   * @return bool
+   *   TRUE for success or if path does not exist, FALSE in the event of an
+   *   error.
+   *
+   * @see file_unmanaged_delete_recursive()
+   */
+  protected function fileUnmanagedDeleteRecursive($path, $callback = NULL) {
+    if (isset($callback)) {
+      call_user_func($callback, $path);
+    }
+    if (is_dir($path)) {
+      $dir = dir($path);
+      while (($entry = $dir->read()) !== FALSE) {
+        if ($entry == '.' || $entry == '..') {
+          continue;
+        }
+        $entry_path = $path . '/' . $entry;
+        $this->fileUnmanagedDeleteRecursive($entry_path, $callback);
+      }
+      $dir->close();
+
+      return rmdir($path);
+    }
+    return unlink($path);
+  }
+
+}