3 namespace Drupal\Core\Test;
5 use Drupal\Component\FileSystem\FileSystem;
6 use Drupal\Core\Database\ConnectionNotDefinedException;
7 use Drupal\Core\Database\Database;
10 * Provides helper methods for interacting with the Simpletest database.
15 * A random number used to ensure that test fixtures are unique to each test
23 * The test database prefix.
27 protected $databasePrefix;
30 * Returns the database connection to the site running Simpletest.
32 * @return \Drupal\Core\Database\Connection
33 * The database connection to use for inserting assertions.
35 * @see \Drupal\simpletest\TestBase::prepareEnvironment()
37 public static function getConnection() {
38 // Check whether there is a test runner connection.
40 // @todo Convert Simpletest UI runner to create + use this connection, too.
42 $connection = Database::getConnection('default', 'test-runner');
44 catch (ConnectionNotDefinedException $e) {
45 // Check whether there is a backup of the original default connection.
46 // @see TestBase::prepareEnvironment()
48 $connection = Database::getConnection('default', 'simpletest_original_default');
50 catch (ConnectionNotDefinedException $e) {
51 // If TestBase::prepareEnvironment() or TestBase::restoreEnvironment()
52 // failed, the test-specific database connection does not exist
53 // yet/anymore, so fall back to the default of the (UI) test runner.
54 $connection = Database::getConnection('default', 'default');
61 * TestDatabase constructor.
63 * @param string|null $db_prefix
64 * If not provided a new test lock is generated.
65 * @param bool $create_lock
66 * (optional) Whether or not to create a lock file. Defaults to FALSE. If
67 * the environment variable RUN_TESTS_CONCURRENCY is greater than 1 it will
68 * be overridden to TRUE regardless of its initial value.
70 * @throws \InvalidArgumentException
71 * Thrown when $db_prefix does not match the regular expression.
73 public function __construct($db_prefix = NULL, $create_lock = FALSE) {
74 if ($db_prefix === NULL) {
75 $this->lockId = $this->getTestLock($create_lock);
76 $this->databasePrefix = 'test' . $this->lockId;
79 $this->databasePrefix = $db_prefix;
80 // It is possible that we're running a test inside a test. In which case
81 // $db_prefix will be something like test12345678test90123456 and the
82 // generated lock ID for the running test method would be 90123456.
83 preg_match('/test(\d+)$/', $db_prefix, $matches);
84 if (!isset($matches[1])) {
85 throw new \InvalidArgumentException("Invalid database prefix: $db_prefix");
87 $this->lockId = $matches[1];
92 * Gets the relative path to the test site directory.
95 * The relative path to the test site directory.
97 public function getTestSitePath() {
98 return 'sites/simpletest/' . $this->lockId;
102 * Gets the test database prefix.
105 * The test database prefix.
107 public function getDatabasePrefix() {
108 return $this->databasePrefix;
112 * Generates a unique lock ID for the test method.
114 * @param bool $create_lock
115 * (optional) Whether or not to create a lock file. Defaults to FALSE.
118 * The unique lock ID for the test method.
120 protected function getTestLock($create_lock = FALSE) {
121 // There is a risk that the generated random number is a duplicate. This
122 // would cause different tests to try to use the same database prefix.
123 // Therefore, if running with a concurrency of greater than 1, we need to
125 if (getenv('RUN_TESTS_CONCURRENCY') > 1) {
130 $lock_id = mt_rand(10000000, 99999999);
131 if ($create_lock && @symlink(__FILE__, $this->getLockFile($lock_id)) === FALSE) {
132 // If we can't create a symlink, the lock ID is in use. Generate another
133 // one. Symlinks are used because they are atomic and reliable.
136 } while ($lock_id === NULL);
144 * TRUE if successful, FALSE if not.
146 public function releaseLock() {
147 return unlink($this->getLockFile($this->lockId));
151 * Releases all test locks.
153 * This should only be called once all the test fixtures have been cleaned up.
155 public static function releaseAllTestLocks() {
156 $tmp = FileSystem::getOsTemporaryDirectory();
158 while (($entry = $dir->read()) !== FALSE) {
159 if ($entry === '.' || $entry === '..') {
162 $entry_path = $tmp . '/' . $entry;
163 if (preg_match('/^test_\d+/', $entry) && is_link($entry_path)) {
170 * Gets the lock file path.
172 * @param int $lock_id
173 * The test method lock ID.
176 * A file path to the symbolic link that prevents the lock ID being re-used.
178 protected function getLockFile($lock_id) {
179 return FileSystem::getOsTemporaryDirectory() . '/test_' . $lock_id;