5 * Copyright (c) 2002-2009, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @link http://www.phpunit.de/
44 * @since File available since Release 3.0.0
47 require_once 'PHPUnit/Util/Filter.php';
49 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
56 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
57 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
58 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
59 * @version Release: 3.3.17
60 * @link http://www.phpunit.de/
61 * @since Class available since Release 3.0.0
63 class PHPUnit_Util_Test
65 const REGEX_COVERS = '/@covers[\s]+([\!<>\:\.\w]+)([\s]+<extended>)?/';
66 const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\]+)/';
67 const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
68 const REGEX_GROUP = '/@group\s+([a-zA-Z0-9._-]+)/';
71 * @param PHPUnit_Framework_Test $test
72 * @param boolean $asString
75 public static function describe(PHPUnit_Framework_Test $test, $asString = TRUE)
78 if ($test instanceof PHPUnit_Framework_SelfDescribing) {
79 return $test->toString();
81 return get_class($test);
84 if ($test instanceof PHPUnit_Framework_TestCase) {
86 get_class($test), $test->getName()
90 else if ($test instanceof PHPUnit_Framework_SelfDescribing) {
91 return array('', $test->toString());
95 return array('', get_class($test));
101 * @param PHPUnit_Framework_Test $test
102 * @param PHPUnit_Framework_TestResult $result
105 public static function lookupResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result)
107 $testName = self::describe($test);
109 foreach ($result->errors() as $error) {
110 if ($testName == self::describe($error->failedTest())) {
115 foreach ($result->failures() as $failure) {
116 if ($testName == self::describe($failure->failedTest())) {
121 foreach ($result->notImplemented() as $notImplemented) {
122 if ($testName == self::describe($notImplemented->failedTest())) {
123 return $notImplemented;
127 foreach ($result->skipped() as $skipped) {
128 if ($testName == self::describe($skipped->failedTest())) {
133 return PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
137 * Returns the files and lines a test method wants to cover.
139 * @param string $className
140 * @param string $methodName
142 * @since Method available since Release 3.2.0
144 public static function getLinesToBeCovered($className, $methodName)
147 $codeToCoverList = array();
149 if (($pos = strpos($methodName, ' ')) !== FALSE) {
150 $methodName = substr($methodName, 0, $pos);
154 $class = new ReflectionClass($className);
155 $method = new ReflectionMethod($className, $methodName);
156 $docComment = $class->getDocComment() . $method->getDocComment();
158 if (preg_match_all(self::REGEX_COVERS, $docComment, $matches)) {
159 foreach ($matches[1] as $i => $method) {
160 $codeToCoverList = array_merge(
162 self::resolveCoversToReflectionObjects($method, !empty($matches[2][$i]))
166 foreach ($codeToCoverList as $codeToCover) {
167 $fileName = $codeToCover->getFileName();
168 $startLine = $codeToCover->getStartLine();
169 $endLine = $codeToCover->getEndLine();
171 if (!isset($result[$fileName])) {
172 $result[$fileName] = array();
175 $result[$fileName] = array_unique(
176 array_merge($result[$fileName], range($startLine, $endLine))
182 catch (ReflectionException $e) {
189 * Returns the expected exception for a test.
191 * @param string $docComment
193 * @since Method available since Release 3.3.6
195 public static function getExpectedException($docComment)
197 if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
198 $class = $matches[1];
202 if (isset($matches[2])) {
203 $message = trim($matches[2]);
206 if (isset($matches[3])) {
207 $code = (int)$matches[3];
211 'class' => $class, 'code' => $code, 'message' => $message
219 * Returns the groups for a test class or method.
221 * @param string $docComment
222 * @param array $groups
224 * @since Method available since Release 3.2.0
226 public static function getGroups($docComment, array $groups = array())
228 if (preg_match_all(self::REGEX_GROUP, $docComment, $matches)) {
229 $groups = array_unique(array_merge($groups, $matches[1]));
236 * Returns the provided data for a method.
238 * @param string $className
239 * @param string $methodName
240 * @param string $docComment
242 * @since Method available since Release 3.2.0
244 public static function getProvidedData($className, $methodName, $docComment)
246 if (preg_match(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
248 $dataProviderMethodNameNamespace = explode('\\', $matches[1]);
249 $leaf = explode('::', array_pop($dataProviderMethodNameNamespace));
250 $dataProviderMethodName = array_pop($leaf);
252 if (!empty($dataProviderMethodNameNamespace)) {
253 $dataProviderMethodNameNamespace = join('\\', $dataProviderMethodNameNamespace) . '\\';
255 $dataProviderMethodNameNamespace = '';
259 $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
261 $dataProviderClassName = $className;
264 $dataProviderClass = new ReflectionClass($dataProviderClassName);
265 $dataProviderMethod = $dataProviderClass->getMethod(
266 $dataProviderMethodName
269 if ($dataProviderMethod->isStatic()) {
272 $object = $dataProviderClass->newInstance();
275 if ($dataProviderMethod->getNumberOfParameters() == 0) {
276 return $dataProviderMethod->invoke($object);
278 return $dataProviderMethod->invoke($object, $methodName);
282 catch (ReflectionException $e) {
288 * Returns the files and lines a test method wants to cover.
290 * @param string $method
291 * @param boolean $extended
293 * @since Method available since Release 3.3.0
295 private static function resolveCoversToReflectionObjects($method, $extended)
297 $codeToCoverList = array();
299 if (strpos($method, '::') !== FALSE) {
300 list($className, $methodName) = explode('::', $method);
302 if ($methodName{0} == '<') {
303 $classes = array($className);
306 $classes = array_merge(
308 class_implements($className),
309 class_parents($className)
313 foreach ($classes as $className)
315 $class = new ReflectionClass($className);
316 $methods = $class->getMethods();
317 $inverse = isset($methodName{1}) && $methodName{1} == '!';
319 if (strpos($methodName, 'protected')) {
320 $visibility = 'isProtected';
323 else if (strpos($methodName, 'private')) {
324 $visibility = 'isPrivate';
327 else if (strpos($methodName, 'public')) {
328 $visibility = 'isPublic';
331 foreach ($methods as $method) {
332 if ($inverse && !$method->$visibility()) {
333 $codeToCoverList[] = $method;
336 else if (!$inverse && $method->$visibility()) {
337 $codeToCoverList[] = $method;
342 $classes = array($className);
345 $classes = array_merge($classes, class_parents($className));
348 foreach ($classes as $className) {
349 $codeToCoverList[] = new ReflectionMethod($className, $methodName);
353 $classes = array($method);
356 $classes = array_merge(
358 class_implements($method),
359 class_parents($method)
363 foreach ($classes as $className) {
364 $codeToCoverList[] = new ReflectionClass($className);
368 return $codeToCoverList;