annotate core/lib/Drupal/Core/Test/TestDatabase.php @ 9:1fc0ff908d1f

Add another data file
author Chris Cannam
date Mon, 05 Feb 2018 12:34:32 +0000
parents 4c8ae668cc8c
children 129ea1e6d783
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Test;
Chris@0 4
Chris@0 5 use Drupal\Component\FileSystem\FileSystem;
Chris@0 6 use Drupal\Core\Database\ConnectionNotDefinedException;
Chris@0 7 use Drupal\Core\Database\Database;
Chris@0 8
Chris@0 9 /**
Chris@0 10 * Provides helper methods for interacting with the Simpletest database.
Chris@0 11 */
Chris@0 12 class TestDatabase {
Chris@0 13
Chris@0 14 /**
Chris@0 15 * A random number used to ensure that test fixtures are unique to each test
Chris@0 16 * method.
Chris@0 17 *
Chris@0 18 * @var int
Chris@0 19 */
Chris@0 20 protected $lockId;
Chris@0 21
Chris@0 22 /**
Chris@0 23 * The test database prefix.
Chris@0 24 *
Chris@0 25 * @var string
Chris@0 26 */
Chris@0 27 protected $databasePrefix;
Chris@0 28
Chris@0 29 /**
Chris@0 30 * Returns the database connection to the site running Simpletest.
Chris@0 31 *
Chris@0 32 * @return \Drupal\Core\Database\Connection
Chris@0 33 * The database connection to use for inserting assertions.
Chris@0 34 *
Chris@0 35 * @see \Drupal\simpletest\TestBase::prepareEnvironment()
Chris@0 36 */
Chris@0 37 public static function getConnection() {
Chris@0 38 // Check whether there is a test runner connection.
Chris@0 39 // @see run-tests.sh
Chris@0 40 // @todo Convert Simpletest UI runner to create + use this connection, too.
Chris@0 41 try {
Chris@0 42 $connection = Database::getConnection('default', 'test-runner');
Chris@0 43 }
Chris@0 44 catch (ConnectionNotDefinedException $e) {
Chris@0 45 // Check whether there is a backup of the original default connection.
Chris@0 46 // @see TestBase::prepareEnvironment()
Chris@0 47 try {
Chris@0 48 $connection = Database::getConnection('default', 'simpletest_original_default');
Chris@0 49 }
Chris@0 50 catch (ConnectionNotDefinedException $e) {
Chris@0 51 // If TestBase::prepareEnvironment() or TestBase::restoreEnvironment()
Chris@0 52 // failed, the test-specific database connection does not exist
Chris@0 53 // yet/anymore, so fall back to the default of the (UI) test runner.
Chris@0 54 $connection = Database::getConnection('default', 'default');
Chris@0 55 }
Chris@0 56 }
Chris@0 57 return $connection;
Chris@0 58 }
Chris@0 59
Chris@0 60 /**
Chris@0 61 * TestDatabase constructor.
Chris@0 62 *
Chris@0 63 * @param string|null $db_prefix
Chris@0 64 * If not provided a new test lock is generated.
Chris@0 65 *
Chris@0 66 * @throws \InvalidArgumentException
Chris@0 67 * Thrown when $db_prefix does not match the regular expression.
Chris@0 68 */
Chris@0 69 public function __construct($db_prefix = NULL) {
Chris@0 70 if ($db_prefix === NULL) {
Chris@0 71 $this->lockId = $this->getTestLock();
Chris@0 72 $this->databasePrefix = 'test' . $this->lockId;
Chris@0 73 }
Chris@0 74 else {
Chris@0 75 $this->databasePrefix = $db_prefix;
Chris@0 76 // It is possible that we're running a test inside a test. In which case
Chris@0 77 // $db_prefix will be something like test12345678test90123456 and the
Chris@0 78 // generated lock ID for the running test method would be 90123456.
Chris@0 79 preg_match('/test(\d+)$/', $db_prefix, $matches);
Chris@0 80 if (!isset($matches[1])) {
Chris@0 81 throw new \InvalidArgumentException("Invalid database prefix: $db_prefix");
Chris@0 82 }
Chris@0 83 $this->lockId = $matches[1];
Chris@0 84 }
Chris@0 85 }
Chris@0 86
Chris@0 87 /**
Chris@0 88 * Gets the relative path to the test site directory.
Chris@0 89 *
Chris@0 90 * @return string
Chris@0 91 * The relative path to the test site directory.
Chris@0 92 */
Chris@0 93 public function getTestSitePath() {
Chris@0 94 return 'sites/simpletest/' . $this->lockId;
Chris@0 95 }
Chris@0 96
Chris@0 97 /**
Chris@0 98 * Gets the test database prefix.
Chris@0 99 *
Chris@0 100 * @return string
Chris@0 101 * The test database prefix.
Chris@0 102 */
Chris@0 103 public function getDatabasePrefix() {
Chris@0 104 return $this->databasePrefix;
Chris@0 105 }
Chris@0 106
Chris@0 107 /**
Chris@0 108 * Generates a unique lock ID for the test method.
Chris@0 109 *
Chris@0 110 * @return int
Chris@0 111 * The unique lock ID for the test method.
Chris@0 112 */
Chris@0 113 protected function getTestLock() {
Chris@0 114 // Ensure that the generated lock ID is not in use, which may happen when
Chris@0 115 // tests are run concurrently.
Chris@0 116 do {
Chris@0 117 $lock_id = mt_rand(10000000, 99999999);
Chris@0 118 // If we're only running with a concurrency of 1 there's no need to create
Chris@0 119 // a test lock file as there is no chance of the random number generated
Chris@0 120 // clashing.
Chris@0 121 if (getenv('RUN_TESTS_CONCURRENCY') > 1 && @symlink(__FILE__, $this->getLockFile($lock_id)) === FALSE) {
Chris@0 122 $lock_id = NULL;
Chris@0 123 }
Chris@0 124 } while ($lock_id === NULL);
Chris@0 125 return $lock_id;
Chris@0 126 }
Chris@0 127
Chris@0 128 /**
Chris@0 129 * Releases all test locks.
Chris@0 130 *
Chris@0 131 * This should only be called once all the test fixtures have been cleaned up.
Chris@0 132 */
Chris@0 133 public static function releaseAllTestLocks() {
Chris@0 134 $tmp = file_directory_os_temp();
Chris@0 135 $dir = dir($tmp);
Chris@0 136 while (($entry = $dir->read()) !== FALSE) {
Chris@0 137 if ($entry === '.' || $entry === '..') {
Chris@0 138 continue;
Chris@0 139 }
Chris@0 140 $entry_path = $tmp . '/' . $entry;
Chris@0 141 if (preg_match('/^test_\d+/', $entry) && is_link($entry_path)) {
Chris@0 142 unlink($entry_path);
Chris@0 143 }
Chris@0 144 }
Chris@0 145 }
Chris@0 146
Chris@0 147 /**
Chris@0 148 * Gets the lock file path.
Chris@0 149 *
Chris@0 150 * @param int $lock_id
Chris@0 151 * The test method lock ID.
Chris@0 152 *
Chris@0 153 * @return string
Chris@0 154 * A file path to the symbolic link that prevents the lock ID being re-used.
Chris@0 155 */
Chris@0 156 protected function getLockFile($lock_id) {
Chris@0 157 return FileSystem::getOsTemporaryDirectory() . '/test_' . $lock_id;
Chris@0 158 }
Chris@0 159
Chris@0 160 }