function DrupalStandardsListenerTrait::checkValidCoversForTest

Same name and namespace in other branches
  1. 9 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 57

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->getAnnotations();
  // 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) {
        list($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.