74203b76101574fc7f5adcb612a5a7a75a4723c4
[yaffs-website] / Test.php
1 <?php
2 /*
3  * This file is part of PHPUnit.
4  *
5  * (c) Sebastian Bergmann <sebastian@phpunit.de>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 if (!function_exists('trait_exists')) {
12     function trait_exists($traitname, $autoload = true)
13     {
14         return false;
15     }
16 }
17
18 /**
19  * Test helpers.
20  *
21  * @since Class available since Release 3.0.0
22  */
23 class PHPUnit_Util_Test
24 {
25     const REGEX_DATA_PROVIDER      = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
26     const REGEX_TEST_WITH          = '/@testWith\s+/';
27     const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
28     const REGEX_REQUIRES_VERSION   = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<value>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
29     const REGEX_REQUIRES_OS        = '/@requires\s+OS\s+(?P<value>.+?)[ \t]*\r?$/m';
30     const REGEX_REQUIRES           = '/@requires\s+(?P<name>function|extension)\s+(?P<value>([^ ]+?))[ \t]*\r?$/m';
31
32     const UNKNOWN = -1;
33     const SMALL   = 0;
34     const MEDIUM  = 1;
35     const LARGE   = 2;
36
37     private static $annotationCache = array();
38
39     private static $hookMethods = array();
40
41     /**
42      * @param PHPUnit_Framework_Test $test
43      * @param bool                   $asString
44      *
45      * @return mixed
46      */
47     public static function describe(PHPUnit_Framework_Test $test, $asString = true)
48     {
49         if ($asString) {
50             if ($test instanceof PHPUnit_Framework_SelfDescribing) {
51                 return $test->toString();
52             } else {
53                 return get_class($test);
54             }
55         } else {
56             if ($test instanceof PHPUnit_Framework_TestCase) {
57                 return array(
58                   get_class($test), $test->getName()
59                 );
60             } elseif ($test instanceof PHPUnit_Framework_SelfDescribing) {
61                 return array('', $test->toString());
62             } else {
63                 return array('', get_class($test));
64             }
65         }
66     }
67
68     /**
69      * @param string $className
70      * @param string $methodName
71      *
72      * @return array|bool
73      *
74      * @throws PHPUnit_Framework_CodeCoverageException
75      *
76      * @since  Method available since Release 4.0.0
77      */
78     public static function getLinesToBeCovered($className, $methodName)
79     {
80         $annotations = self::parseTestMethodAnnotations(
81             $className,
82             $methodName
83         );
84
85         if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
86             return false;
87         }
88
89         return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
90     }
91
92     /**
93      * Returns lines of code specified with the @uses annotation.
94      *
95      * @param string $className
96      * @param string $methodName
97      *
98      * @return array
99      *
100      * @since  Method available since Release 4.0.0
101      */
102     public static function getLinesToBeUsed($className, $methodName)
103     {
104         return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
105     }
106
107     /**
108      * @param string $className
109      * @param string $methodName
110      * @param string $mode
111      *
112      * @return array
113      *
114      * @throws PHPUnit_Framework_CodeCoverageException
115      *
116      * @since  Method available since Release 4.2.0
117      */
118     private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode)
119     {
120         $annotations = self::parseTestMethodAnnotations(
121             $className,
122             $methodName
123         );
124
125         $classShortcut = null;
126
127         if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
128             if (count($annotations['class'][$mode . 'DefaultClass']) > 1) {
129                 throw new PHPUnit_Framework_CodeCoverageException(
130                     sprintf(
131                         'More than one @%sClass annotation in class or interface "%s".',
132                         $mode,
133                         $className
134                     )
135                 );
136             }
137
138             $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
139         }
140
141         $list = array();
142
143         if (isset($annotations['class'][$mode])) {
144             $list = $annotations['class'][$mode];
145         }
146
147         if (isset($annotations['method'][$mode])) {
148             $list = array_merge($list, $annotations['method'][$mode]);
149         }
150
151         $codeList = array();
152
153         foreach (array_unique($list) as $element) {
154             if ($classShortcut && strncmp($element, '::', 2) === 0) {
155                 $element = $classShortcut . $element;
156             }
157
158             $element = preg_replace('/[\s()]+$/', '', $element);
159             $element = explode(' ', $element);
160             $element = $element[0];
161
162             $codeList = array_merge(
163                 $codeList,
164                 self::resolveElementToReflectionObjects($element)
165             );
166         }
167
168         return self::resolveReflectionObjectsToLines($codeList);
169     }
170
171     /**
172      * Returns the requirements for a test.
173      *
174      * @param string $className
175      * @param string $methodName
176      *
177      * @return array
178      *
179      * @since  Method available since Release 3.6.0
180      */
181     public static function getRequirements($className, $methodName)
182     {
183         $reflector  = new ReflectionClass($className);
184         $docComment = $reflector->getDocComment();
185         $reflector  = new ReflectionMethod($className, $methodName);
186         $docComment .= "\n" . $reflector->getDocComment();
187         $requires   = array();
188
189         if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) {
190             $requires['OS'] = sprintf(
191                 '/%s/i',
192                 addcslashes($matches['value'][$count - 1], '/')
193             );
194         }
195         if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) {
196             for ($i = 0; $i < $count; $i++) {
197                 $requires[$matches['name'][$i]] = $matches['value'][$i];
198             }
199         }
200
201         // https://bugs.php.net/bug.php?id=63055
202         $matches = array();
203
204         if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
205             for ($i = 0; $i < $count; $i++) {
206                 $name = $matches['name'][$i] . 's';
207                 if (!isset($requires[$name])) {
208                     $requires[$name] = array();
209                 }
210                 $requires[$name][] = $matches['value'][$i];
211             }
212         }
213
214         return $requires;
215     }
216
217     /**
218      * Returns the missing requirements for a test.
219      *
220      * @param string $className
221      * @param string $methodName
222      *
223      * @return array
224      *
225      * @since  Method available since Release 4.3.0
226      */
227     public static function getMissingRequirements($className, $methodName)
228     {
229         $required = static::getRequirements($className, $methodName);
230         $missing  = array();
231
232         if (!empty($required['PHP']) && version_compare(PHP_VERSION, $required['PHP'], '<')) {
233             $missing[] = sprintf('PHP %s (or later) is required.', $required['PHP']);
234         }
235
236         if (!empty($required['PHPUnit'])) {
237             $phpunitVersion = PHPUnit_Runner_Version::id();
238             if (version_compare($phpunitVersion, $required['PHPUnit'], '<')) {
239                 $missing[] = sprintf('PHPUnit %s (or later) is required.', $required['PHPUnit']);
240             }
241         }
242
243         if (!empty($required['OS']) && !preg_match($required['OS'], PHP_OS)) {
244             $missing[] = sprintf('Operating system matching %s is required.', $required['OS']);
245         }
246
247         if (!empty($required['functions'])) {
248             foreach ($required['functions'] as $function) {
249                 $pieces = explode('::', $function);
250                 if (2 === count($pieces) && method_exists($pieces[0], $pieces[1])) {
251                     continue;
252                 }
253                 if (function_exists($function)) {
254                     continue;
255                 }
256                 $missing[] = sprintf('Function %s is required.', $function);
257             }
258         }
259
260         if (!empty($required['extensions'])) {
261             foreach ($required['extensions'] as $extension) {
262                 if (!extension_loaded($extension)) {
263                     $missing[] = sprintf('Extension %s is required.', $extension);
264                 }
265             }
266         }
267
268         return $missing;
269     }
270
271     /**
272      * Returns the expected exception for a test.
273      *
274      * @param string $className
275      * @param string $methodName
276      *
277      * @return array
278      *
279      * @since  Method available since Release 3.3.6
280      */
281     public static function getExpectedException($className, $methodName)
282     {
283         $reflector  = new ReflectionMethod($className, $methodName);
284         $docComment = $reflector->getDocComment();
285         $docComment = substr($docComment, 3, -2);
286
287         if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
288             $annotations = self::parseTestMethodAnnotations(
289                 $className,
290                 $methodName
291             );
292
293             $class         = $matches[1];
294             $code          = null;
295             $message       = '';
296             $messageRegExp = '';
297
298             if (isset($matches[2])) {
299                 $message = trim($matches[2]);
300             } elseif (isset($annotations['method']['expectedExceptionMessage'])) {
301                 $message = self::parseAnnotationContent(
302                     $annotations['method']['expectedExceptionMessage'][0]
303                 );
304             }
305
306             if (isset($annotations['method']['expectedExceptionMessageRegExp'])) {
307                 $messageRegExp = self::parseAnnotationContent(
308                     $annotations['method']['expectedExceptionMessageRegExp'][0]
309                 );
310             }
311
312             if (isset($matches[3])) {
313                 $code = $matches[3];
314             } elseif (isset($annotations['method']['expectedExceptionCode'])) {
315                 $code = self::parseAnnotationContent(
316                     $annotations['method']['expectedExceptionCode'][0]
317                 );
318             }
319
320             if (is_numeric($code)) {
321                 $code = (int) $code;
322             } elseif (is_string($code) && defined($code)) {
323                 $code = (int) constant($code);
324             }
325
326             return array(
327               'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp
328             );
329         }
330
331         return false;
332     }
333
334     /**
335      * Parse annotation content to use constant/class constant values
336      *
337      * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
338      *
339      * If the constant is not found the string is used as is to ensure maximum BC.
340      *
341      * @param string $message
342      *
343      * @return string
344      */
345     private static function parseAnnotationContent($message)
346     {
347         if (strpos($message, '::') !== false && count(explode('::', $message)) == 2) {
348             if (defined($message)) {
349                 $message = constant($message);
350             }
351         }
352
353         return $message;
354     }
355
356     /**
357      * Returns the provided data for a method.
358      *
359      * @param string $className
360      * @param string $methodName
361      *
362      * @return array|Iterator when a data provider is specified and exists
363      *                        null           when no data provider is specified
364      *
365      * @throws PHPUnit_Framework_Exception
366      *
367      * @since  Method available since Release 3.2.0
368      */
369     public static function getProvidedData($className, $methodName)
370     {
371         $reflector  = new ReflectionMethod($className, $methodName);
372         $docComment = $reflector->getDocComment();
373
374         $data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName);
375
376         if ($data === null) {
377             $data = self::getDataFromTestWithAnnotation($docComment);
378         }
379
380         if (is_array($data) && empty($data)) {
381             throw new PHPUnit_Framework_SkippedTestError;
382         }
383
384         if ($data !== null) {
385             if (is_object($data)) {
386                 $data = iterator_to_array($data);
387             }
388
389             foreach ($data as $key => $value) {
390                 if (!is_array($value)) {
391                     throw new PHPUnit_Framework_Exception(
392                         sprintf(
393                             'Data set %s is invalid.',
394                             is_int($key) ? '#' . $key : '"' . $key . '"'
395                         )
396                     );
397                 }
398             }
399         }
400
401         return $data;
402     }
403
404     /**
405      * Returns the provided data for a method.
406      *
407      * @param string $docComment
408      * @param string $className
409      * @param string $methodName
410      *
411      * @return array|Iterator when a data provider is specified and exists
412      *                        null           when no data provider is specified
413      *
414      * @throws PHPUnit_Framework_Exception
415      */
416     private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName)
417     {
418         if (preg_match(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
419             $dataProviderMethodNameNamespace = explode('\\', $matches[1]);
420             $leaf                            = explode('::', array_pop($dataProviderMethodNameNamespace));
421             $dataProviderMethodName          = array_pop($leaf);
422
423             if (!empty($dataProviderMethodNameNamespace)) {
424                 $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\';
425             } else {
426                 $dataProviderMethodNameNamespace = '';
427             }
428
429             if (!empty($leaf)) {
430                 $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
431             } else {
432                 $dataProviderClassName = $className;
433             }
434
435             $dataProviderClass  = new ReflectionClass($dataProviderClassName);
436             $dataProviderMethod = $dataProviderClass->getMethod(
437                 $dataProviderMethodName
438             );
439
440             if ($dataProviderMethod->isStatic()) {
441                 $object = null;
442             } else {
443                 $object = $dataProviderClass->newInstance();
444             }
445
446             if ($dataProviderMethod->getNumberOfParameters() == 0) {
447                 $data = $dataProviderMethod->invoke($object);
448             } else {
449                 $data = $dataProviderMethod->invoke($object, $methodName);
450             }
451
452             return $data;
453         }
454     }
455
456     /**
457      * @param string $docComment full docComment string
458      *
459      * @return array when @testWith annotation is defined
460      *               null  when @testWith annotation is omitted
461      *
462      * @throws PHPUnit_Framework_Exception when @testWith annotation is defined but cannot be parsed
463      */
464     public static function getDataFromTestWithAnnotation($docComment)
465     {
466         $docComment = self::cleanUpMultiLineAnnotation($docComment);
467
468         if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
469             $offset            = strlen($matches[0][0]) + $matches[0][1];
470             $annotationContent = substr($docComment, $offset);
471             $data              = array();
472
473             foreach (explode("\n", $annotationContent) as $candidateRow) {
474                 $candidateRow = trim($candidateRow);
475
476                 if ($candidateRow[0] !== '[') {
477                     break;
478                 }
479
480                 $dataSet = json_decode($candidateRow, true);
481
482                 if (json_last_error() != JSON_ERROR_NONE) {
483                     $error = function_exists('json_last_error_msg') ? json_last_error_msg() : json_last_error();
484
485                     throw new PHPUnit_Framework_Exception(
486                         'The dataset for the @testWith annotation cannot be parsed: ' . $error
487                     );
488                 }
489
490                 $data[] = $dataSet;
491             }
492
493             if (!$data) {
494                 throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.');
495             }
496
497             return $data;
498         }
499     }
500
501     private static function cleanUpMultiLineAnnotation($docComment)
502     {
503         //removing initial '   * ' for docComment
504         $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
505         $docComment = substr($docComment, 0, -1);
506         $docComment = rtrim($docComment, "\n");
507
508         return $docComment;
509     }
510
511     /**
512      * @param string $className
513      * @param string $methodName
514      *
515      * @return array
516      *
517      * @throws ReflectionException
518      *
519      * @since  Method available since Release 3.4.0
520      */
521     public static function parseTestMethodAnnotations($className, $methodName = '')
522     {
523         if (!isset(self::$annotationCache[$className])) {
524             $class                             = new ReflectionClass($className);
525             self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
526         }
527
528         if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
529             try {
530                 $method      = new ReflectionMethod($className, $methodName);
531                 $annotations = self::parseAnnotations($method->getDocComment());
532             } catch (ReflectionException $e) {
533                 $annotations = array();
534             }
535             self::$annotationCache[$className . '::' . $methodName] = $annotations;
536         }
537
538         return array(
539           'class'  => self::$annotationCache[$className],
540           'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : array()
541         );
542     }
543
544     /**
545      * @param string $docblock
546      *
547      * @return array
548      *
549      * @since  Method available since Release 3.4.0
550      */
551     private static function parseAnnotations($docblock)
552     {
553         $annotations = array();
554         // Strip away the docblock header and footer to ease parsing of one line annotations
555         $docblock = substr($docblock, 3, -2);
556
557         if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
558             $numMatches = count($matches[0]);
559
560             for ($i = 0; $i < $numMatches; ++$i) {
561                 $annotations[$matches['name'][$i]][] = $matches['value'][$i];
562             }
563         }
564
565         return $annotations;
566     }
567
568     /**
569      * Returns the backup settings for a test.
570      *
571      * @param string $className
572      * @param string $methodName
573      *
574      * @return array
575      *
576      * @since  Method available since Release 3.4.0
577      */
578     public static function getBackupSettings($className, $methodName)
579     {
580         return array(
581           'backupGlobals' => self::getBooleanAnnotationSetting(
582               $className,
583               $methodName,
584               'backupGlobals'
585           ),
586           'backupStaticAttributes' => self::getBooleanAnnotationSetting(
587               $className,
588               $methodName,
589               'backupStaticAttributes'
590           )
591         );
592     }
593
594     /**
595      * Returns the dependencies for a test class or method.
596      *
597      * @param string $className
598      * @param string $methodName
599      *
600      * @return array
601      *
602      * @since  Method available since Release 3.4.0
603      */
604     public static function getDependencies($className, $methodName)
605     {
606         $annotations = self::parseTestMethodAnnotations(
607             $className,
608             $methodName
609         );
610
611         $dependencies = array();
612
613         if (isset($annotations['class']['depends'])) {
614             $dependencies = $annotations['class']['depends'];
615         }
616
617         if (isset($annotations['method']['depends'])) {
618             $dependencies = array_merge(
619                 $dependencies,
620                 $annotations['method']['depends']
621             );
622         }
623
624         return array_unique($dependencies);
625     }
626
627     /**
628      * Returns the error handler settings for a test.
629      *
630      * @param string $className
631      * @param string $methodName
632      *
633      * @return bool
634      *
635      * @since  Method available since Release 3.4.0
636      */
637     public static function getErrorHandlerSettings($className, $methodName)
638     {
639         return self::getBooleanAnnotationSetting(
640             $className,
641             $methodName,
642             'errorHandler'
643         );
644     }
645
646     /**
647      * Returns the groups for a test class or method.
648      *
649      * @param string $className
650      * @param string $methodName
651      *
652      * @return array
653      *
654      * @since  Method available since Release 3.2.0
655      */
656     public static function getGroups($className, $methodName = '')
657     {
658         $annotations = self::parseTestMethodAnnotations(
659             $className,
660             $methodName
661         );
662
663         $groups = array();
664
665         if (isset($annotations['method']['author'])) {
666             $groups = $annotations['method']['author'];
667         } elseif (isset($annotations['class']['author'])) {
668             $groups = $annotations['class']['author'];
669         }
670
671         if (isset($annotations['class']['group'])) {
672             $groups = array_merge($groups, $annotations['class']['group']);
673         }
674
675         if (isset($annotations['method']['group'])) {
676             $groups = array_merge($groups, $annotations['method']['group']);
677         }
678
679         if (isset($annotations['class']['ticket'])) {
680             $groups = array_merge($groups, $annotations['class']['ticket']);
681         }
682
683         if (isset($annotations['method']['ticket'])) {
684             $groups = array_merge($groups, $annotations['method']['ticket']);
685         }
686
687         foreach (array('method', 'class') as $element) {
688             foreach (array('small', 'medium', 'large') as $size) {
689                 if (isset($annotations[$element][$size])) {
690                     $groups[] = $size;
691                     break 2;
692                 }
693
694                 if (isset($annotations[$element][$size])) {
695                     $groups[] = $size;
696                     break 2;
697                 }
698             }
699         }
700
701         return array_unique($groups);
702     }
703
704     /**
705      * Returns the size of the test.
706      *
707      * @param string $className
708      * @param string $methodName
709      *
710      * @return int
711      *
712      * @since  Method available since Release 3.6.0
713      */
714     public static function getSize($className, $methodName)
715     {
716         $groups = array_flip(self::getGroups($className, $methodName));
717         $size   = self::UNKNOWN;
718         $class  = new ReflectionClass($className);
719
720         if (isset($groups['large']) ||
721             (class_exists('PHPUnit_Extensions_Database_TestCase', false) &&
722              $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase')) ||
723             (class_exists('PHPUnit_Extensions_SeleniumTestCase', false) &&
724              $class->isSubclassOf('PHPUnit_Extensions_SeleniumTestCase'))) {
725             $size = self::LARGE;
726         } elseif (isset($groups['medium'])) {
727             $size = self::MEDIUM;
728         } elseif (isset($groups['small'])) {
729             $size = self::SMALL;
730         }
731
732         return $size;
733     }
734
735     /**
736      * Returns the tickets for a test class or method.
737      *
738      * @param string $className
739      * @param string $methodName
740      *
741      * @return array
742      *
743      * @since  Method available since Release 3.4.0
744      */
745     public static function getTickets($className, $methodName)
746     {
747         $annotations = self::parseTestMethodAnnotations(
748             $className,
749             $methodName
750         );
751
752         $tickets = array();
753
754         if (isset($annotations['class']['ticket'])) {
755             $tickets = $annotations['class']['ticket'];
756         }
757
758         if (isset($annotations['method']['ticket'])) {
759             $tickets = array_merge($tickets, $annotations['method']['ticket']);
760         }
761
762         return array_unique($tickets);
763     }
764
765     /**
766      * Returns the process isolation settings for a test.
767      *
768      * @param string $className
769      * @param string $methodName
770      *
771      * @return bool
772      *
773      * @since  Method available since Release 3.4.1
774      */
775     public static function getProcessIsolationSettings($className, $methodName)
776     {
777         $annotations = self::parseTestMethodAnnotations(
778             $className,
779             $methodName
780         );
781
782         if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
783             isset($annotations['method']['runInSeparateProcess'])) {
784             return true;
785         } else {
786             return false;
787         }
788     }
789
790     /**
791      * Returns the preserve global state settings for a test.
792      *
793      * @param string $className
794      * @param string $methodName
795      *
796      * @return bool
797      *
798      * @since  Method available since Release 3.4.0
799      */
800     public static function getPreserveGlobalStateSettings($className, $methodName)
801     {
802         return self::getBooleanAnnotationSetting(
803             $className,
804             $methodName,
805             'preserveGlobalState'
806         );
807     }
808
809     /**
810      * @param string $className
811      *
812      * @return array
813      *
814      * @since  Method available since Release 4.0.8
815      */
816     public static function getHookMethods($className)
817     {
818         if (!class_exists($className, false)) {
819             return self::emptyHookMethodsArray();
820         }
821
822         if (!isset(self::$hookMethods[$className])) {
823             self::$hookMethods[$className] = self::emptyHookMethodsArray();
824
825             try {
826                 $class = new ReflectionClass($className);
827
828                 foreach ($class->getMethods() as $method) {
829                     if (self::isBeforeClassMethod($method)) {
830                         self::$hookMethods[$className]['beforeClass'][] = $method->getName();
831                     }
832
833                     if (self::isBeforeMethod($method)) {
834                         self::$hookMethods[$className]['before'][] = $method->getName();
835                     }
836
837                     if (self::isAfterMethod($method)) {
838                         self::$hookMethods[$className]['after'][] = $method->getName();
839                     }
840
841                     if (self::isAfterClassMethod($method)) {
842                         self::$hookMethods[$className]['afterClass'][] = $method->getName();
843                     }
844                 }
845             } catch (ReflectionException $e) {
846             }
847         }
848
849         return self::$hookMethods[$className];
850     }
851
852     /**
853      * @return array
854      *
855      * @since  Method available since Release 4.0.9
856      */
857     private static function emptyHookMethodsArray()
858     {
859         return array(
860             'beforeClass' => array('setUpBeforeClass'),
861             'before'      => array('setUp'),
862             'after'       => array('tearDown'),
863             'afterClass'  => array('tearDownAfterClass')
864         );
865     }
866
867     /**
868      * @param string $className
869      * @param string $methodName
870      * @param string $settingName
871      *
872      * @return bool
873      *
874      * @since  Method available since Release 3.4.0
875      */
876     private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
877     {
878         $annotations = self::parseTestMethodAnnotations(
879             $className,
880             $methodName
881         );
882
883         $result = null;
884
885         if (isset($annotations['class'][$settingName])) {
886             if ($annotations['class'][$settingName][0] == 'enabled') {
887                 $result = true;
888             } elseif ($annotations['class'][$settingName][0] == 'disabled') {
889                 $result = false;
890             }
891         }
892
893         if (isset($annotations['method'][$settingName])) {
894             if ($annotations['method'][$settingName][0] == 'enabled') {
895                 $result = true;
896             } elseif ($annotations['method'][$settingName][0] == 'disabled') {
897                 $result = false;
898             }
899         }
900
901         return $result;
902     }
903
904     /**
905      * @param string $element
906      *
907      * @return array
908      *
909      * @throws PHPUnit_Framework_InvalidCoversTargetException
910      *
911      * @since  Method available since Release 4.0.0
912      */
913     private static function resolveElementToReflectionObjects($element)
914     {
915         $codeToCoverList = array();
916
917         if (strpos($element, '\\') !== false && function_exists($element)) {
918             $codeToCoverList[] = new ReflectionFunction($element);
919         } elseif (strpos($element, '::') !== false) {
920             list($className, $methodName) = explode('::', $element);
921
922             if (isset($methodName[0]) && $methodName[0] == '<') {
923                 $classes = array($className);
924
925                 foreach ($classes as $className) {
926                     if (!class_exists($className) &&
927                         !interface_exists($className)) {
928                         throw new PHPUnit_Framework_InvalidCoversTargetException(
929                             sprintf(
930                                 'Trying to @cover or @use not existing class or ' .
931                                 'interface "%s".',
932                                 $className
933                             )
934                         );
935                     }
936
937                     $class   = new ReflectionClass($className);
938                     $methods = $class->getMethods();
939                     $inverse = isset($methodName[1]) && $methodName[1] == '!';
940
941                     if (strpos($methodName, 'protected')) {
942                         $visibility = 'isProtected';
943                     } elseif (strpos($methodName, 'private')) {
944                         $visibility = 'isPrivate';
945                     } elseif (strpos($methodName, 'public')) {
946                         $visibility = 'isPublic';
947                     }
948
949                     foreach ($methods as $method) {
950                         if ($inverse && !$method->$visibility()) {
951                             $codeToCoverList[] = $method;
952                         } elseif (!$inverse && $method->$visibility()) {
953                             $codeToCoverList[] = $method;
954                         }
955                     }
956                 }
957             } else {
958                 $classes = array($className);
959
960                 foreach ($classes as $className) {
961                     if ($className == '' && function_exists($methodName)) {
962                         $codeToCoverList[] = new ReflectionFunction(
963                             $methodName
964                         );
965                     } else {
966                         if (!((class_exists($className) ||
967                                interface_exists($className) ||
968                                trait_exists($className)) &&
969                               method_exists($className, $methodName))) {
970                             throw new PHPUnit_Framework_InvalidCoversTargetException(
971                                 sprintf(
972                                     'Trying to @cover or @use not existing method "%s::%s".',
973                                     $className,
974                                     $methodName
975                                 )
976                             );
977                         }
978
979                         $codeToCoverList[] = new ReflectionMethod(
980                             $className,
981                             $methodName
982                         );
983                     }
984                 }
985             }
986         } else {
987             $extended = false;
988
989             if (strpos($element, '<extended>') !== false) {
990                 $element  = str_replace('<extended>', '', $element);
991                 $extended = true;
992             }
993
994             $classes = array($element);
995
996             if ($extended) {
997                 $classes = array_merge(
998                     $classes,
999                     class_implements($element),
1000                     class_parents($element)
1001                 );
1002             }
1003
1004             foreach ($classes as $className) {
1005                 if (!class_exists($className) &&
1006                     !interface_exists($className) &&
1007                     !trait_exists($className)) {
1008                     throw new PHPUnit_Framework_InvalidCoversTargetException(
1009                         sprintf(
1010                             'Trying to @cover or @use not existing class or ' .
1011                             'interface "%s".',
1012                             $className
1013                         )
1014                     );
1015                 }
1016
1017                 $codeToCoverList[] = new ReflectionClass($className);
1018             }
1019         }
1020
1021         return $codeToCoverList;
1022     }
1023
1024     /**
1025      * @param array $reflectors
1026      *
1027      * @return array
1028      */
1029     private static function resolveReflectionObjectsToLines(array $reflectors)
1030     {
1031         $result = array();
1032
1033         foreach ($reflectors as $reflector) {
1034             $filename = $reflector->getFileName();
1035
1036             if (!isset($result[$filename])) {
1037                 $result[$filename] = array();
1038             }
1039
1040             $result[$filename] = array_unique(
1041                 array_merge(
1042                     $result[$filename],
1043                     range($reflector->getStartLine(), $reflector->getEndLine())
1044                 )
1045             );
1046         }
1047
1048         return $result;
1049     }
1050
1051     /**
1052      * @param ReflectionMethod $method
1053      *
1054      * @return bool
1055      *
1056      * @since  Method available since Release 4.0.8
1057      */
1058     private static function isBeforeClassMethod(ReflectionMethod $method)
1059     {
1060         return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false;
1061     }
1062
1063     /**
1064      * @param ReflectionMethod $method
1065      *
1066      * @return bool
1067      *
1068      * @since  Method available since Release 4.0.8
1069      */
1070     private static function isBeforeMethod(ReflectionMethod $method)
1071     {
1072         return preg_match('/@before\b/', $method->getDocComment());
1073     }
1074
1075     /**
1076      * @param ReflectionMethod $method
1077      *
1078      * @return bool
1079      *
1080      * @since  Method available since Release 4.0.8
1081      */
1082     private static function isAfterClassMethod(ReflectionMethod $method)
1083     {
1084         return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false;
1085     }
1086
1087     /**
1088      * @param ReflectionMethod $method
1089      *
1090      * @return bool
1091      *
1092      * @since  Method available since Release 4.0.8
1093      */
1094     private static function isAfterMethod(ReflectionMethod $method)
1095     {
1096         return preg_match('/@after\b/', $method->getDocComment());
1097     }
1098 }