function DrupalStandardsListenerTrait::checkValidCoversForTest

Same name and namespace in other branches
  1. 8.9.x core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php \Drupal\Tests\Listeners\DrupalStandardsListenerTrait::checkValidCoversForTest()

Check an individual test run for valid @covers annotation.

This method is called from $this::endTest().

Parameters

\PHPUnit\Framework\TestCase $test: The test to examine.

1 call to DrupalStandardsListenerTrait::checkValidCoversForTest()
DrupalStandardsListenerTrait::doEndTest in core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
Reacts to the end of a test.

File

core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php, line 58

Class

DrupalStandardsListenerTrait
Listens for PHPUnit tests and fails those with invalid coverage annotations.

Namespace

Drupal\Tests\Listeners

Code

private function checkValidCoversForTest(TestCase $test) {
    // If we're generating a coverage report already, don't do anything here.
    if ($test->getTestResultObject() && $test->getTestResultObject()
        ->getCollectCodeCoverageInformation()) {
        return;
    }
    // Gather our annotations.
    $annotations = Test::parseTestMethodAnnotations(static::class, $test->getName());
    // Glean the @coversDefaultClass annotation.
    $default_class = '';
    $valid_default_class = FALSE;
    if (isset($annotations['class']['coversDefaultClass'])) {
        if (count($annotations['class']['coversDefaultClass']) > 1) {
            $this->fail($test, '@coversDefaultClass has too many values');
        }
        // Grab the first one.
        $default_class = reset($annotations['class']['coversDefaultClass']);
        // Check whether the default class exists.
        $valid_default_class = $this->classExists($default_class);
        if (!$valid_default_class && interface_exists($default_class)) {
            $this->fail($test, "@coversDefaultClass refers to an interface '{$default_class}' and those can not be tested.");
        }
        elseif (!$valid_default_class) {
            $this->fail($test, "@coversDefaultClass does not exist '{$default_class}'");
        }
    }
    // Glean @covers annotation.
    if (isset($annotations['method']['covers'])) {
        // Drupal allows multiple @covers per test method, so we have to check
        // them all.
        foreach ($annotations['method']['covers'] as $covers) {
            // Ensure the annotation isn't empty.
            if (trim($covers) === '') {
                $this->fail($test, '@covers should not be empty');
                // If @covers is empty, we can't proceed.
                return;
            }
            // Ensure we don't have ().
            if (strpos($covers, '()') !== FALSE) {
                $this->fail($test, "@covers invalid syntax: Do not use '()'");
            }
            // Glean the class and method from @covers.
            $class = $covers;
            $method = '';
            if (strpos($covers, '::') !== FALSE) {
                [
                    $class,
                    $method,
                ] = explode('::', $covers);
            }
            // Check for the existence of the class if it's specified by @covers.
            if (!empty($class)) {
                // If the class doesn't exist we have either a bad classname or
                // are missing the :: for a method. Either way we can't proceed.
                if (!$this->classExists($class)) {
                    if (empty($method)) {
                        $this->fail($test, "@covers invalid syntax: Needs '::' or class does not exist in {$covers}");
                        return;
                    }
                    elseif (interface_exists($class)) {
                        $this->fail($test, "@covers refers to an interface '{$class}' and those can not be tested.");
                    }
                    else {
                        $this->fail($test, '@covers class does not exist ' . $class);
                        return;
                    }
                }
            }
            else {
                // The class isn't specified and we have the ::, so therefore this
                // test either covers a function, or relies on a default class.
                if (empty($default_class)) {
                    // If there's no default class, then we need to check if the global
                    // function exists. Since this listener should always be listening
                    // for endTest(), the function should have already been loaded from
                    // its .module or .inc file.
                    if (!function_exists($method)) {
                        $this->fail($test, '@covers global method does not exist ' . $method);
                    }
                }
                else {
                    // We have a default class and this annotation doesn't act like a
                    // global function, so we should use the default class if it's
                    // valid.
                    if ($valid_default_class) {
                        $class = $default_class;
                    }
                }
            }
            // Finally, after all that, let's see if the method exists.
            if (!empty($class) && !empty($method)) {
                $ref_class = new \ReflectionClass($class);
                if (!$ref_class->hasMethod($method)) {
                    $this->fail($test, '@covers method does not exist ' . $class . '::' . $method);
                }
            }
        }
    }
}

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