class TestDatabase

Same name in other branches
  1. 9 core/lib/Drupal/Core/Test/TestDatabase.php \Drupal\Core\Test\TestDatabase
  2. 8.9.x core/lib/Drupal/Core/Test/TestDatabase.php \Drupal\Core\Test\TestDatabase
  3. 10 core/lib/Drupal/Core/Test/TestDatabase.php \Drupal\Core\Test\TestDatabase

Provides helper methods for interacting with the fixture database.

Hierarchy

Expanded class hierarchy of TestDatabase

11 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

... See full list

File

core/lib/Drupal/Core/Test/TestDatabase.php, line 10

Namespace

Drupal\Core\Test
View 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.