SimpletestTestRunResultsStorage.php

Same filename and directory in other branches
  1. 10 core/lib/Drupal/Core/Test/SimpletestTestRunResultsStorage.php

Namespace

Drupal\Core\Test

File

core/lib/Drupal/Core/Test/SimpletestTestRunResultsStorage.php

View source
<?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(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;
    }

}

Classes

Title Deprecated Summary
SimpletestTestRunResultsStorage Implements a test run results storage compatible with legacy Simpletest.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.