Version 1
[yaffs-website] / web / core / tests / Drupal / Tests / Listeners / DrupalStandardsListener.php
1 <?php
2
3 namespace Drupal\Tests\Listeners;
4
5 /**
6  * Listens for PHPUnit tests and fails those with invalid coverage annotations.
7  *
8  * Enforces various coding standards within test runs.
9  */
10 class DrupalStandardsListener extends \PHPUnit_Framework_BaseTestListener {
11
12   /**
13    * Signals a coding standards failure to the user.
14    *
15    * @param \PHPUnit_Framework_TestCase $test
16    *   The test where we should insert our test failure.
17    * @param string $message
18    *   The message to add to the failure notice. The test class name and test
19    *   name will be appended to this message automatically.
20    */
21   protected function fail(\PHPUnit_Framework_TestCase $test, $message) {
22     // Add the report to the test's results.
23     $message .= ': ' . get_class($test) . '::' . $test->getName();
24     $fail = new \PHPUnit_Framework_AssertionFailedError($message);
25     $result = $test->getTestResultObject();
26     $result->addFailure($test, $fail, 0);
27   }
28
29   /**
30    * Helper method to check if a string names a valid class or trait.
31    *
32    * @param string $class
33    *   Name of the class to check.
34    *
35    * @return bool
36    *   TRUE if the class exists, FALSE otherwise.
37    */
38   protected function classExists($class) {
39     return class_exists($class, TRUE) || trait_exists($class, TRUE) || interface_exists($class, TRUE);
40   }
41
42   /**
43    * Check an individual test run for valid @covers annotation.
44    *
45    * This method is called from $this::endTest().
46    *
47    * @param \PHPUnit_Framework_TestCase $test
48    *   The test to examine.
49    */
50   public function checkValidCoversForTest(\PHPUnit_Framework_TestCase $test) {
51     // If we're generating a coverage report already, don't do anything here.
52     if ($test->getTestResultObject() && $test->getTestResultObject()->getCollectCodeCoverageInformation()) {
53       return;
54     }
55     // Gather our annotations.
56     $annotations = $test->getAnnotations();
57     // Glean the @coversDefaultClass annotation.
58     $default_class = '';
59     $valid_default_class = FALSE;
60     if (isset($annotations['class']['coversDefaultClass'])) {
61       if (count($annotations['class']['coversDefaultClass']) > 1) {
62         $this->fail($test, '@coversDefaultClass has too many values');
63       }
64       // Grab the first one.
65       $default_class = reset($annotations['class']['coversDefaultClass']);
66       // Check whether the default class exists.
67       $valid_default_class = $this->classExists($default_class);
68       if (!$valid_default_class) {
69         $this->fail($test, "@coversDefaultClass does not exist '$default_class'");
70       }
71     }
72     // Glean @covers annotation.
73     if (isset($annotations['method']['covers'])) {
74       // Drupal allows multiple @covers per test method, so we have to check
75       // them all.
76       foreach ($annotations['method']['covers'] as $covers) {
77         // Ensure the annotation isn't empty.
78         if (trim($covers) === '') {
79           $this->fail($test, '@covers should not be empty');
80           // If @covers is empty, we can't proceed.
81           return;
82         }
83         // Ensure we don't have ().
84         if (strpos($covers, '()') !== FALSE) {
85           $this->fail($test, "@covers invalid syntax: Do not use '()'");
86         }
87         // Glean the class and method from @covers.
88         $class = $covers;
89         $method = '';
90         if (strpos($covers, '::') !== FALSE) {
91           list($class, $method) = explode('::', $covers);
92         }
93         // Check for the existence of the class if it's specified by @covers.
94         if (!empty($class)) {
95           // If the class doesn't exist we have either a bad classname or
96           // are missing the :: for a method. Either way we can't proceed.
97           if (!$this->classExists($class)) {
98             if (empty($method)) {
99               $this->fail($test, "@covers invalid syntax: Needs '::' or class does not exist in $covers");
100               return;
101             }
102             else {
103               $this->fail($test, '@covers class does not exist ' . $class);
104               return;
105             }
106           }
107         }
108         else {
109           // The class isn't specified and we have the ::, so therefore this
110           // test either covers a function, or relies on a default class.
111           if (empty($default_class)) {
112             // If there's no default class, then we need to check if the global
113             // function exists. Since this listener should always be listening
114             // for endTest(), the function should have already been loaded from
115             // its .module or .inc file.
116             if (!function_exists($method)) {
117               $this->fail($test, '@covers global method does not exist ' . $method);
118             }
119           }
120           else {
121             // We have a default class and this annotation doesn't act like a
122             // global function, so we should use the default class if it's
123             // valid.
124             if ($valid_default_class) {
125               $class = $default_class;
126             }
127           }
128         }
129         // Finally, after all that, let's see if the method exists.
130         if (!empty($class) && !empty($method)) {
131           $ref_class = new \ReflectionClass($class);
132           if (!$ref_class->hasMethod($method)) {
133             $this->fail($test, '@covers method does not exist ' . $class . '::' . $method);
134           }
135         }
136       }
137     }
138   }
139
140   /**
141    * {@inheritdoc}
142    */
143   public function endTest(\PHPUnit_Framework_Test $test, $time) {
144     // \PHPUnit_Framework_Test does not have any useful methods of its own for
145     // our purpose, so we have to distinguish between the different known
146     // subclasses.
147     if ($test instanceof \PHPUnit_Framework_TestCase) {
148       $this->checkValidCoversForTest($test);
149     }
150     elseif ($test instanceof \PHPUnit_Framework_TestSuite) {
151       foreach ($test->getGroupDetails() as $tests) {
152         foreach ($tests as $test) {
153           $this->endTest($test, $time);
154         }
155       }
156     }
157   }
158
159 }