Chris@0: lockId = $this->getTestLock(); Chris@0: $this->databasePrefix = 'test' . $this->lockId; Chris@0: } Chris@0: else { Chris@0: $this->databasePrefix = $db_prefix; Chris@0: // It is possible that we're running a test inside a test. In which case Chris@0: // $db_prefix will be something like test12345678test90123456 and the Chris@0: // generated lock ID for the running test method would be 90123456. Chris@0: preg_match('/test(\d+)$/', $db_prefix, $matches); Chris@0: if (!isset($matches[1])) { Chris@0: throw new \InvalidArgumentException("Invalid database prefix: $db_prefix"); Chris@0: } Chris@0: $this->lockId = $matches[1]; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the relative path to the test site directory. Chris@0: * Chris@0: * @return string Chris@0: * The relative path to the test site directory. Chris@0: */ Chris@0: public function getTestSitePath() { Chris@0: return 'sites/simpletest/' . $this->lockId; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the test database prefix. Chris@0: * Chris@0: * @return string Chris@0: * The test database prefix. Chris@0: */ Chris@0: public function getDatabasePrefix() { Chris@0: return $this->databasePrefix; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Generates a unique lock ID for the test method. Chris@0: * Chris@0: * @return int Chris@0: * The unique lock ID for the test method. Chris@0: */ Chris@0: protected function getTestLock() { Chris@0: // Ensure that the generated lock ID is not in use, which may happen when Chris@0: // tests are run concurrently. Chris@0: do { Chris@0: $lock_id = mt_rand(10000000, 99999999); Chris@0: // If we're only running with a concurrency of 1 there's no need to create Chris@0: // a test lock file as there is no chance of the random number generated Chris@0: // clashing. Chris@0: if (getenv('RUN_TESTS_CONCURRENCY') > 1 && @symlink(__FILE__, $this->getLockFile($lock_id)) === FALSE) { Chris@0: $lock_id = NULL; Chris@0: } Chris@0: } while ($lock_id === NULL); Chris@0: return $lock_id; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Releases all test locks. Chris@0: * Chris@0: * This should only be called once all the test fixtures have been cleaned up. Chris@0: */ Chris@0: public static function releaseAllTestLocks() { Chris@0: $tmp = file_directory_os_temp(); Chris@0: $dir = dir($tmp); Chris@0: while (($entry = $dir->read()) !== FALSE) { Chris@0: if ($entry === '.' || $entry === '..') { Chris@0: continue; Chris@0: } Chris@0: $entry_path = $tmp . '/' . $entry; Chris@0: if (preg_match('/^test_\d+/', $entry) && is_link($entry_path)) { Chris@0: unlink($entry_path); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the lock file path. Chris@0: * Chris@0: * @param int $lock_id Chris@0: * The test method lock ID. Chris@0: * Chris@0: * @return string Chris@0: * A file path to the symbolic link that prevents the lock ID being re-used. Chris@0: */ Chris@0: protected function getLockFile($lock_id) { Chris@0: return FileSystem::getOsTemporaryDirectory() . '/test_' . $lock_id; Chris@0: } Chris@0: Chris@0: }