class TestDatabase
Same name in other branches
- 9 core/lib/Drupal/Core/Test/TestDatabase.php \Drupal\Core\Test\TestDatabase
- 8.9.x core/lib/Drupal/Core/Test/TestDatabase.php \Drupal\Core\Test\TestDatabase
- 11.x core/lib/Drupal/Core/Test/TestDatabase.php \Drupal\Core\Test\TestDatabase
Provides helper methods for interacting with the fixture database.
Hierarchy
- class \Drupal\Core\Test\TestDatabase
Expanded class hierarchy of TestDatabase
10 files declare their use of TestDatabase
- bootstrap.inc in core/
includes/ bootstrap.inc - Functions that need to be loaded on every Drupal request.
- DrupalKernel.php in core/
lib/ Drupal/ Core/ DrupalKernel.php - KernelTestBase.php in core/
tests/ Drupal/ KernelTests/ KernelTestBase.php - QuickStartTest.php in core/
tests/ Drupal/ Tests/ Core/ Command/ QuickStartTest.php - RecipeQuickStartTest.php in core/
tests/ Drupal/ Tests/ Core/ Recipe/ RecipeQuickStartTest.php
File
-
core/
lib/ Drupal/ Core/ Test/ TestDatabase.php, line 10
Namespace
Drupal\Core\TestView source
class TestDatabase {
/**
* The test lock ID.
*
* 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;
/**
* TestDatabase constructor.
*
* @param string|null $db_prefix
* If not provided a new test lock is generated.
* @param bool $create_lock
* (optional) Whether or not to create a lock file. Defaults to FALSE. If
* the environment variable RUN_TESTS_CONCURRENCY is greater than 1 it will
* be overridden to TRUE regardless of its initial value.
*
* @throws \InvalidArgumentException
* Thrown when $db_prefix does not match the regular expression.
*/
public function __construct($db_prefix = NULL, bool $create_lock = FALSE) {
if ($db_prefix === NULL) {
$this->lockId = $this->getTestLock($create_lock);
$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() : string {
return 'sites/simpletest/' . $this->lockId;
}
/**
* Gets the test database prefix.
*
* @return string
* The test database prefix.
*/
public function getDatabasePrefix() : string {
return $this->databasePrefix;
}
/**
* Generates a unique lock ID for the test method.
*
* @param bool $create_lock
* (optional) Whether or not to create a lock file. Defaults to FALSE.
*
* @return int
* The unique lock ID for the test method.
*/
protected function getTestLock(bool $create_lock = FALSE) : int {
// There is a risk that the generated random number is a duplicate. This
// would cause different tests to try to use the same database prefix.
// Therefore, if running with a concurrency of greater than 1, we need to
// create a lock.
if (getenv('RUN_TESTS_CONCURRENCY') > 1) {
$create_lock = TRUE;
}
do {
$lock_id = mt_rand(10000000, 99999999);
if ($create_lock && @symlink(__FILE__, $this->getLockFile($lock_id)) === FALSE) {
// If we can't create a symlink, the lock ID is in use. Generate another
// one. Symlinks are used because they are atomic and reliable.
$lock_id = NULL;
}
} while ($lock_id === NULL);
return $lock_id;
}
/**
* Releases a lock.
*
* @return bool
* TRUE if successful, FALSE if not.
*/
public function releaseLock() : bool {
return unlink($this->getLockFile($this->lockId));
}
/**
* Releases all test locks.
*
* This should only be called once all the test fixtures have been cleaned up.
*/
public static function releaseAllTestLocks() : void {
$tmp = FileSystem::getOsTemporaryDirectory();
$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(int $lock_id) : string {
return FileSystem::getOsTemporaryDirectory() . '/test_' . $lock_id;
}
/**
* Gets the file path of the PHP error log of the test.
*
* @return string
* The relative path to the test site PHP error log file.
*/
public function getPhpErrorLogPath() : string {
return $this->getTestSitePath() . '/error.log';
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overrides |
---|---|---|---|---|
TestDatabase::$databasePrefix | protected | property | The test database prefix. | |
TestDatabase::$lockId | protected | property | The test lock ID. | |
TestDatabase::getDatabasePrefix | public | function | Gets the test database prefix. | |
TestDatabase::getLockFile | protected | function | Gets the lock file path. | |
TestDatabase::getPhpErrorLogPath | public | function | Gets the file path of the PHP error log of the test. | |
TestDatabase::getTestLock | protected | function | Generates a unique lock ID for the test method. | 1 |
TestDatabase::getTestSitePath | public | function | Gets the relative path to the test site directory. | |
TestDatabase::releaseAllTestLocks | public static | function | Releases all test locks. | |
TestDatabase::releaseLock | public | function | Releases a lock. | |
TestDatabase::__construct | public | function | TestDatabase constructor. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.