Issue #3075608 by mondrake, Mile23, longwave: Introduce TestRun objects and refactor TestDatabase to be testable
parent
237373bca0
commit
f711435482
|
@ -8,75 +8,38 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
|
||||
/**
|
||||
* Helper class for cleaning test environments.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
||||
|
||||
/**
|
||||
* Path to Drupal root directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Connection to the database being used for tests.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $testDatabase;
|
||||
|
||||
/**
|
||||
* Connection to the database where test results are stored.
|
||||
*
|
||||
* This could be the same as $testDatabase, or it could be different.
|
||||
* run-tests.sh allows you to specify a different results database with the
|
||||
* --sqlite parameter.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $resultsDatabase;
|
||||
|
||||
/**
|
||||
* The file system service.
|
||||
*
|
||||
* @var \Drupal\Core\File\FileSystemInterface
|
||||
*/
|
||||
protected $fileSystem;
|
||||
|
||||
/**
|
||||
* Console output.
|
||||
*
|
||||
* @var \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* Construct an environment cleaner.
|
||||
* Constructs a test environment cleaner.
|
||||
*
|
||||
* @param string $root
|
||||
* The path to the root of the Drupal installation.
|
||||
* @param \Drupal\Core\Database\Connection $test_database
|
||||
* @param \Drupal\Core\Database\Connection $testDatabase
|
||||
* Connection to the database against which tests were run.
|
||||
* @param \Drupal\Core\Database\Connection $results_database
|
||||
* Connection to the database where test results were stored. This could be
|
||||
* the same as $test_database, or it could be different.
|
||||
* @param \Drupal\Core\Test\TestRunResultsStorageInterface $testRunResultsStorage
|
||||
* The test run results storage.
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* A symfony console output object.
|
||||
* @param \Drupal\Core\File\FileSystemInterface $file_system
|
||||
* The file_system service.
|
||||
* A Symfony console output object.
|
||||
* @param \Drupal\Core\File\FileSystemInterface $fileSystem
|
||||
* Drupal's file_system service.
|
||||
*/
|
||||
public function __construct($root, Connection $test_database, Connection $results_database, OutputInterface $output, FileSystemInterface $file_system) {
|
||||
$this->root = $root;
|
||||
$this->testDatabase = $test_database;
|
||||
$this->resultsDatabase = $results_database;
|
||||
$this->output = $output;
|
||||
$this->fileSystem = $file_system;
|
||||
public function __construct(
|
||||
protected string $root,
|
||||
protected Connection $testDatabase,
|
||||
protected TestRunResultsStorageInterface $testRunResultsStorage,
|
||||
protected OutputInterface $output,
|
||||
protected FileSystemInterface $fileSystem
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanEnvironment($clear_results = TRUE, $clear_temp_directories = TRUE, $clear_database = TRUE) {
|
||||
public function cleanEnvironment(bool $clear_results = TRUE, bool $clear_temp_directories = TRUE, bool $clear_database = TRUE): void {
|
||||
$count = 0;
|
||||
if ($clear_database) {
|
||||
$this->doCleanDatabase();
|
||||
|
@ -85,7 +48,7 @@ class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
|||
$this->doCleanTemporaryDirectories();
|
||||
}
|
||||
if ($clear_results) {
|
||||
$count = $this->cleanResultsTable();
|
||||
$count = $this->cleanResults();
|
||||
$this->output->write('Test results removed: ' . $count);
|
||||
}
|
||||
else {
|
||||
|
@ -96,7 +59,7 @@ class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanDatabase() {
|
||||
public function cleanDatabase(): void {
|
||||
$count = $this->doCleanDatabase();
|
||||
if ($count > 0) {
|
||||
$this->output->write('Leftover tables removed: ' . $count);
|
||||
|
@ -112,7 +75,7 @@ class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
|||
* @return int
|
||||
* The number of tables that were removed.
|
||||
*/
|
||||
protected function doCleanDatabase() {
|
||||
protected function doCleanDatabase(): int {
|
||||
/** @var \Drupal\Core\Database\Schema $schema */
|
||||
$schema = $this->testDatabase->schema();
|
||||
$tables = $schema->findTables('test%');
|
||||
|
@ -131,7 +94,7 @@ class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanTemporaryDirectories() {
|
||||
public function cleanTemporaryDirectories(): void {
|
||||
$count = $this->doCleanTemporaryDirectories();
|
||||
if ($count > 0) {
|
||||
$this->output->write('Temporary directories removed: ' . $count);
|
||||
|
@ -147,7 +110,7 @@ class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
|||
* @return int
|
||||
* The count of temporary directories removed.
|
||||
*/
|
||||
protected function doCleanTemporaryDirectories() {
|
||||
protected function doCleanTemporaryDirectories(): int {
|
||||
$count = 0;
|
||||
$simpletest_dir = $this->root . '/sites/simpletest';
|
||||
if (is_dir($simpletest_dir)) {
|
||||
|
@ -168,27 +131,8 @@ class EnvironmentCleaner implements EnvironmentCleanerInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanResultsTable($test_id = NULL) {
|
||||
$count = 0;
|
||||
if ($test_id) {
|
||||
$count = $this->resultsDatabase->query('SELECT COUNT([test_id]) FROM {simpletest_test_id} WHERE [test_id] = :test_id', [':test_id' => $test_id])->fetchField();
|
||||
|
||||
$this->resultsDatabase->delete('simpletest')
|
||||
->condition('test_id', $test_id)
|
||||
->execute();
|
||||
$this->resultsDatabase->delete('simpletest_test_id')
|
||||
->condition('test_id', $test_id)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
$count = $this->resultsDatabase->query('SELECT COUNT([test_id]) FROM {simpletest_test_id}')->fetchField();
|
||||
|
||||
// Clear test results.
|
||||
$this->resultsDatabase->delete('simpletest')->execute();
|
||||
$this->resultsDatabase->delete('simpletest_test_id')->execute();
|
||||
}
|
||||
|
||||
return $count;
|
||||
public function cleanResults(TestRun $test_run = NULL): int {
|
||||
return $test_run ? $test_run->removeResults() : $this->testRunResultsStorage->cleanUp();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,11 +8,9 @@ namespace Drupal\Core\Test;
|
|||
* This interface is marked internal. It does not imply an API.
|
||||
*
|
||||
* @todo Formalize this interface in
|
||||
* https://www.drupal.org/project/drupal/issues/3075490 and
|
||||
* https://www.drupal.org/project/drupal/issues/3075608
|
||||
* https://www.drupal.org/project/drupal/issues/3075490
|
||||
*
|
||||
* @see https://www.drupal.org/project/drupal/issues/3075490
|
||||
* @see https://www.drupal.org/project/drupal/issues/3075608
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
@ -25,33 +23,34 @@ interface EnvironmentCleanerInterface {
|
|||
* under test.
|
||||
*
|
||||
* @param bool $clear_results
|
||||
* (optional) Whether to clear the test results database. Defaults to TRUE.
|
||||
* (optional) Whether to clear the test results storage. Defaults to TRUE.
|
||||
* @param bool $clear_temp_directories
|
||||
* (optional) Whether to clear the test site directories. Defaults to TRUE.
|
||||
* @param bool $clear_database
|
||||
* (optional) Whether to clean up the fixture database. Defaults to TRUE.
|
||||
*/
|
||||
public function cleanEnvironment($clear_results = TRUE, $clear_temp_directories = TRUE, $clear_database = TRUE);
|
||||
public function cleanEnvironment(bool $clear_results = TRUE, bool $clear_temp_directories = TRUE, bool $clear_database = TRUE): void;
|
||||
|
||||
/**
|
||||
* Remove database entries left over in the fixture database.
|
||||
*/
|
||||
public function cleanDatabase();
|
||||
public function cleanDatabase(): void;
|
||||
|
||||
/**
|
||||
* Finds all leftover fixture site directories and removes them.
|
||||
*/
|
||||
public function cleanTemporaryDirectories();
|
||||
public function cleanTemporaryDirectories(): void;
|
||||
|
||||
/**
|
||||
* Clears test result tables from the results database.
|
||||
* Clears test results from the results storage.
|
||||
*
|
||||
* @param $test_id
|
||||
* Test ID to remove results for, or NULL to remove all results.
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object to remove results for, or NULL to remove all
|
||||
* results.
|
||||
*
|
||||
* @return int
|
||||
* The number of results that were removed.
|
||||
*/
|
||||
public function cleanResultsTable($test_id = NULL);
|
||||
public function cleanResults(TestRun $test_run = NULL): int;
|
||||
|
||||
}
|
||||
|
|
|
@ -13,55 +13,42 @@ use Symfony\Component\Process\PhpExecutableFinder;
|
|||
* This class runs PHPUnit-based tests and converts their JUnit results to a
|
||||
* format that can be stored in the {simpletest} database schema.
|
||||
*
|
||||
* This class is @internal and not considered to be API.
|
||||
* This class is internal and not considered to be API.
|
||||
*
|
||||
* @code
|
||||
* $runner = PhpUnitTestRunner::create(\Drupal::getContainer());
|
||||
* $results = $runner->runTests($test_id, $test_list['phpunit']);
|
||||
* $results = $runner->execute($test_run, $test_list['phpunit']);
|
||||
* @endcode
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PhpUnitTestRunner implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* Path to the working directory.
|
||||
* Constructs a test runner.
|
||||
*
|
||||
* JUnit log files will be stored in this directory.
|
||||
*
|
||||
* @var string
|
||||
* @param string $appRoot
|
||||
* Path to the application root.
|
||||
* @param string $workingDirectory
|
||||
* Path to the working directory. JUnit log files will be stored in this
|
||||
* directory.
|
||||
*/
|
||||
protected $workingDirectory;
|
||||
|
||||
/**
|
||||
* Path to the application root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $appRoot;
|
||||
public function __construct(
|
||||
protected string $appRoot,
|
||||
protected string $workingDirectory
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
public static function create(ContainerInterface $container): static {
|
||||
return new static(
|
||||
(string) $container->getParameter('app.root'),
|
||||
(string) $container->get('file_system')->realpath('public://simpletest')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a test runner.
|
||||
*
|
||||
* @param string $app_root
|
||||
* Path to the application root.
|
||||
* @param string $working_directory
|
||||
* Path to the working directory. JUnit log files will be stored in this
|
||||
* directory.
|
||||
*/
|
||||
public function __construct($app_root, $working_directory) {
|
||||
$this->appRoot = $app_root;
|
||||
$this->workingDirectory = $working_directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to use for PHPUnit's --log-junit option.
|
||||
*
|
||||
|
@ -73,7 +60,7 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
public function xmlLogFilePath($test_id) {
|
||||
public function xmlLogFilePath(int $test_id): string {
|
||||
return $this->workingDirectory . '/phpunit-' . $test_id . '.xml';
|
||||
}
|
||||
|
||||
|
@ -85,7 +72,7 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
public function phpUnitCommand() {
|
||||
public function phpUnitCommand(): string {
|
||||
// Load the actual autoloader being used and determine its filename using
|
||||
// reflection. We can determine the vendor directory based on that filename.
|
||||
$autoloader = require $this->appRoot . '/autoload.php';
|
||||
|
@ -124,7 +111,7 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
public function runCommand(array $unescaped_test_classnames, $phpunit_file, &$status = NULL, &$output = NULL) {
|
||||
public function runCommand(array $unescaped_test_classnames, string $phpunit_file, int &$status = NULL, array &$output = NULL): string {
|
||||
global $base_url;
|
||||
// Setup an environment variable containing the database connection so that
|
||||
// functional tests can connect to the database.
|
||||
|
@ -184,8 +171,8 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
|
|||
/**
|
||||
* Executes PHPUnit tests and returns the results of the run.
|
||||
*
|
||||
* @param int $test_id
|
||||
* The current test ID.
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
* @param string[] $unescaped_test_classnames
|
||||
* An array of test class names, including full namespaces, to be passed as
|
||||
* a regular expression to PHPUnit's --filter option.
|
||||
|
@ -199,18 +186,18 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
public function runTests($test_id, array $unescaped_test_classnames, &$status = NULL) {
|
||||
$phpunit_file = $this->xmlLogFilePath($test_id);
|
||||
public function execute(TestRun $test_run, array $unescaped_test_classnames, int &$status = NULL): array {
|
||||
$phpunit_file = $this->xmlLogFilePath($test_run->id());
|
||||
// Store output from our test run.
|
||||
$output = [];
|
||||
$this->runCommand($unescaped_test_classnames, $phpunit_file, $status, $output);
|
||||
|
||||
if ($status == TestStatus::PASS) {
|
||||
return JUnitConverter::xmlToRows($test_id, $phpunit_file);
|
||||
return JUnitConverter::xmlToRows($test_run->id(), $phpunit_file);
|
||||
}
|
||||
return [
|
||||
[
|
||||
'test_id' => $test_id,
|
||||
'test_id' => $test_run->id(),
|
||||
'test_class' => implode(",", $unescaped_test_classnames),
|
||||
'status' => TestStatus::label($status),
|
||||
'message' => 'PHPUnit Test failed to complete; Error: ' . implode("\n", $output),
|
||||
|
@ -222,19 +209,35 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the parsed PHPUnit results into the test run.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
* @param array[] $phpunit_results
|
||||
* An array of test results, as returned from
|
||||
* \Drupal\Core\Test\JUnitConverter::xmlToRows(). Can be the return value of
|
||||
* PhpUnitTestRunner::execute().
|
||||
*/
|
||||
public function processPhpUnitResults(TestRun $test_run, array $phpunit_results): void {
|
||||
foreach ($phpunit_results as $result) {
|
||||
$test_run->insertLogEntry($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tallies test results per test class.
|
||||
*
|
||||
* @param string[][] $results
|
||||
* Array of results in the {simpletest} schema. Can be the return value of
|
||||
* PhpUnitTestRunner::runTests().
|
||||
* PhpUnitTestRunner::execute().
|
||||
*
|
||||
* @return int[][]
|
||||
* Array of status tallies, keyed by test class name and status type.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function summarizeResults(array $results) {
|
||||
public function summarizeResults(array $results): array {
|
||||
$summaries = [];
|
||||
foreach ($results as $result) {
|
||||
if (!isset($summaries[$result['test_class']])) {
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Test;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\ConnectionNotDefinedException;
|
||||
|
||||
/**
|
||||
* Implements a test run results storage compatible with legacy Simpletest.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SimpletestTestRunResultsStorage implements TestRunResultsStorageInterface {
|
||||
|
||||
/**
|
||||
* SimpletestTestRunResultsStorage constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use for inserting assertions.
|
||||
*/
|
||||
public function __construct(
|
||||
protected Connection $connection
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database connection to use for inserting assertions.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
* The database connection to use for inserting assertions.
|
||||
*/
|
||||
public static function getConnection(): Connection {
|
||||
// Check whether there is a test runner connection.
|
||||
// @see run-tests.sh
|
||||
// @todo Convert Simpletest UI runner to create + use this connection, too.
|
||||
try {
|
||||
$connection = Database::getConnection('default', 'test-runner');
|
||||
}
|
||||
catch (ConnectionNotDefinedException $e) {
|
||||
// Check whether there is a backup of the original default connection.
|
||||
// @see FunctionalTestSetupTrait::prepareEnvironment()
|
||||
try {
|
||||
$connection = Database::getConnection('default', 'simpletest_original_default');
|
||||
}
|
||||
catch (ConnectionNotDefinedException $e) {
|
||||
// If FunctionalTestSetupTrait::prepareEnvironment() failed, the
|
||||
// test-specific database connection does not exist yet/anymore, so
|
||||
// fall back to the default of the (UI) test runner.
|
||||
$connection = Database::getConnection('default', 'default');
|
||||
}
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createNew(): int|string {
|
||||
return $this->connection->insert('simpletest_test_id')
|
||||
->useDefaults(['test_id'])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDatabasePrefix(TestRun $test_run, string $database_prefix): void {
|
||||
$affected_rows = $this->connection->update('simpletest_test_id')
|
||||
->fields(['last_prefix' => $database_prefix])
|
||||
->condition('test_id', $test_run->id())
|
||||
->execute();
|
||||
if (!$affected_rows) {
|
||||
throw new \RuntimeException('Failed to set up database prefix.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function insertLogEntry(TestRun $test_run, array $entry): bool {
|
||||
$entry['test_id'] = $test_run->id();
|
||||
$entry = array_merge([
|
||||
'function' => 'Unknown',
|
||||
'line' => 0,
|
||||
'file' => 'Unknown',
|
||||
], $entry);
|
||||
|
||||
return (bool) $this->connection->insert('simpletest')
|
||||
->fields($entry)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeResults(TestRun $test_run): int {
|
||||
$this->connection->startTransaction('delete_test_run');
|
||||
$this->connection->delete('simpletest')
|
||||
->condition('test_id', $test_run->id())
|
||||
->execute();
|
||||
$count = $this->connection->delete('simpletest_test_id')
|
||||
->condition('test_id', $test_run->id())
|
||||
->execute();
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLogEntriesByTestClass(TestRun $test_run): array {
|
||||
return $this->connection->select('simpletest')
|
||||
->fields('simpletest')
|
||||
->condition('test_id', $test_run->id())
|
||||
->orderBy('test_class')
|
||||
->orderBy('message_id')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCurrentTestRunState(TestRun $test_run): array {
|
||||
// Define a subquery to identify the latest 'message_id' given the
|
||||
// $test_id.
|
||||
$max_message_id_subquery = $this->connection
|
||||
->select('simpletest', 'sub')
|
||||
->condition('test_id', $test_run->id());
|
||||
$max_message_id_subquery->addExpression('MAX([message_id])', 'max_message_id');
|
||||
|
||||
// Run a select query to return 'last_prefix' from {simpletest_test_id} and
|
||||
// 'test_class' from {simpletest}.
|
||||
$select = $this->connection->select($max_message_id_subquery, 'st_sub');
|
||||
$select->join('simpletest', 'st', '[st].[message_id] = [st_sub].[max_message_id]');
|
||||
$select->join('simpletest_test_id', 'sttid', '[st].[test_id] = [sttid].[test_id]');
|
||||
$select->addField('sttid', 'last_prefix', 'db_prefix');
|
||||
$select->addField('st', 'test_class');
|
||||
|
||||
return $select->execute()->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildTestingResultsEnvironment(bool $keep_results): void {
|
||||
$schema = $this->connection->schema();
|
||||
foreach (static::testingResultsSchema() as $name => $table_spec) {
|
||||
$table_exists = $schema->tableExists($name);
|
||||
if (!$keep_results && $table_exists) {
|
||||
$this->connection->truncate($name)->execute();
|
||||
}
|
||||
if (!$table_exists) {
|
||||
$schema->createTable($name, $table_spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateTestingResultsEnvironment(): bool {
|
||||
$schema = $this->connection->schema();
|
||||
return $schema->tableExists('simpletest') && $schema->tableExists('simpletest_test_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanUp(): int {
|
||||
// Clear test results.
|
||||
$this->connection->startTransaction('delete_simpletest');
|
||||
$this->connection->delete('simpletest')->execute();
|
||||
$count = $this->connection->delete('simpletest_test_id')->execute();
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the database schema for run-tests.sh and simpletest module.
|
||||
*
|
||||
* @return array
|
||||
* Array suitable for use in a hook_schema() implementation.
|
||||
*/
|
||||
public static function testingResultsSchema(): array {
|
||||
$schema['simpletest'] = [
|
||||
'description' => 'Stores simpletest messages',
|
||||
'fields' => [
|
||||
'message_id' => [
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary Key: Unique simpletest message ID.',
|
||||
],
|
||||
'test_id' => [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Test ID, messages belonging to the same ID are reported together',
|
||||
],
|
||||
'test_class' => [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The name of the class that created this message.',
|
||||
],
|
||||
'status' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 9,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Message status. Core understands pass, fail, exception.',
|
||||
],
|
||||
'message' => [
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The message itself.',
|
||||
],
|
||||
'message_group' => [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The message group this message belongs to. For example: warning, browser, user.',
|
||||
],
|
||||
'function' => [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Name of the assertion function or method that created this message.',
|
||||
],
|
||||
'line' => [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Line number on which the function is called.',
|
||||
],
|
||||
'file' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Name of the file where the function is called.',
|
||||
],
|
||||
],
|
||||
'primary key' => ['message_id'],
|
||||
'indexes' => [
|
||||
'reporter' => ['test_class', 'message_id'],
|
||||
],
|
||||
];
|
||||
$schema['simpletest_test_id'] = [
|
||||
'description' => 'Stores simpletest test IDs, used to auto-increment the test ID so that a fresh test ID is used.',
|
||||
'fields' => [
|
||||
'test_id' => [
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary Key: Unique simpletest ID used to group test results together. Each time a set of tests are run a new test ID is used.',
|
||||
],
|
||||
'last_prefix' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 60,
|
||||
'not null' => FALSE,
|
||||
'default' => '',
|
||||
'description' => 'The last database prefix used during testing.',
|
||||
],
|
||||
],
|
||||
'primary key' => ['test_id'],
|
||||
];
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,8 +3,6 @@
|
|||
namespace Drupal\Core\Test;
|
||||
|
||||
use Drupal\Component\FileSystem\FileSystem;
|
||||
use Drupal\Core\Database\ConnectionNotDefinedException;
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
/**
|
||||
* Provides helper methods for interacting with the fixture database.
|
||||
|
@ -28,36 +26,6 @@ class TestDatabase {
|
|||
*/
|
||||
protected $databasePrefix;
|
||||
|
||||
/**
|
||||
* Returns the database connection to the site under test.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
* The database connection to use for inserting assertions.
|
||||
*
|
||||
* @see \Drupal\Core\Test\TestSetupTrait::getDatabaseConnection()
|
||||
*/
|
||||
public static function getConnection() {
|
||||
// Check whether there is a test runner connection.
|
||||
// @see run-tests.sh
|
||||
try {
|
||||
$connection = Database::getConnection('default', 'test-runner');
|
||||
}
|
||||
catch (ConnectionNotDefinedException $e) {
|
||||
// Check whether there is a backup of the original default connection.
|
||||
// @see \Drupal\Core\Test\TestSetupTrait::changeDatabasePrefix()
|
||||
try {
|
||||
$connection = Database::getConnection('default', 'simpletest_original_default');
|
||||
}
|
||||
catch (ConnectionNotDefinedException $e) {
|
||||
// If TestBase::prepareEnvironment() or TestBase::restoreEnvironment()
|
||||
// failed, the test-specific database connection does not exist
|
||||
// yet/anymore, so fall back to the default of the (UI) test runner.
|
||||
$connection = Database::getConnection('default', 'default');
|
||||
}
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestDatabase constructor.
|
||||
*
|
||||
|
@ -71,7 +39,7 @@ class TestDatabase {
|
|||
* @throws \InvalidArgumentException
|
||||
* Thrown when $db_prefix does not match the regular expression.
|
||||
*/
|
||||
public function __construct($db_prefix = NULL, $create_lock = FALSE) {
|
||||
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;
|
||||
|
@ -95,7 +63,7 @@ class TestDatabase {
|
|||
* @return string
|
||||
* The relative path to the test site directory.
|
||||
*/
|
||||
public function getTestSitePath() {
|
||||
public function getTestSitePath(): string {
|
||||
return 'sites/simpletest/' . $this->lockId;
|
||||
}
|
||||
|
||||
|
@ -105,7 +73,7 @@ class TestDatabase {
|
|||
* @return string
|
||||
* The test database prefix.
|
||||
*/
|
||||
public function getDatabasePrefix() {
|
||||
public function getDatabasePrefix(): string {
|
||||
return $this->databasePrefix;
|
||||
}
|
||||
|
||||
|
@ -118,7 +86,7 @@ class TestDatabase {
|
|||
* @return int
|
||||
* The unique lock ID for the test method.
|
||||
*/
|
||||
protected function getTestLock($create_lock = FALSE) {
|
||||
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
|
||||
|
@ -144,7 +112,7 @@ class TestDatabase {
|
|||
* @return bool
|
||||
* TRUE if successful, FALSE if not.
|
||||
*/
|
||||
public function releaseLock() {
|
||||
public function releaseLock(): bool {
|
||||
return unlink($this->getLockFile($this->lockId));
|
||||
}
|
||||
|
||||
|
@ -153,7 +121,7 @@ class TestDatabase {
|
|||
*
|
||||
* This should only be called once all the test fixtures have been cleaned up.
|
||||
*/
|
||||
public static function releaseAllTestLocks() {
|
||||
public static function releaseAllTestLocks(): void {
|
||||
$tmp = FileSystem::getOsTemporaryDirectory();
|
||||
$dir = dir($tmp);
|
||||
while (($entry = $dir->read()) !== FALSE) {
|
||||
|
@ -176,256 +144,18 @@ class TestDatabase {
|
|||
* @return string
|
||||
* A file path to the symbolic link that prevents the lock ID being re-used.
|
||||
*/
|
||||
protected function getLockFile($lock_id) {
|
||||
protected function getLockFile(int $lock_id): string {
|
||||
return FileSystem::getOsTemporaryDirectory() . '/test_' . $lock_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an assertion from outside the testing context.
|
||||
* Gets the file path of the PHP error log of the test.
|
||||
*
|
||||
* This is useful for inserting assertions that can only be recorded after
|
||||
* the test case has been destroyed, such as PHP fatal errors. The caller
|
||||
* information is not automatically gathered since the caller is most likely
|
||||
* inserting the assertion on behalf of other code.
|
||||
*
|
||||
* @param string $test_id
|
||||
* The test ID to which the assertion relates.
|
||||
* @param string $test_class
|
||||
* The test class to store an assertion for.
|
||||
* @param bool|string $status
|
||||
* A boolean or a string of 'pass' or 'fail'. TRUE means 'pass'.
|
||||
* @param string $message
|
||||
* The assertion message.
|
||||
* @param string $group
|
||||
* The assertion message group.
|
||||
* @param array $caller
|
||||
* The an array containing the keys 'file' and 'line' that represent the
|
||||
* file and line number of that file that is responsible for the assertion.
|
||||
*
|
||||
* @return int
|
||||
* Message ID of the stored assertion.
|
||||
*
|
||||
* @internal
|
||||
* @return string
|
||||
* The relative path to the test site PHP error log file.
|
||||
*/
|
||||
public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = []) {
|
||||
// Convert boolean status to string status.
|
||||
if (is_bool($status)) {
|
||||
$status = $status ? 'pass' : 'fail';
|
||||
}
|
||||
|
||||
$caller += [
|
||||
'function' => 'Unknown',
|
||||
'line' => 0,
|
||||
'file' => 'Unknown',
|
||||
];
|
||||
|
||||
$assertion = [
|
||||
'test_id' => $test_id,
|
||||
'test_class' => $test_class,
|
||||
'status' => $status,
|
||||
'message' => $message,
|
||||
'message_group' => $group,
|
||||
'function' => $caller['function'],
|
||||
'line' => $caller['line'],
|
||||
'file' => $caller['file'],
|
||||
];
|
||||
|
||||
return static::getConnection()
|
||||
->insert('simpletest')
|
||||
->fields($assertion)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the last test that ran given a test ID.
|
||||
*
|
||||
* @param int $test_id
|
||||
* The test ID to get the last test from.
|
||||
*
|
||||
* @return array
|
||||
* Associative array containing the last database prefix used and the
|
||||
* last test class that ran.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function lastTestGet($test_id) {
|
||||
$connection = static::getConnection();
|
||||
|
||||
// Define a subquery to identify the latest 'message_id' given the
|
||||
// $test_id.
|
||||
$max_message_id_subquery = $connection
|
||||
->select('simpletest', 'sub')
|
||||
->condition('test_id', $test_id);
|
||||
$max_message_id_subquery->addExpression('MAX([message_id])', 'max_message_id');
|
||||
|
||||
// Run a select query to return 'last_prefix' from {simpletest_test_id} and
|
||||
// 'test_class' from {simpletest}.
|
||||
$select = $connection->select($max_message_id_subquery, 'st_sub');
|
||||
$select->join('simpletest', 'st', '[st].[message_id] = [st_sub].[max_message_id]');
|
||||
$select->join('simpletest_test_id', 'sttid', '[st].[test_id] = [sttid].[test_id]');
|
||||
$select->addField('sttid', 'last_prefix');
|
||||
$select->addField('st', 'test_class');
|
||||
return $select->execute()->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the error log and reports any errors as assertion failures.
|
||||
*
|
||||
* The errors in the log should only be fatal errors since any other errors
|
||||
* will have been recorded by the error handler.
|
||||
*
|
||||
* @param int $test_id
|
||||
* The test ID to which the log relates.
|
||||
* @param string $test_class
|
||||
* The test class to which the log relates.
|
||||
*
|
||||
* @return bool
|
||||
* Whether any fatal errors were found.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function logRead($test_id, $test_class) {
|
||||
$log = DRUPAL_ROOT . '/' . $this->getTestSitePath() . '/error.log';
|
||||
$found = FALSE;
|
||||
if (file_exists($log)) {
|
||||
foreach (file($log) as $line) {
|
||||
if (preg_match('/\[.*?\] (.*?): (.*?) in (.*) on line (\d+)/', $line, $match)) {
|
||||
// Parse PHP fatal errors for example: PHP Fatal error: Call to
|
||||
// undefined function break_me() in /path/to/file.php on line 17
|
||||
$caller = [
|
||||
'line' => $match[4],
|
||||
'file' => $match[3],
|
||||
];
|
||||
static::insertAssert($test_id, $test_class, FALSE, $match[2], $match[1], $caller);
|
||||
}
|
||||
else {
|
||||
// Unknown format, place the entire message in the log.
|
||||
static::insertAssert($test_id, $test_class, FALSE, $line, 'Fatal error');
|
||||
}
|
||||
$found = TRUE;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the database schema for run-tests.sh and PHPUnit tests.
|
||||
*
|
||||
* @return array
|
||||
* Array suitable for use in a hook_schema() implementation.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function testingSchema() {
|
||||
$schema['simpletest'] = [
|
||||
'description' => 'Stores test messages',
|
||||
'fields' => [
|
||||
'message_id' => [
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary Key: Unique test message ID.',
|
||||
],
|
||||
'test_id' => [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Test ID, messages belonging to the same ID are reported together',
|
||||
],
|
||||
'test_class' => [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The name of the class that created this message.',
|
||||
],
|
||||
'status' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 9,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Message status. Core understands pass, fail, exception.',
|
||||
],
|
||||
'message' => [
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The message itself.',
|
||||
],
|
||||
'message_group' => [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The message group this message belongs to. For example: warning, browser, user.',
|
||||
],
|
||||
'function' => [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Name of the assertion function or method that created this message.',
|
||||
],
|
||||
'line' => [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Line number on which the function is called.',
|
||||
],
|
||||
'file' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Name of the file where the function is called.',
|
||||
],
|
||||
],
|
||||
'primary key' => ['message_id'],
|
||||
'indexes' => [
|
||||
'reporter' => ['test_class', 'message_id'],
|
||||
],
|
||||
];
|
||||
$schema['simpletest_test_id'] = [
|
||||
'description' => 'Stores test IDs, used to auto-increment the test ID so that a fresh test ID is used.',
|
||||
'fields' => [
|
||||
'test_id' => [
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary Key: Unique test ID used to group test results together. Each time a set of tests
|
||||
are run a new test ID is used.',
|
||||
],
|
||||
'last_prefix' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 60,
|
||||
'not null' => FALSE,
|
||||
'default' => '',
|
||||
'description' => 'The last database prefix used during testing.',
|
||||
],
|
||||
],
|
||||
'primary key' => ['test_id'],
|
||||
];
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the parsed PHPUnit results into {simpletest}.
|
||||
*
|
||||
* @param array[] $phpunit_results
|
||||
* An array of test results, as returned from
|
||||
* \Drupal\Core\Test\JUnitConverter::xmlToRows(). These results are in a
|
||||
* form suitable for inserting into the {simpletest} table of the test
|
||||
* results database.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function processPhpUnitResults($phpunit_results) {
|
||||
if ($phpunit_results) {
|
||||
$query = static::getConnection()
|
||||
->insert('simpletest')
|
||||
->fields(array_keys($phpunit_results[0]));
|
||||
foreach ($phpunit_results as $result) {
|
||||
$query->values($result);
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
public function getPhpErrorLogPath(): string {
|
||||
return $this->getTestSitePath() . '/error.log';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Test;
|
||||
|
||||
/**
|
||||
* Implements an object that tracks execution of a test run.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TestRun {
|
||||
|
||||
/**
|
||||
* The test database prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $databasePrefix;
|
||||
|
||||
/**
|
||||
* The latest class under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testClass;
|
||||
|
||||
/**
|
||||
* TestRun constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRunResultsStorageInterface $testRunResultsStorage
|
||||
* The test run results storage.
|
||||
* @param int|string $testId
|
||||
* A unique test run id.
|
||||
*/
|
||||
public function __construct(
|
||||
protected TestRunResultsStorageInterface $testRunResultsStorage,
|
||||
protected int|string $testId
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new test run object.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRunResultsStorageInterface $test_run_results_storage
|
||||
* The test run results storage.
|
||||
*
|
||||
* @return self
|
||||
* The new test run object.
|
||||
*/
|
||||
public static function createNew(TestRunResultsStorageInterface $test_run_results_storage): TestRun {
|
||||
$test_id = $test_run_results_storage->createNew();
|
||||
return new static($test_run_results_storage, $test_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a test run object from storage.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRunResultsStorageInterface $test_run_results_storage
|
||||
* The test run results storage.
|
||||
* @param int|string $test_id
|
||||
* The test run id.
|
||||
*
|
||||
* @return self
|
||||
* The test run object.
|
||||
*/
|
||||
public static function get(TestRunResultsStorageInterface $test_run_results_storage, int|string $test_id): TestRun {
|
||||
return new static($test_run_results_storage, $test_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the test run object.
|
||||
*
|
||||
* @return int|string
|
||||
* The id of the test run object.
|
||||
*/
|
||||
public function id(): int|string {
|
||||
return $this->testId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test database prefix.
|
||||
*
|
||||
* @param string $database_prefix
|
||||
* The database prefix.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* If the database prefix cannot be saved to storage.
|
||||
*/
|
||||
public function setDatabasePrefix(string $database_prefix): void {
|
||||
$this->databasePrefix = $database_prefix;
|
||||
$this->testRunResultsStorage->setDatabasePrefix($this, $database_prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test database prefix.
|
||||
*
|
||||
* @return string
|
||||
* The database prefix.
|
||||
*/
|
||||
public function getDatabasePrefix(): string {
|
||||
if (is_null($this->databasePrefix)) {
|
||||
$state = $this->testRunResultsStorage->getCurrentTestRunState($this);
|
||||
$this->databasePrefix = $state['db_prefix'];
|
||||
$this->testClass = $state['test_class'];
|
||||
}
|
||||
return $this->databasePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest class under test.
|
||||
*
|
||||
* @return string
|
||||
* The test class.
|
||||
*/
|
||||
public function getTestClass(): string {
|
||||
if (is_null($this->testClass)) {
|
||||
$state = $this->testRunResultsStorage->getCurrentTestRunState($this);
|
||||
$this->databasePrefix = $state['db_prefix'];
|
||||
$this->testClass = $state['test_class'];
|
||||
}
|
||||
return $this->testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a test log entry.
|
||||
*
|
||||
* @param array $entry
|
||||
* The array of the log entry elements.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the addition was successful, FALSE otherwise.
|
||||
*/
|
||||
public function insertLogEntry(array $entry): bool {
|
||||
$this->testClass = $entry['test_class'];
|
||||
return $this->testRunResultsStorage->insertLogEntry($this, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get test results for a test run, ordered by test class.
|
||||
*
|
||||
* @return array
|
||||
* Array of results ordered by test class and message id.
|
||||
*/
|
||||
public function getLogEntriesByTestClass(): array {
|
||||
return $this->testRunResultsStorage->getLogEntriesByTestClass($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the test results from the storage.
|
||||
*
|
||||
* @return int
|
||||
* The number of log entries that were removed from storage.
|
||||
*/
|
||||
public function removeResults(): int {
|
||||
return $this->testRunResultsStorage->removeResults($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the PHP error log and reports any errors as assertion failures.
|
||||
*
|
||||
* The errors in the log should only be fatal errors since any other errors
|
||||
* will have been recorded by the error handler.
|
||||
*
|
||||
* @param string $error_log_path
|
||||
* The path of log file.
|
||||
* @param string $test_class
|
||||
* The test class to which the log relates.
|
||||
*
|
||||
* @return bool
|
||||
* Whether any fatal errors were found.
|
||||
*/
|
||||
public function processPhpErrorLogFile(string $error_log_path, string $test_class): bool {
|
||||
$found = FALSE;
|
||||
if (file_exists($error_log_path)) {
|
||||
foreach (file($error_log_path) as $line) {
|
||||
if (preg_match('/\[.*?\] (.*?): (.*?) in (.*) on line (\d+)/', $line, $match)) {
|
||||
// Parse PHP fatal errors for example: PHP Fatal error: Call to
|
||||
// undefined function break_me() in /path/to/file.php on line 17
|
||||
$this->insertLogEntry([
|
||||
'test_class' => $test_class,
|
||||
'status' => 'fail',
|
||||
'message' => $match[2],
|
||||
'message_group' => $match[1],
|
||||
'line' => $match[4],
|
||||
'file' => $match[3],
|
||||
]);
|
||||
}
|
||||
else {
|
||||
// Unknown format, place the entire message in the log.
|
||||
$this->insertLogEntry([
|
||||
'test_class' => $test_class,
|
||||
'status' => 'fail',
|
||||
'message' => $line,
|
||||
'message_group' => 'Fatal error',
|
||||
]);
|
||||
}
|
||||
$found = TRUE;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Test;
|
||||
|
||||
/**
|
||||
* Interface describing a test run results storage object.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface TestRunResultsStorageInterface {
|
||||
|
||||
/**
|
||||
* Gets a new unique identifier for a test run.
|
||||
*
|
||||
* @return int|string
|
||||
* A unique identifier.
|
||||
*/
|
||||
public function createNew(): int|string;
|
||||
|
||||
/**
|
||||
* Sets the test database prefix of a test run in storage.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
* @param string $database_prefix
|
||||
* The database prefix.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* If the operation failed.
|
||||
*/
|
||||
public function setDatabasePrefix(TestRun $test_run, string $database_prefix): void;
|
||||
|
||||
/**
|
||||
* Adds a test log entry for a test run to the storage.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
* @param array $entry
|
||||
* The array of the log entry elements.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the addition was successful, FALSE otherwise.
|
||||
*/
|
||||
public function insertLogEntry(TestRun $test_run, array $entry): bool;
|
||||
|
||||
/**
|
||||
* Removes the results of a test run from the storage.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
*
|
||||
* @return int
|
||||
* The number of log entries that were removed from storage.
|
||||
*/
|
||||
public function removeResults(TestRun $test_run): int;
|
||||
|
||||
/**
|
||||
* Get test results for a test run, ordered by test class.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
*
|
||||
* @return array
|
||||
* Array of results ordered by test class and message id.
|
||||
*/
|
||||
public function getLogEntriesByTestClass(TestRun $test_run): array;
|
||||
|
||||
/**
|
||||
* Get state information about a test run, from storage.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestRun $test_run
|
||||
* The test run object.
|
||||
*
|
||||
* @return array
|
||||
* Array of state information, for example 'last_prefix' and 'test_class'.
|
||||
*/
|
||||
public function getCurrentTestRunState(TestRun $test_run): array;
|
||||
|
||||
/**
|
||||
* Prepares the test run storage.
|
||||
*
|
||||
* @param bool $keep_results
|
||||
* If TRUE, any pre-existing storage will be preserved; if FALSE,
|
||||
* pre-existing storage will be cleaned up.
|
||||
*/
|
||||
public function buildTestingResultsEnvironment(bool $keep_results): void;
|
||||
|
||||
/**
|
||||
* Checks if the test run storage is valid.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE when the storage is valid and ready for use, FALSE otherwise.
|
||||
*
|
||||
* @see ::buildTestingResultsEnvironment()
|
||||
*/
|
||||
public function validateTestingResultsEnvironment(): bool;
|
||||
|
||||
/**
|
||||
* Resets the test run storage.
|
||||
*
|
||||
* @return int
|
||||
* The number of log entries that were removed from storage.
|
||||
*/
|
||||
public function cleanUp(): int;
|
||||
|
||||
}
|
|
@ -119,9 +119,15 @@ trait TestSetupTrait {
|
|||
*
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
* The database connection to use for inserting assertions.
|
||||
*
|
||||
* @deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. There is no
|
||||
* replacement.
|
||||
*
|
||||
* @see https://www.drupal.org/node/3176816
|
||||
*/
|
||||
public static function getDatabaseConnection() {
|
||||
return TestDatabase::getConnection();
|
||||
@trigger_error(__METHOD__ . ' is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. There is no replacement. See https://www.drupal.org/node/3176816', E_USER_DEPRECATED);
|
||||
return SimpletestTestRunResultsStorage::getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,9 +18,12 @@ use Drupal\Core\Composer\Composer;
|
|||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\EnvironmentCleaner;
|
||||
use Drupal\Core\Test\PhpUnitTestRunner;
|
||||
use Drupal\Core\Test\SimpletestTestRunResultsStorage;
|
||||
use Drupal\Core\Test\RunTests\TestFileParser;
|
||||
use Drupal\Core\Test\TestDatabase;
|
||||
use Drupal\Core\Test\TestRun;
|
||||
use Drupal\Core\Test\TestRunnerKernel;
|
||||
use Drupal\Core\Test\TestRunResultsStorageInterface;
|
||||
use Drupal\Core\Test\TestDiscovery;
|
||||
use Drupal\TestTools\PhpUnitCompatibility\ClassWriter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
@ -60,7 +63,9 @@ if (!class_exists(TestCase::class)) {
|
|||
|
||||
if ($args['execute-test']) {
|
||||
simpletest_script_setup_database();
|
||||
simpletest_script_run_one_test($args['test-id'], $args['execute-test']);
|
||||
$test_run_results_storage = simpletest_script_setup_test_run_results_storage();
|
||||
$test_run = TestRun::get($test_run_results_storage, $args['test-id']);
|
||||
simpletest_script_run_one_test($test_run, $args['execute-test']);
|
||||
// Sub-process exited already; this is just for clarity.
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -125,12 +130,16 @@ if ($args['list-files'] || $args['list-files-json']) {
|
|||
|
||||
simpletest_script_setup_database(TRUE);
|
||||
|
||||
// Setup the test run results storage environment. Currently, this coincides
|
||||
// with the simpletest database schema.
|
||||
$test_run_results_storage = simpletest_script_setup_test_run_results_storage(TRUE);
|
||||
|
||||
if ($args['clean']) {
|
||||
// Clean up left-over tables and directories.
|
||||
$cleaner = new EnvironmentCleaner(
|
||||
DRUPAL_ROOT,
|
||||
Database::getConnection(),
|
||||
TestDatabase::getConnection(),
|
||||
$test_run_results_storage,
|
||||
new ConsoleOutput(),
|
||||
\Drupal::service('file_system')
|
||||
);
|
||||
|
@ -168,7 +177,7 @@ for ($i = 0; $i < $args['repeat']; $i++) {
|
|||
}
|
||||
|
||||
// Execute tests.
|
||||
$status = simpletest_script_execute_batch($tests_to_run);
|
||||
$status = simpletest_script_execute_batch($test_run_results_storage, $tests_to_run);
|
||||
|
||||
// Stop the timer.
|
||||
simpletest_script_reporter_timer_stop();
|
||||
|
@ -180,10 +189,10 @@ simpletest_script_reporter_timer_stop();
|
|||
TestDatabase::releaseAllTestLocks();
|
||||
|
||||
// Display results before database is cleared.
|
||||
simpletest_script_reporter_display_results();
|
||||
simpletest_script_reporter_display_results($test_run_results_storage);
|
||||
|
||||
if ($args['xml']) {
|
||||
simpletest_script_reporter_write_xml_results();
|
||||
simpletest_script_reporter_write_xml_results($test_run_results_storage);
|
||||
}
|
||||
|
||||
// Clean up all test results.
|
||||
|
@ -192,11 +201,11 @@ if (!$args['keep-results']) {
|
|||
$cleaner = new EnvironmentCleaner(
|
||||
DRUPAL_ROOT,
|
||||
Database::getConnection(),
|
||||
TestDatabase::getConnection(),
|
||||
$test_run_results_storage,
|
||||
new ConsoleOutput(),
|
||||
\Drupal::service('file_system')
|
||||
);
|
||||
$cleaner->cleanResultsTable();
|
||||
$cleaner->cleanResults();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
|
@ -628,6 +637,15 @@ function simpletest_script_setup_database($new = FALSE) {
|
|||
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
|
||||
}
|
||||
Database::addConnectionInfo('default', 'default', $databases['default']['default']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the test runs results storage.
|
||||
*/
|
||||
function simpletest_script_setup_test_run_results_storage($new = FALSE) {
|
||||
global $args;
|
||||
|
||||
$databases['default'] = Database::getConnectionInfo('default');
|
||||
|
||||
// If no --sqlite parameter has been passed, then the test runner database
|
||||
// connection is the default database connection.
|
||||
|
@ -662,33 +680,24 @@ function simpletest_script_setup_database($new = FALSE) {
|
|||
|
||||
// Create the test result schema.
|
||||
try {
|
||||
$connection = Database::getConnection('default', 'test-runner');
|
||||
$schema = $connection->schema();
|
||||
$test_run_results_storage = new SimpletestTestRunResultsStorage(Database::getConnection('default', 'test-runner'));
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
simpletest_script_print_error($databases['test-runner']['default']['driver'] . ': ' . $e->getMessage());
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
|
||||
}
|
||||
if ($new && $sqlite) {
|
||||
foreach (TestDatabase::testingSchema() as $name => $table_spec) {
|
||||
try {
|
||||
$table_exists = $schema->tableExists($name);
|
||||
if (empty($args['keep-results-table']) && $table_exists) {
|
||||
$connection->truncate($name)->execute();
|
||||
}
|
||||
if (!$table_exists) {
|
||||
$schema->createTable($name, $table_spec);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
|
||||
}
|
||||
try {
|
||||
$test_run_results_storage->buildTestingResultsEnvironment(!empty($args['keep-results-table']));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
|
||||
}
|
||||
}
|
||||
// Verify that the test result database schema exists by checking one table.
|
||||
try {
|
||||
if (!$schema->tableExists('simpletest')) {
|
||||
if (!$test_run_results_storage->validateTestingResultsEnvironment()) {
|
||||
simpletest_script_print_error('Missing test result database schema. Use the --sqlite parameter.');
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
|
||||
}
|
||||
|
@ -697,12 +706,14 @@ function simpletest_script_setup_database($new = FALSE) {
|
|||
echo (string) $e;
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
|
||||
}
|
||||
|
||||
return $test_run_results_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a batch of tests.
|
||||
*/
|
||||
function simpletest_script_execute_batch($test_classes) {
|
||||
function simpletest_script_execute_batch(TestRunResultsStorageInterface $test_run_results_storage, $test_classes) {
|
||||
global $args, $test_ids;
|
||||
|
||||
$total_status = SIMPLETEST_SCRIPT_EXIT_SUCCESS;
|
||||
|
@ -716,20 +727,17 @@ function simpletest_script_execute_batch($test_classes) {
|
|||
}
|
||||
|
||||
try {
|
||||
$test_id = Database::getConnection('default', 'test-runner')
|
||||
->insert('simpletest_test_id')
|
||||
->useDefaults(['test_id'])
|
||||
->execute();
|
||||
$test_run = TestRun::createNew($test_run_results_storage);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
|
||||
}
|
||||
$test_ids[] = $test_id;
|
||||
$test_ids[] = $test_run->id();
|
||||
|
||||
$test_class = array_shift($test_classes);
|
||||
// Fork a child process.
|
||||
$command = simpletest_script_command($test_id, $test_class);
|
||||
$command = simpletest_script_command($test_run, $test_class);
|
||||
$process = proc_open($command, [], $pipes, NULL, NULL, ['bypass_shell' => TRUE]);
|
||||
|
||||
if (!is_resource($process)) {
|
||||
|
@ -740,7 +748,7 @@ function simpletest_script_execute_batch($test_classes) {
|
|||
// Register our new child.
|
||||
$children[] = [
|
||||
'process' => $process,
|
||||
'test_id' => $test_id,
|
||||
'test_run' => $test_run,
|
||||
'class' => $test_class,
|
||||
'pipes' => $pipes,
|
||||
];
|
||||
|
@ -766,17 +774,21 @@ function simpletest_script_execute_batch($test_classes) {
|
|||
// @see https://www.drupal.org/node/2780087
|
||||
$total_status = max(SIMPLETEST_SCRIPT_EXIT_FAILURE, $total_status);
|
||||
// Insert a fail for xml results.
|
||||
TestDatabase::insertAssert($child['test_id'], $child['class'], FALSE, $message, 'run-tests.sh check');
|
||||
$child['test_run']->insertLogEntry([
|
||||
'test_class' => $child['class'],
|
||||
'status' => 'fail',
|
||||
'message' => $message,
|
||||
'message_group' => 'run-tests.sh check',
|
||||
]);
|
||||
// Ensure that an error line is displayed for the class.
|
||||
simpletest_script_reporter_display_summary(
|
||||
$child['class'],
|
||||
['#pass' => 0, '#fail' => 1, '#exception' => 0, '#debug' => 0]
|
||||
);
|
||||
if ($args['die-on-fail']) {
|
||||
$db_prefix = TestDatabase::lastTestGet($child['test_id'])['last_prefix'];
|
||||
$test_db = new TestDatabase($db_prefix);
|
||||
$test_db = new TestDatabase($child['test_run']->getDatabasePrefix());
|
||||
$test_directory = $test_db->getTestSitePath();
|
||||
echo 'Test database and files kept and test exited immediately on fail so should be reproducible if you change settings.php to use the database prefix ' . $db_prefix . ' and config directories in ' . $test_directory . "\n";
|
||||
echo 'Test database and files kept and test exited immediately on fail so should be reproducible if you change settings.php to use the database prefix ' . $child['test_run']->getDatabasePrefix() . ' and config directories in ' . $test_directory . "\n";
|
||||
$args['keep-results'] = TRUE;
|
||||
// Exit repeat loop immediately.
|
||||
$args['repeat'] = -1;
|
||||
|
@ -794,15 +806,15 @@ function simpletest_script_execute_batch($test_classes) {
|
|||
/**
|
||||
* Run a PHPUnit-based test.
|
||||
*/
|
||||
function simpletest_script_run_phpunit($test_id, $class) {
|
||||
function simpletest_script_run_phpunit(TestRun $test_run, $class) {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
if ($reflection->hasProperty('runLimit')) {
|
||||
set_time_limit($reflection->getStaticPropertyValue('runLimit'));
|
||||
}
|
||||
|
||||
$runner = PhpUnitTestRunner::create(\Drupal::getContainer());
|
||||
$results = $runner->runTests($test_id, [$class], $status);
|
||||
TestDatabase::processPhpUnitResults($results);
|
||||
$results = $runner->execute($test_run, [$class], $status);
|
||||
$runner->processPhpUnitResults($test_run, $results);
|
||||
|
||||
$summaries = $runner->summarizeResults($results);
|
||||
foreach ($summaries as $class => $summary) {
|
||||
|
@ -814,14 +826,14 @@ function simpletest_script_run_phpunit($test_id, $class) {
|
|||
/**
|
||||
* Run a single test, bootstrapping Drupal if needed.
|
||||
*/
|
||||
function simpletest_script_run_one_test($test_id, $test_class) {
|
||||
function simpletest_script_run_one_test(TestRun $test_run, $test_class) {
|
||||
global $args;
|
||||
|
||||
try {
|
||||
if ($args['suppress-deprecations']) {
|
||||
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
|
||||
}
|
||||
$status = simpletest_script_run_phpunit($test_id, $test_class);
|
||||
$status = simpletest_script_run_phpunit($test_run, $test_class);
|
||||
exit($status);
|
||||
}
|
||||
// DrupalTestCase::run() catches exceptions already, so this is only reached
|
||||
|
@ -843,7 +855,7 @@ function simpletest_script_run_one_test($test_id, $test_class) {
|
|||
* @return string
|
||||
* The assembled command string.
|
||||
*/
|
||||
function simpletest_script_command($test_id, $test_class) {
|
||||
function simpletest_script_command(TestRun $test_run, $test_class) {
|
||||
global $args, $php;
|
||||
|
||||
$command = escapeshellarg($php) . ' ' . escapeshellarg('./core/scripts/' . $args['script']);
|
||||
|
@ -855,7 +867,7 @@ function simpletest_script_command($test_id, $test_class) {
|
|||
$command .= ' --dburl ' . escapeshellarg($args['dburl']);
|
||||
}
|
||||
$command .= ' --php ' . escapeshellarg($php);
|
||||
$command .= " --test-id $test_id";
|
||||
$command .= " --test-id {$test_run->id()}";
|
||||
foreach (['verbose', 'keep-results', 'color', 'die-on-fail', 'suppress-deprecations'] as $arg) {
|
||||
if ($args[$arg]) {
|
||||
$command .= ' --' . $arg;
|
||||
|
@ -1081,11 +1093,11 @@ function simpletest_script_reporter_display_summary($class, $results) {
|
|||
/**
|
||||
* Display jUnit XML test results.
|
||||
*/
|
||||
function simpletest_script_reporter_write_xml_results() {
|
||||
function simpletest_script_reporter_write_xml_results(TestRunResultsStorageInterface $test_run_results_storage) {
|
||||
global $args, $test_ids, $results_map;
|
||||
|
||||
try {
|
||||
$results = simpletest_script_load_messages_by_test_id($test_ids);
|
||||
$results = simpletest_script_load_messages_by_test_id($test_run_results_storage, $test_ids);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
|
@ -1174,7 +1186,7 @@ function simpletest_script_reporter_timer_stop() {
|
|||
/**
|
||||
* Display test results.
|
||||
*/
|
||||
function simpletest_script_reporter_display_results() {
|
||||
function simpletest_script_reporter_display_results(TestRunResultsStorageInterface $test_run_results_storage) {
|
||||
global $args, $test_ids, $results_map;
|
||||
|
||||
if ($args['verbose']) {
|
||||
|
@ -1183,7 +1195,7 @@ function simpletest_script_reporter_display_results() {
|
|||
echo "---------------------\n";
|
||||
|
||||
try {
|
||||
$results = simpletest_script_load_messages_by_test_id($test_ids);
|
||||
$results = simpletest_script_load_messages_by_test_id($test_run_results_storage, $test_ids);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
|
@ -1333,7 +1345,7 @@ function simpletest_script_print_alternatives($string, $array, $degree = 4) {
|
|||
* @return array
|
||||
* Array of test result messages from the database.
|
||||
*/
|
||||
function simpletest_script_load_messages_by_test_id($test_ids) {
|
||||
function simpletest_script_load_messages_by_test_id(TestRunResultsStorageInterface $test_run_results_storage, $test_ids) {
|
||||
global $args;
|
||||
$results = [];
|
||||
|
||||
|
@ -1348,10 +1360,11 @@ function simpletest_script_load_messages_by_test_id($test_ids) {
|
|||
|
||||
foreach ($test_id_chunks as $test_id_chunk) {
|
||||
try {
|
||||
$result_chunk = Database::getConnection('default', 'test-runner')
|
||||
->query("SELECT * FROM {simpletest} WHERE [test_id] IN ( :test_ids[] ) ORDER BY [test_class], [message_id]", [
|
||||
':test_ids[]' => $test_id_chunk,
|
||||
])->fetchAll();
|
||||
$result_chunk = [];
|
||||
foreach ($test_id_chunk as $test_id) {
|
||||
$test_run = TestRun::get($test_run_results_storage, $test_id);
|
||||
$result_chunk = array_merge($result_chunk, $test_run->getLogEntriesByTestClass());
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo (string) $e;
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\KernelTests\Core\Test;
|
|||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Test\EnvironmentCleaner;
|
||||
use Drupal\Core\Test\TestRunResultsStorageInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
@ -30,11 +31,12 @@ class EnvironmentCleanerTest extends KernelTestBase {
|
|||
]);
|
||||
|
||||
$connection = $this->prophesize(Connection::class);
|
||||
$test_run_results_storage = $this->prophesize(TestRunResultsStorageInterface::class);
|
||||
|
||||
$cleaner = new EnvironmentCleaner(
|
||||
vfsStream::url('cleanup_test'),
|
||||
$connection->reveal(),
|
||||
$connection->reveal(),
|
||||
$test_run_results_storage->reveal(),
|
||||
new NullOutput(),
|
||||
\Drupal::service('file_system')
|
||||
);
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Test;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\TestRun;
|
||||
use Drupal\Core\Test\SimpletestTestRunResultsStorage;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Test\SimpletestTestRunResultsStorage
|
||||
* @group Test
|
||||
*/
|
||||
class SimpletestTestRunResultsStorageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The database connection for testing.
|
||||
*
|
||||
* NOTE: this is the connection to the fixture database to allow testing the
|
||||
* storage class, NOT the database where actual tests results are stored.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The test run results storage.
|
||||
*
|
||||
* @var \Drupal\Core\Test\TestRunResultsStorageInterface
|
||||
*/
|
||||
protected $testRunResultsStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->connection = Database::getConnection();
|
||||
$this->testRunResultsStorage = new SimpletestTestRunResultsStorage($this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildTestingResultsEnvironment
|
||||
* @covers ::validateTestingResultsEnvironment
|
||||
*/
|
||||
public function testBuildNewEnvironment(): void {
|
||||
$schema = $this->connection->schema();
|
||||
|
||||
$this->assertFalse($schema->tableExists('simpletest'));
|
||||
$this->assertFalse($schema->tableExists('simpletest_test_id'));
|
||||
$this->assertFalse($this->testRunResultsStorage->validateTestingResultsEnvironment());
|
||||
|
||||
$this->testRunResultsStorage->buildTestingResultsEnvironment(FALSE);
|
||||
|
||||
$this->assertTrue($schema->tableExists('simpletest'));
|
||||
$this->assertTrue($schema->tableExists('simpletest_test_id'));
|
||||
$this->assertTrue($this->testRunResultsStorage->validateTestingResultsEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildTestingResultsEnvironment
|
||||
* @covers ::validateTestingResultsEnvironment
|
||||
* @covers ::createNew
|
||||
* @covers ::insertLogEntry
|
||||
* @covers ::cleanUp
|
||||
*/
|
||||
public function testBuildEnvironmentKeepingExistingResults(): void {
|
||||
$schema = $this->connection->schema();
|
||||
|
||||
// Initial build of the environment.
|
||||
$this->testRunResultsStorage->buildTestingResultsEnvironment(FALSE);
|
||||
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->createNew());
|
||||
$test_run = TestRun::get($this->testRunResultsStorage, 1);
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->insertLogEntry($test_run, $this->getTestLogEntry('Test\GroundControl')));
|
||||
$this->assertEquals(1, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
// Build the environment again, keeping results. Results should be kept.
|
||||
$this->testRunResultsStorage->buildTestingResultsEnvironment(TRUE);
|
||||
$this->assertTrue($schema->tableExists('simpletest'));
|
||||
$this->assertTrue($schema->tableExists('simpletest_test_id'));
|
||||
$this->assertTrue($this->testRunResultsStorage->validateTestingResultsEnvironment());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
$this->assertEquals(2, $this->testRunResultsStorage->createNew());
|
||||
$test_run = TestRun::get($this->testRunResultsStorage, 2);
|
||||
$this->assertEquals(2, $this->testRunResultsStorage->insertLogEntry($test_run, $this->getTestLogEntry('Test\GroundControl')));
|
||||
$this->assertEquals(2, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(2, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
// Cleanup the environment.
|
||||
$this->assertEquals(2, $this->testRunResultsStorage->cleanUp());
|
||||
$this->assertEquals(0, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(0, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildTestingResultsEnvironment
|
||||
* @covers ::createNew
|
||||
* @covers ::insertLogEntry
|
||||
* @covers ::setDatabasePrefix
|
||||
* @covers ::removeResults
|
||||
*/
|
||||
public function testGetCurrentTestRunState(): void {
|
||||
$this->testRunResultsStorage->buildTestingResultsEnvironment(FALSE);
|
||||
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->createNew());
|
||||
$test_run_1 = TestRun::get($this->testRunResultsStorage, 1);
|
||||
$this->testRunResultsStorage->setDatabasePrefix($test_run_1, 'oddity1234');
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->insertLogEntry($test_run_1, $this->getTestLogEntry('Test\GroundControl')));
|
||||
$this->assertEquals([
|
||||
'db_prefix' => 'oddity1234',
|
||||
'test_class' => 'Test\GroundControl',
|
||||
], $this->testRunResultsStorage->getCurrentTestRunState($test_run_1));
|
||||
|
||||
// Add another test run.
|
||||
$this->assertEquals(2, $this->testRunResultsStorage->createNew());
|
||||
$test_run_2 = TestRun::get($this->testRunResultsStorage, 2);
|
||||
$this->assertEquals(2, $this->testRunResultsStorage->insertLogEntry($test_run_2, $this->getTestLogEntry('Test\GroundControl')));
|
||||
|
||||
// Remove test run 1 results.
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->removeResults($test_run_1));
|
||||
$this->assertEquals(1, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildTestingResultsEnvironment
|
||||
* @covers ::createNew
|
||||
* @covers ::insertLogEntry
|
||||
* @covers ::setDatabasePrefix
|
||||
* @covers ::getLogEntriesByTestClass
|
||||
*/
|
||||
public function testGetLogEntriesByTestClass(): void {
|
||||
$this->testRunResultsStorage->buildTestingResultsEnvironment(FALSE);
|
||||
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->createNew());
|
||||
$test_run = TestRun::get($this->testRunResultsStorage, 1);
|
||||
$this->testRunResultsStorage->setDatabasePrefix($test_run, 'oddity1234');
|
||||
$this->assertEquals(1, $this->testRunResultsStorage->insertLogEntry($test_run, $this->getTestLogEntry('Test\PlanetEarth')));
|
||||
$this->assertEquals(2, $this->testRunResultsStorage->insertLogEntry($test_run, $this->getTestLogEntry('Test\GroundControl')));
|
||||
$this->assertEquals([
|
||||
0 => (object) [
|
||||
'message_id' => 2,
|
||||
'test_id' => 1,
|
||||
'test_class' => 'Test\GroundControl',
|
||||
'status' => 'pass',
|
||||
'message' => 'Major Tom',
|
||||
'message_group' => 'other',
|
||||
'function' => 'Unknown',
|
||||
'line' => 0,
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
1 => (object) [
|
||||
'message_id' => 1,
|
||||
'test_id' => 1,
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'pass',
|
||||
'message' => 'Major Tom',
|
||||
'message_group' => 'other',
|
||||
'function' => 'Unknown',
|
||||
'line' => 0,
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
], $this->testRunResultsStorage->getLogEntriesByTestClass($test_run));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sample test run log entry.
|
||||
*
|
||||
* @param string $test_class
|
||||
* The test class.
|
||||
*
|
||||
* @return string[]
|
||||
* An array with the elements to be logged.
|
||||
*/
|
||||
protected function getTestLogEntry(string $test_class): array {
|
||||
return [
|
||||
'test_class' => $test_class,
|
||||
'status' => 'pass',
|
||||
'message' => 'Major Tom',
|
||||
'message_group' => 'other',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Test;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\JUnitConverter;
|
||||
use Drupal\Core\Test\PhpUnitTestRunner;
|
||||
use Drupal\Core\Test\TestRun;
|
||||
use Drupal\Core\Test\SimpletestTestRunResultsStorage;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Test\TestRun
|
||||
* @group Test
|
||||
*/
|
||||
class TestRunTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The database connection for testing.
|
||||
*
|
||||
* NOTE: this is the connection to the fixture database to allow testing the
|
||||
* storage class, NOT the database where actual tests results are stored.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The test run results storage.
|
||||
*
|
||||
* @var \Drupal\Core\Test\TestRunResultsStorageInterface
|
||||
*/
|
||||
protected $testRunResultsStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->connection = Database::getConnection();
|
||||
$this->testRunResultsStorage = new SimpletestTestRunResultsStorage($this->connection);
|
||||
$this->testRunResultsStorage->buildTestingResultsEnvironment(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::createNew
|
||||
* @covers ::get
|
||||
* @covers ::id
|
||||
* @covers ::insertLogEntry
|
||||
* @covers ::setDatabasePrefix
|
||||
* @covers ::getDatabasePrefix
|
||||
* @covers ::getTestClass
|
||||
*/
|
||||
public function testCreateAndGet(): void {
|
||||
// Test ::createNew.
|
||||
$test_run = TestRun::createNew($this->testRunResultsStorage);
|
||||
$this->assertEquals(1, $test_run->id());
|
||||
$this->assertEquals(0, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
$test_run->setDatabasePrefix('oddity1234');
|
||||
$this->assertEquals('oddity1234', $test_run->getDatabasePrefix());
|
||||
$this->assertEquals('oddity1234', $this->connection->select('simpletest_test_id', 's')->fields('s', ['last_prefix'])->execute()->fetchField());
|
||||
|
||||
$this->assertEquals(1, $test_run->insertLogEntry($this->getTestLogEntry('Test\GroundControl')));
|
||||
$this->assertEquals('oddity1234', $test_run->getDatabasePrefix());
|
||||
$this->assertEquals('Test\GroundControl', $test_run->getTestClass());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
// Explicitly void the $test_run variable.
|
||||
$test_run = NULL;
|
||||
|
||||
// Test ::get.
|
||||
$test_run = TestRun::get($this->testRunResultsStorage, 1);
|
||||
$this->assertEquals(1, $test_run->id());
|
||||
$this->assertEquals('oddity1234', $test_run->getDatabasePrefix());
|
||||
$this->assertEquals('Test\GroundControl', $test_run->getTestClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::createNew
|
||||
* @covers ::id
|
||||
* @covers ::insertLogEntry
|
||||
* @covers ::setDatabasePrefix
|
||||
*/
|
||||
public function testCreateAndRemove(): void {
|
||||
$test_run_1 = TestRun::createNew($this->testRunResultsStorage);
|
||||
$test_run_1->setDatabasePrefix('oddity1234');
|
||||
$test_run_1->insertLogEntry($this->getTestLogEntry('Test\GroundControl'));
|
||||
$this->assertEquals(1, $test_run_1->id());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
$test_run_2 = TestRun::createNew($this->testRunResultsStorage);
|
||||
$test_run_2->setDatabasePrefix('oddity5678');
|
||||
$test_run_2->insertLogEntry($this->getTestLogEntry('Test\PlanetEarth'));
|
||||
$this->assertEquals(2, $test_run_2->id());
|
||||
$this->assertEquals(2, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(2, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
|
||||
$this->assertEquals(1, $test_run_1->removeResults());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
$this->assertEquals(1, $this->connection->select('simpletest_test_id')->countQuery()->execute()->fetchField());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::createNew
|
||||
* @covers ::insertLogEntry
|
||||
* @covers ::setDatabasePrefix
|
||||
* @covers ::getLogEntriesByTestClass
|
||||
* @covers ::getDatabasePrefix
|
||||
* @covers ::getTestClass
|
||||
*/
|
||||
public function testGetLogEntriesByTestClass(): void {
|
||||
$test_run = TestRun::createNew($this->testRunResultsStorage);
|
||||
$test_run->setDatabasePrefix('oddity1234');
|
||||
$this->assertEquals(1, $test_run->insertLogEntry($this->getTestLogEntry('Test\PlanetEarth')));
|
||||
$this->assertEquals(2, $test_run->insertLogEntry($this->getTestLogEntry('Test\GroundControl')));
|
||||
$this->assertEquals([
|
||||
0 => (object) [
|
||||
'message_id' => 2,
|
||||
'test_id' => 1,
|
||||
'test_class' => 'Test\GroundControl',
|
||||
'status' => 'pass',
|
||||
'message' => 'Major Tom',
|
||||
'message_group' => 'other',
|
||||
'function' => 'Unknown',
|
||||
'line' => 0,
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
1 => (object) [
|
||||
'message_id' => 1,
|
||||
'test_id' => 1,
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'pass',
|
||||
'message' => 'Major Tom',
|
||||
'message_group' => 'other',
|
||||
'function' => 'Unknown',
|
||||
'line' => 0,
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
], $test_run->getLogEntriesByTestClass());
|
||||
$this->assertEquals('oddity1234', $test_run->getDatabasePrefix());
|
||||
$this->assertEquals('Test\GroundControl', $test_run->getTestClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::createNew
|
||||
* @covers ::setDatabasePrefix
|
||||
* @covers ::processPhpErrorLogFile
|
||||
* @covers ::getLogEntriesByTestClass
|
||||
*/
|
||||
public function testProcessPhpErrorLogFile(): void {
|
||||
$test_run = TestRun::createNew($this->testRunResultsStorage);
|
||||
$test_run->setDatabasePrefix('oddity1234');
|
||||
$test_run->processPhpErrorLogFile('core/tests/fixtures/test-error.log', 'Test\PlanetEarth');
|
||||
$this->assertEquals([
|
||||
0 => (object) [
|
||||
'message_id' => '1',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "Argument 1 passed to Drupal\FunctionalTests\Bootstrap\ErrorContainer::Drupal\FunctionalTests\Bootstrap\{closure}() must be an instance of Drupal\FunctionalTests\Bootstrap\ErrorContainer, int given, called",
|
||||
'message_group' => 'TypeError',
|
||||
'function' => 'Unknown',
|
||||
'line' => '18',
|
||||
'file' => '/var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ErrorContainer.php on line 20 in /var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ErrorContainer.php',
|
||||
],
|
||||
1 => (object) [
|
||||
'message_id' => '2',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#1 /var/www/core/lib/Drupal/Core/DrupalKernel.php(1396): Drupal\FunctionalTests\Bootstrap\ErrorContainer->get('http_kernel')\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
2 => (object) [
|
||||
'message_id' => '3',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#2 /var/www/core/lib/Drupal/Core/DrupalKernel.php(693): Drupal\Core\DrupalKernel->getHttpKernel()\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
3 => (object) [
|
||||
'message_id' => '4',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#3 /var/www/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
4 => (object) [
|
||||
'message_id' => '5',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#4 {main}\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
5 => (object) [
|
||||
'message_id' => '6',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "Thrown exception during Container::get",
|
||||
'message_group' => 'Exception',
|
||||
'function' => 'Unknown',
|
||||
'line' => '17',
|
||||
'file' => '/var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ExceptionContainer.php',
|
||||
],
|
||||
6 => (object) [
|
||||
'message_id' => '7',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#1 /var/www/core/lib/Drupal/Core/DrupalKernel.php(693): Drupal\Core\DrupalKernel->getHttpKernel()\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
7 => (object) [
|
||||
'message_id' => '8',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#2 /var/www/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
8 => (object) [
|
||||
'message_id' => '9',
|
||||
'test_id' => '1',
|
||||
'test_class' => 'Test\PlanetEarth',
|
||||
'status' => 'fail',
|
||||
'message' => "#3 {main}\n",
|
||||
'message_group' => 'Fatal error',
|
||||
'function' => 'Unknown',
|
||||
'line' => '0',
|
||||
'file' => 'Unknown',
|
||||
],
|
||||
], $test_run->getLogEntriesByTestClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertLogEntry
|
||||
*/
|
||||
public function testProcessPhpUnitResults(): void {
|
||||
$phpunit_error_xml = __DIR__ . '/../../../Tests/Core/Test/fixtures/phpunit_error.xml';
|
||||
$res = JUnitConverter::xmlToRows(1, $phpunit_error_xml);
|
||||
|
||||
$runner = PhpUnitTestRunner::create(\Drupal::getContainer());
|
||||
$test_run = TestRun::createNew($this->testRunResultsStorage);
|
||||
$runner->processPhpUnitResults($test_run, $res);
|
||||
|
||||
$this->assertEquals(4, $this->connection->select('simpletest')->countQuery()->execute()->fetchField());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sample test run log entry.
|
||||
*
|
||||
* @param string $test_class
|
||||
* The test class.
|
||||
*
|
||||
* @return string[]
|
||||
* An array with the elements to be logged.
|
||||
*/
|
||||
protected function getTestLogEntry(string $test_class): array {
|
||||
return [
|
||||
'test_class' => $test_class,
|
||||
'status' => 'pass',
|
||||
'message' => 'Major Tom',
|
||||
'message_group' => 'other',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Test;
|
||||
|
||||
use Drupal\Core\Test\TestSetupTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the TestSetupTrait trait.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Core\Test\TestSetupTrait
|
||||
* @group Testing
|
||||
*
|
||||
* Run in a separate process as this test involves Database statics and
|
||||
* environment variables.
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
class TestSetupTraitTest extends KernelTestBase {
|
||||
|
||||
use TestSetupTrait;
|
||||
|
||||
/**
|
||||
* @covers ::getDatabaseConnection
|
||||
* @group legacy
|
||||
*/
|
||||
public function testGetDatabaseConnection(): void {
|
||||
$this->expectDeprecation('Drupal\Core\Test\TestSetupTrait::getDatabaseConnection is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. There is no replacement. See https://www.drupal.org/node/3176816');
|
||||
$this->assertNotNull($this->getDatabaseConnection());
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\Tests\Core\Test;
|
||||
|
||||
use Drupal\Core\Test\PhpUnitTestRunner;
|
||||
use Drupal\Core\Test\SimpletestTestRunResultsStorage;
|
||||
use Drupal\Core\Test\TestRun;
|
||||
use Drupal\Core\Test\TestStatus;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
|
@ -17,12 +19,23 @@ class PhpUnitTestRunnerTest extends UnitTestCase {
|
|||
/**
|
||||
* Tests an error in the test running phase.
|
||||
*
|
||||
* @covers ::runTests
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function testRunTestsError() {
|
||||
$test_id = 23;
|
||||
$log_path = 'test_log_path';
|
||||
|
||||
// Create a mock test run storage.
|
||||
$storage = $this->getMockBuilder(SimpletestTestRunResultsStorage::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['createNew'])
|
||||
->getMock();
|
||||
|
||||
// Set some expectations for createNew().
|
||||
$storage->expects($this->once())
|
||||
->method('createNew')
|
||||
->willReturn($test_id);
|
||||
|
||||
// Create a mock runner.
|
||||
$runner = $this->getMockBuilder(PhpUnitTestRunner::class)
|
||||
->disableOriginalConstructor()
|
||||
|
@ -40,13 +53,15 @@ class PhpUnitTestRunnerTest extends UnitTestCase {
|
|||
->willReturnCallback(
|
||||
function ($unescaped_test_classnames, $phpunit_file, &$status) {
|
||||
$status = TestStatus::EXCEPTION;
|
||||
return ' ';
|
||||
}
|
||||
);
|
||||
|
||||
// The runTests() method expects $status by reference, so we initialize it
|
||||
// The execute() method expects $status by reference, so we initialize it
|
||||
// to some value we don't expect back.
|
||||
$status = -1;
|
||||
$results = $runner->runTests($test_id, ['SomeTest'], $status);
|
||||
$test_run = TestRun::createNew($storage);
|
||||
$results = $runner->execute($test_run, ['SomeTest'], $status);
|
||||
|
||||
// Make sure our status code made the round trip.
|
||||
$this->assertEquals(TestStatus::EXCEPTION, $status);
|
||||
|
|
|
@ -27,6 +27,7 @@ class TestDatabaseTest extends UnitTestCase {
|
|||
* @covers ::__construct
|
||||
* @covers ::getDatabasePrefix
|
||||
* @covers ::getTestSitePath
|
||||
* @covers ::getPhpErrorLogPath
|
||||
*
|
||||
* @dataProvider providerTestConstructor
|
||||
*/
|
||||
|
@ -34,6 +35,7 @@ class TestDatabaseTest extends UnitTestCase {
|
|||
$test_db = new TestDatabase($db_prefix);
|
||||
$this->assertEquals($expected_db_prefix, $test_db->getDatabasePrefix());
|
||||
$this->assertEquals($expected_site_path, $test_db->getTestSitePath());
|
||||
$this->assertEquals($expected_site_path . '/error.log', $test_db->getPhpErrorLogPath());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,6 +52,9 @@ class TestDatabaseTest extends UnitTestCase {
|
|||
* Verify that a test lock is generated if there is no provided prefix.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getDatabasePrefix
|
||||
* @covers ::getTestSitePath
|
||||
* @covers ::getPhpErrorLogPath
|
||||
*/
|
||||
public function testConstructorNullPrefix() {
|
||||
// We use a stub class here because we can't mock getTestLock() so that it's
|
||||
|
@ -58,6 +63,7 @@ class TestDatabaseTest extends UnitTestCase {
|
|||
|
||||
$this->assertEquals('test23', $test_db->getDatabasePrefix());
|
||||
$this->assertEquals('sites/simpletest/23', $test_db->getTestSitePath());
|
||||
$this->assertEquals('sites/simpletest/23/error.log', $test_db->getPhpErrorLogPath());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,7 +73,7 @@ class TestDatabaseTest extends UnitTestCase {
|
|||
*/
|
||||
class TestTestDatabase extends TestDatabase {
|
||||
|
||||
protected function getTestLock($create_lock = FALSE) {
|
||||
protected function getTestLock(bool $create_lock = FALSE): int {
|
||||
return 23;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[14-Sep-2019 12:39:18 UTC] TypeError: Argument 1 passed to Drupal\FunctionalTests\Bootstrap\ErrorContainer::Drupal\FunctionalTests\Bootstrap\{closure}() must be an instance of Drupal\FunctionalTests\Bootstrap\ErrorContainer, int given, called in /var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ErrorContainer.php on line 20 in /var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ErrorContainer.php on line 18 #0 /var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ErrorContainer.php(20): Drupal\FunctionalTests\Bootstrap\ErrorContainer->Drupal\FunctionalTests\Bootstrap\{closure}(1)
|
||||
#1 /var/www/core/lib/Drupal/Core/DrupalKernel.php(1396): Drupal\FunctionalTests\Bootstrap\ErrorContainer->get('http_kernel')
|
||||
#2 /var/www/core/lib/Drupal/Core/DrupalKernel.php(693): Drupal\Core\DrupalKernel->getHttpKernel()
|
||||
#3 /var/www/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
|
||||
#4 {main}
|
||||
[14-Sep-2019 12:39:22 UTC] Exception: Thrown exception during Container::get in /var/www/core/tests/Drupal/FunctionalTests/Bootstrap/ExceptionContainer.php on line 17 #0 /var/www/core/lib/Drupal/Core/DrupalKernel.php(1396): Drupal\FunctionalTests\Bootstrap\ExceptionContainer->get('http_kernel')
|
||||
#1 /var/www/core/lib/Drupal/Core/DrupalKernel.php(693): Drupal\Core\DrupalKernel->getHttpKernel()
|
||||
#2 /var/www/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
|
||||
#3 {main}
|
Loading…
Reference in New Issue