diff core/lib/Drupal/Core/Test/TestDatabase.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 129ea1e6d783
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/lib/Drupal/Core/Test/TestDatabase.php	Wed Nov 29 16:09:58 2017 +0000
@@ -0,0 +1,160 @@
+<?php
+
+namespace Drupal\Core\Test;
+
+use Drupal\Component\FileSystem\FileSystem;
+use Drupal\Core\Database\ConnectionNotDefinedException;
+use Drupal\Core\Database\Database;
+
+/**
+ * Provides helper methods for interacting with the Simpletest database.
+ */
+class TestDatabase {
+
+  /**
+   * A random number used to ensure that test fixtures are unique to each test
+   * method.
+   *
+   * @var int
+   */
+  protected $lockId;
+
+  /**
+   * The test database prefix.
+   *
+   * @var string
+   */
+  protected $databasePrefix;
+
+  /**
+   * Returns the database connection to the site running Simpletest.
+   *
+   * @return \Drupal\Core\Database\Connection
+   *   The database connection to use for inserting assertions.
+   *
+   * @see \Drupal\simpletest\TestBase::prepareEnvironment()
+   */
+  public static function getConnection() {
+    // Check whether there is a test runner connection.
+    // @see run-tests.sh
+    // @todo Convert Simpletest UI runner to create + use this connection, too.
+    try {
+      $connection = Database::getConnection('default', 'test-runner');
+    }
+    catch (ConnectionNotDefinedException $e) {
+      // Check whether there is a backup of the original default connection.
+      // @see TestBase::prepareEnvironment()
+      try {
+        $connection = Database::getConnection('default', 'simpletest_original_default');
+      }
+      catch (ConnectionNotDefinedException $e) {
+        // If TestBase::prepareEnvironment() or TestBase::restoreEnvironment()
+        // failed, the test-specific database connection does not exist
+        // yet/anymore, so fall back to the default of the (UI) test runner.
+        $connection = Database::getConnection('default', 'default');
+      }
+    }
+    return $connection;
+  }
+
+  /**
+   * TestDatabase constructor.
+   *
+   * @param string|null $db_prefix
+   *   If not provided a new test lock is generated.
+   *
+   * @throws \InvalidArgumentException
+   *   Thrown when $db_prefix does not match the regular expression.
+   */
+  public function __construct($db_prefix = NULL) {
+    if ($db_prefix === NULL) {
+      $this->lockId = $this->getTestLock();
+      $this->databasePrefix = 'test' . $this->lockId;
+    }
+    else {
+      $this->databasePrefix = $db_prefix;
+      // It is possible that we're running a test inside a test. In which case
+      // $db_prefix will be something like test12345678test90123456 and the
+      // generated lock ID for the running test method would be 90123456.
+      preg_match('/test(\d+)$/', $db_prefix, $matches);
+      if (!isset($matches[1])) {
+        throw new \InvalidArgumentException("Invalid database prefix: $db_prefix");
+      }
+      $this->lockId = $matches[1];
+    }
+  }
+
+  /**
+   * Gets the relative path to the test site directory.
+   *
+   * @return string
+   *   The relative path to the test site directory.
+   */
+  public function getTestSitePath() {
+    return 'sites/simpletest/' . $this->lockId;
+  }
+
+  /**
+   * Gets the test database prefix.
+   *
+   * @return string
+   *   The test database prefix.
+   */
+  public function getDatabasePrefix() {
+    return $this->databasePrefix;
+  }
+
+  /**
+   * Generates a unique lock ID for the test method.
+   *
+   * @return int
+   *   The unique lock ID for the test method.
+   */
+  protected function getTestLock() {
+    // Ensure that the generated lock ID is not in use, which may happen when
+    // tests are run concurrently.
+    do {
+      $lock_id = mt_rand(10000000, 99999999);
+      // If we're only running with a concurrency of 1 there's no need to create
+      // a test lock file as there is no chance of the random number generated
+      // clashing.
+      if (getenv('RUN_TESTS_CONCURRENCY') > 1 && @symlink(__FILE__, $this->getLockFile($lock_id)) === FALSE) {
+        $lock_id = NULL;
+      }
+    } while ($lock_id === NULL);
+    return $lock_id;
+  }
+
+  /**
+   * Releases all test locks.
+   *
+   * This should only be called once all the test fixtures have been cleaned up.
+   */
+  public static function releaseAllTestLocks() {
+    $tmp = file_directory_os_temp();
+    $dir = dir($tmp);
+    while (($entry = $dir->read()) !== FALSE) {
+      if ($entry === '.' || $entry === '..') {
+        continue;
+      }
+      $entry_path = $tmp . '/' . $entry;
+      if (preg_match('/^test_\d+/', $entry) && is_link($entry_path)) {
+        unlink($entry_path);
+      }
+    }
+  }
+
+  /**
+   * Gets the lock file path.
+   *
+   * @param int $lock_id
+   *   The test method lock ID.
+   *
+   * @return string
+   *   A file path to the symbolic link that prevents the lock ID being re-used.
+   */
+  protected function getLockFile($lock_id) {
+    return FileSystem::getOsTemporaryDirectory() . '/test_' . $lock_id;
+  }
+
+}