PageRenderTime 44ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/PHPUnit/Util/Test.php

https://github.com/wimg/phpunit
PHP | 874 lines | 551 code | 119 blank | 204 comment | 106 complexity | dd939ded77c8ff0f0dc7c62aad202315 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * PHPUnit
  4. *
  5. * Copyright (c) 2001-2013, Sebastian Bergmann <sebastian@phpunit.de>.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * * Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  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
  18. * distribution.
  19. *
  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.
  23. *
  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.
  36. *
  37. * @package PHPUnit
  38. * @subpackage Util
  39. * @author Sebastian Bergmann <sebastian@phpunit.de>
  40. * @copyright 2001-2013 Sebastian Bergmann <sebastian@phpunit.de>
  41. * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
  42. * @link http://www.phpunit.de/
  43. * @since File available since Release 3.0.0
  44. */
  45. /**
  46. * Test helpers.
  47. *
  48. * @package PHPUnit
  49. * @subpackage Util
  50. * @author Sebastian Bergmann <sebastian@phpunit.de>
  51. * @copyright 2001-2013 Sebastian Bergmann <sebastian@phpunit.de>
  52. * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
  53. * @link http://www.phpunit.de/
  54. * @since Class available since Release 3.0.0
  55. */
  56. class PHPUnit_Util_Test
  57. {
  58. const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
  59. const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
  60. const REGEX_REQUIRES_VERSION = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<value>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
  61. const REGEX_REQUIRES_OS = '/@requires\s+OS\s+(?P<value>.+)\r?$/m';
  62. const REGEX_REQUIRES = '/@requires\s+(?P<name>function|extension)\s(?P<value>([^ ]+))\r?$/m';
  63. const SMALL = 0;
  64. const MEDIUM = 1;
  65. const LARGE = 2;
  66. private static $annotationCache = array();
  67. private static $templateMethods = array(
  68. 'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown'
  69. );
  70. /**
  71. * @param PHPUnit_Framework_Test $test
  72. * @param boolean $asString
  73. * @return mixed
  74. */
  75. public static function describe(PHPUnit_Framework_Test $test, $asString = TRUE)
  76. {
  77. if ($asString) {
  78. if ($test instanceof PHPUnit_Framework_SelfDescribing) {
  79. return $test->toString();
  80. } else {
  81. return get_class($test);
  82. }
  83. } else {
  84. if ($test instanceof PHPUnit_Framework_TestCase) {
  85. return array(
  86. get_class($test), $test->getName()
  87. );
  88. }
  89. else if ($test instanceof PHPUnit_Framework_SelfDescribing) {
  90. return array('', $test->toString());
  91. }
  92. else {
  93. return array('', get_class($test));
  94. }
  95. }
  96. }
  97. /**
  98. * Returns the files and lines a test method wants to cover.
  99. *
  100. * @param string $className
  101. * @param string $methodName
  102. * @return array
  103. * @since Method available since Release 3.8.0
  104. */
  105. public static function getLinesToBeCovered($className, $methodName)
  106. {
  107. $codeToCoverList = array();
  108. // @codeCoverageIgnoreStart
  109. if (($pos = strpos($methodName, ' ')) !== FALSE) {
  110. $methodName = substr($methodName, 0, $pos);
  111. }
  112. // @codeCoverageIgnoreEnd
  113. $class = new ReflectionClass($className);
  114. try {
  115. $method = new ReflectionMethod($className, $methodName);
  116. }
  117. catch (ReflectionException $e) {
  118. return array();
  119. }
  120. $docComment = substr($class->getDocComment(), 3, -2) . PHP_EOL .
  121. substr($method->getDocComment(), 3, -2);
  122. $templateMethods = array(
  123. 'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown'
  124. );
  125. foreach ($templateMethods as $templateMethod) {
  126. if ($class->hasMethod($templateMethod)) {
  127. $reflector = $class->getMethod($templateMethod);
  128. $docComment .= PHP_EOL .
  129. substr($reflector->getDocComment(), 3, -2);
  130. unset($reflector);
  131. }
  132. }
  133. if (strpos($docComment, '@coversNothing') !== FALSE) {
  134. return FALSE;
  135. }
  136. $classShortcut = preg_match_all(
  137. '(@coversDefaultClass\s+(?P<coveredClass>[^\s]++)\s*$)m',
  138. $class->getDocComment(),
  139. $matches
  140. );
  141. if ($classShortcut) {
  142. if ($classShortcut > 1) {
  143. throw new PHPUnit_Framework_CodeCoverageException(
  144. sprintf(
  145. 'More than one @coversClass annotation in class or interface "%s".',
  146. $className
  147. )
  148. );
  149. }
  150. $classShortcut = $matches['coveredClass'][0];
  151. }
  152. $match = preg_match_all(
  153. '(@covers\s+(?P<coveredElement>[^\s()]++)[\s()]*$)m',
  154. $docComment,
  155. $matches
  156. );
  157. if ($match) {
  158. foreach ($matches['coveredElement'] as $coveredElement) {
  159. if ($classShortcut && strncmp($coveredElement, '::', 2) === 0) {
  160. $coveredElement = $classShortcut . $coveredElement;
  161. }
  162. $codeToCoverList = array_merge(
  163. $codeToCoverList,
  164. self::resolveElementToReflectionObjects($coveredElement)
  165. );
  166. }
  167. }
  168. return self::resolveReflectionObjectsToLines($codeToCoverList);
  169. }
  170. /**
  171. * Returns lines of code specified with the @uses annotation.
  172. *
  173. * @param string $className
  174. * @param string $methodName
  175. * @return array
  176. * @since Method available since Release 3.8.0
  177. */
  178. public static function getLinesToBeUsed($className, $methodName)
  179. {
  180. $annotations = self::parseTestMethodAnnotations(
  181. $className, $methodName
  182. );
  183. $uses = array();
  184. if (isset($annotations['class']['uses'])) {
  185. $uses = $annotations['class']['uses'];
  186. }
  187. if (isset($annotations['method']['uses'])) {
  188. $uses = array_merge($uses, $annotations['method']['uses']);
  189. }
  190. $uses = array_unique($uses);
  191. $codeToUseList = array();
  192. foreach (array_unique($uses) as $element) {
  193. $codeToUseList = array_merge(
  194. $codeToUseList,
  195. self::resolveElementToReflectionObjects($element)
  196. );
  197. }
  198. return self::resolveReflectionObjectsToLines($codeToUseList);
  199. }
  200. /**
  201. * Returns the requirements for a test.
  202. *
  203. * @param string $className
  204. * @param string $methodName
  205. * @return array
  206. * @since Method available since Release 3.6.0
  207. */
  208. public static function getRequirements($className, $methodName)
  209. {
  210. $reflector = new ReflectionClass($className);
  211. $docComment = $reflector->getDocComment();
  212. $reflector = new ReflectionMethod($className, $methodName);
  213. $docComment .= "\n" . $reflector->getDocComment();
  214. $requires = array();
  215. if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) {
  216. $requires['OS'] = sprintf(
  217. '/%s/i',
  218. addcslashes($matches['value'][$count - 1], '/')
  219. );
  220. }
  221. if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) {
  222. for ($i = 0; $i < $count; $i++) {
  223. $requires[$matches['name'][$i]] = $matches['value'][$i];
  224. }
  225. }
  226. // https://bugs.php.net/bug.php?id=63055
  227. $matches = array();
  228. if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
  229. for ($i = 0; $i < $count; $i++) {
  230. $name = $matches['name'][$i] . 's';
  231. if (!isset($requires[$name])) {
  232. $requires[$name] = array();
  233. }
  234. $requires[$name][] = $matches['value'][$i];
  235. }
  236. }
  237. return $requires;
  238. }
  239. /**
  240. * Returns the expected exception for a test.
  241. *
  242. * @param string $className
  243. * @param string $methodName
  244. * @return array
  245. * @since Method available since Release 3.3.6
  246. */
  247. public static function getExpectedException($className, $methodName)
  248. {
  249. $reflector = new ReflectionMethod($className, $methodName);
  250. $docComment = $reflector->getDocComment();
  251. $docComment = substr($docComment, 3, -2);
  252. if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
  253. $annotations = self::parseTestMethodAnnotations(
  254. $className, $methodName
  255. );
  256. $class = $matches[1];
  257. $code = NULL;
  258. $message = '';
  259. if (isset($matches[2])) {
  260. $message = trim($matches[2]);
  261. }
  262. else if (isset($annotations['method']['expectedExceptionMessage'])) {
  263. $message = self::parseAnnotationContent(
  264. $annotations['method']['expectedExceptionMessage'][0]
  265. );
  266. }
  267. if (isset($matches[3])) {
  268. $code = $matches[3];
  269. }
  270. else if (isset($annotations['method']['expectedExceptionCode'])) {
  271. $code = self::parseAnnotationContent(
  272. $annotations['method']['expectedExceptionCode'][0]
  273. );
  274. }
  275. if (is_numeric($code)) {
  276. $code = (int)$code;
  277. }
  278. else if (is_string($code) && defined($code)) {
  279. $code = (int)constant($code);
  280. }
  281. return array(
  282. 'class' => $class, 'code' => $code, 'message' => $message
  283. );
  284. }
  285. return FALSE;
  286. }
  287. /**
  288. * Parse annotation content to use constant/class constant values
  289. *
  290. * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
  291. *
  292. * If the constant is not found the string is used as is to ensure maximum BC.
  293. *
  294. * @param string $message
  295. * @return string
  296. */
  297. private static function parseAnnotationContent($message)
  298. {
  299. if (strpos($message, '::') !== FALSE && count(explode('::', $message) == 2)) {
  300. if (defined($message)) {
  301. $message = constant($message);
  302. }
  303. }
  304. return $message;
  305. }
  306. /**
  307. * Returns the provided data for a method.
  308. *
  309. * @param string $className
  310. * @param string $methodName
  311. * @param string $docComment
  312. * @return mixed array|Iterator when a data provider is specified and exists
  313. * false when a data provider is specified and does not exist
  314. * null when no data provider is specified
  315. * @since Method available since Release 3.2.0
  316. */
  317. public static function getProvidedData($className, $methodName)
  318. {
  319. $reflector = new ReflectionMethod($className, $methodName);
  320. $docComment = $reflector->getDocComment();
  321. $data = NULL;
  322. if (preg_match(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
  323. $dataProviderMethodNameNamespace = explode('\\', $matches[1]);
  324. $leaf = explode('::', array_pop($dataProviderMethodNameNamespace));
  325. $dataProviderMethodName = array_pop($leaf);
  326. if (!empty($dataProviderMethodNameNamespace)) {
  327. $dataProviderMethodNameNamespace = join('\\', $dataProviderMethodNameNamespace) . '\\';
  328. } else {
  329. $dataProviderMethodNameNamespace = '';
  330. }
  331. if (!empty($leaf)) {
  332. $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
  333. } else {
  334. $dataProviderClassName = $className;
  335. }
  336. $dataProviderClass = new ReflectionClass($dataProviderClassName);
  337. $dataProviderMethod = $dataProviderClass->getMethod(
  338. $dataProviderMethodName
  339. );
  340. if ($dataProviderMethod->isStatic()) {
  341. $object = NULL;
  342. } else {
  343. $object = $dataProviderClass->newInstance();
  344. }
  345. if ($dataProviderMethod->getNumberOfParameters() == 0) {
  346. $data = $dataProviderMethod->invoke($object);
  347. } else {
  348. $data = $dataProviderMethod->invoke($object, $methodName);
  349. }
  350. }
  351. if ($data !== NULL) {
  352. if (is_object($data)) {
  353. $data = iterator_to_array($data);
  354. }
  355. foreach ($data as $key => $value) {
  356. if (!is_array($value)) {
  357. throw new PHPUnit_Framework_Exception(
  358. sprintf(
  359. 'Data set %s is invalid.',
  360. is_int($key) ? '#' . $key : '"' . $key . '"'
  361. )
  362. );
  363. }
  364. }
  365. }
  366. return $data;
  367. }
  368. /**
  369. * @param string $className
  370. * @param string $methodName
  371. * @return array
  372. * @throws ReflectionException
  373. * @since Method available since Release 3.4.0
  374. */
  375. public static function parseTestMethodAnnotations($className, $methodName = '')
  376. {
  377. if (!isset(self::$annotationCache[$className])) {
  378. $class = new ReflectionClass($className);
  379. self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
  380. }
  381. if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
  382. try {
  383. $method = new ReflectionMethod($className, $methodName);
  384. $annotations = self::parseAnnotations($method->getDocComment());
  385. } catch (ReflectionException $e) {
  386. $annotations = array();
  387. }
  388. self::$annotationCache[$className . '::' . $methodName] = $annotations;
  389. }
  390. return array(
  391. 'class' => self::$annotationCache[$className],
  392. 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : array()
  393. );
  394. }
  395. /**
  396. * @param string $docblock
  397. * @return array
  398. * @since Method available since Release 3.4.0
  399. */
  400. private static function parseAnnotations($docblock)
  401. {
  402. $annotations = array();
  403. // Strip away the docblock header and footer to ease parsing of one line annotations
  404. $docblock = substr($docblock, 3, -2);
  405. if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
  406. $numMatches = count($matches[0]);
  407. for ($i = 0; $i < $numMatches; ++$i) {
  408. $annotations[$matches['name'][$i]][] = $matches['value'][$i];
  409. }
  410. }
  411. return $annotations;
  412. }
  413. /**
  414. * Returns the backup settings for a test.
  415. *
  416. * @param string $className
  417. * @param string $methodName
  418. * @return array
  419. * @since Method available since Release 3.4.0
  420. */
  421. public static function getBackupSettings($className, $methodName)
  422. {
  423. return array(
  424. 'backupGlobals' => self::getBooleanAnnotationSetting(
  425. $className, $methodName, 'backupGlobals'
  426. ),
  427. 'backupStaticAttributes' => self::getBooleanAnnotationSetting(
  428. $className, $methodName, 'backupStaticAttributes'
  429. )
  430. );
  431. }
  432. /**
  433. * Returns the dependencies for a test class or method.
  434. *
  435. * @param string $className
  436. * @param string $methodName
  437. * @return array
  438. * @since Method available since Release 3.4.0
  439. */
  440. public static function getDependencies($className, $methodName)
  441. {
  442. $annotations = self::parseTestMethodAnnotations(
  443. $className, $methodName
  444. );
  445. $dependencies = array();
  446. if (isset($annotations['class']['depends'])) {
  447. $dependencies = $annotations['class']['depends'];
  448. }
  449. if (isset($annotations['method']['depends'])) {
  450. $dependencies = array_merge(
  451. $dependencies, $annotations['method']['depends']
  452. );
  453. }
  454. return array_unique($dependencies);
  455. }
  456. /**
  457. * Returns the error handler settings for a test.
  458. *
  459. * @param string $className
  460. * @param string $methodName
  461. * @return boolean
  462. * @since Method available since Release 3.4.0
  463. */
  464. public static function getErrorHandlerSettings($className, $methodName)
  465. {
  466. return self::getBooleanAnnotationSetting(
  467. $className, $methodName, 'errorHandler'
  468. );
  469. }
  470. /**
  471. * Returns the groups for a test class or method.
  472. *
  473. * @param string $className
  474. * @param string $methodName
  475. * @return array
  476. * @since Method available since Release 3.2.0
  477. */
  478. public static function getGroups($className, $methodName = '')
  479. {
  480. $annotations = self::parseTestMethodAnnotations(
  481. $className, $methodName
  482. );
  483. $groups = array();
  484. if (isset($annotations['method']['author'])) {
  485. $groups = $annotations['method']['author'];
  486. }
  487. else if (isset($annotations['class']['author'])) {
  488. $groups = $annotations['class']['author'];
  489. }
  490. if (isset($annotations['class']['group'])) {
  491. $groups = array_merge($groups, $annotations['class']['group']);
  492. }
  493. if (isset($annotations['method']['group'])) {
  494. $groups = array_merge($groups, $annotations['method']['group']);
  495. }
  496. if (isset($annotations['class']['ticket'])) {
  497. $groups = array_merge($groups, $annotations['class']['ticket']);
  498. }
  499. if (isset($annotations['method']['ticket'])) {
  500. $groups = array_merge($groups, $annotations['method']['ticket']);
  501. }
  502. foreach (array('small', 'medium', 'large') as $size) {
  503. if (isset($annotations['method'][$size])) {
  504. $groups[] = $size;
  505. }
  506. else if (isset($annotations['class'][$size])) {
  507. $groups[] = $size;
  508. }
  509. }
  510. return array_unique($groups);
  511. }
  512. /**
  513. * Returns the size of the test.
  514. *
  515. * @param string $className
  516. * @param string $methodName
  517. * @return integer
  518. * @since Method available since Release 3.6.0
  519. */
  520. public static function getSize($className, $methodName)
  521. {
  522. $groups = array_flip(self::getGroups($className, $methodName));
  523. $size = self::SMALL;
  524. $class = new ReflectionClass($className);
  525. if ((class_exists('PHPUnit_Extensions_Database_TestCase', FALSE) &&
  526. $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase')) ||
  527. (class_exists('PHPUnit_Extensions_SeleniumTestCase', FALSE) &&
  528. $class->isSubclassOf('PHPUnit_Extensions_SeleniumTestCase'))) {
  529. $size = self::LARGE;
  530. }
  531. else if (isset($groups['medium'])) {
  532. $size = self::MEDIUM;
  533. }
  534. else if (isset($groups['large'])) {
  535. $size = self::LARGE;
  536. }
  537. return $size;
  538. }
  539. /**
  540. * Returns the tickets for a test class or method.
  541. *
  542. * @param string $className
  543. * @param string $methodName
  544. * @return array
  545. * @since Method available since Release 3.4.0
  546. */
  547. public static function getTickets($className, $methodName)
  548. {
  549. $annotations = self::parseTestMethodAnnotations(
  550. $className, $methodName
  551. );
  552. $tickets = array();
  553. if (isset($annotations['class']['ticket'])) {
  554. $tickets = $annotations['class']['ticket'];
  555. }
  556. if (isset($annotations['method']['ticket'])) {
  557. $tickets = array_merge($tickets, $annotations['method']['ticket']);
  558. }
  559. return array_unique($tickets);
  560. }
  561. /**
  562. * Returns the process isolation settings for a test.
  563. *
  564. * @param string $className
  565. * @param string $methodName
  566. * @return boolean
  567. * @since Method available since Release 3.4.1
  568. */
  569. public static function getProcessIsolationSettings($className, $methodName)
  570. {
  571. $annotations = self::parseTestMethodAnnotations(
  572. $className, $methodName
  573. );
  574. if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
  575. isset($annotations['method']['runInSeparateProcess'])) {
  576. return TRUE;
  577. } else {
  578. return FALSE;
  579. }
  580. }
  581. /**
  582. * Returns the preserve global state settings for a test.
  583. *
  584. * @param string $className
  585. * @param string $methodName
  586. * @return boolean
  587. * @since Method available since Release 3.4.0
  588. */
  589. public static function getPreserveGlobalStateSettings($className, $methodName)
  590. {
  591. return self::getBooleanAnnotationSetting(
  592. $className, $methodName, 'preserveGlobalState'
  593. );
  594. }
  595. /**
  596. * @param string $className
  597. * @param string $methodName
  598. * @param string $settingName
  599. * @return boolean
  600. * @since Method available since Release 3.4.0
  601. */
  602. private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
  603. {
  604. $annotations = self::parseTestMethodAnnotations(
  605. $className, $methodName
  606. );
  607. $result = NULL;
  608. if (isset($annotations['class'][$settingName])) {
  609. if ($annotations['class'][$settingName][0] == 'enabled') {
  610. $result = TRUE;
  611. }
  612. else if ($annotations['class'][$settingName][0] == 'disabled') {
  613. $result = FALSE;
  614. }
  615. }
  616. if (isset($annotations['method'][$settingName])) {
  617. if ($annotations['method'][$settingName][0] == 'enabled') {
  618. $result = TRUE;
  619. }
  620. else if ($annotations['method'][$settingName][0] == 'disabled') {
  621. $result = FALSE;
  622. }
  623. }
  624. return $result;
  625. }
  626. /**
  627. * @param string $element
  628. * @return array
  629. * @since Method available since Release 3.8.0
  630. */
  631. private static function resolveElementToReflectionObjects($element)
  632. {
  633. $codeToCoverList = array();
  634. if (strpos($element, '::') !== FALSE) {
  635. list($className, $methodName) = explode('::', $element);
  636. if (isset($methodName[0]) && $methodName[0] == '<') {
  637. $classes = array($className);
  638. foreach ($classes as $className) {
  639. if (!class_exists($className) &&
  640. !interface_exists($className)) {
  641. throw new PHPUnit_Framework_InvalidCoversTargetException(
  642. sprintf(
  643. 'Trying to @cover or @use not existing class or ' .
  644. 'interface "%s".',
  645. $className
  646. )
  647. );
  648. }
  649. $class = new ReflectionClass($className);
  650. $methods = $class->getMethods();
  651. $inverse = isset($methodName[1]) && $methodName[1] == '!';
  652. if (strpos($methodName, 'protected')) {
  653. $visibility = 'isProtected';
  654. }
  655. else if (strpos($methodName, 'private')) {
  656. $visibility = 'isPrivate';
  657. }
  658. else if (strpos($methodName, 'public')) {
  659. $visibility = 'isPublic';
  660. }
  661. foreach ($methods as $method) {
  662. if ($inverse && !$method->$visibility()) {
  663. $codeToCoverList[] = $method;
  664. }
  665. else if (!$inverse && $method->$visibility()) {
  666. $codeToCoverList[] = $method;
  667. }
  668. }
  669. }
  670. } else {
  671. $classes = array($className);
  672. foreach ($classes as $className) {
  673. if ($className == '' && function_exists($methodName)) {
  674. $codeToCoverList[] = new ReflectionFunction(
  675. $methodName
  676. );
  677. } else {
  678. if (!((class_exists($className) ||
  679. interface_exists($className) ||
  680. trait_exists($className)) &&
  681. method_exists($className, $methodName))) {
  682. throw new PHPUnit_Framework_InvalidCoversTargetException(
  683. sprintf(
  684. 'Trying to @cover or @use not existing method "%s::%s".',
  685. $className,
  686. $methodName
  687. )
  688. );
  689. }
  690. $codeToCoverList[] = new ReflectionMethod(
  691. $className, $methodName
  692. );
  693. }
  694. }
  695. }
  696. } else {
  697. $extended = FALSE;
  698. if (strpos($element, '<extended>') !== FALSE) {
  699. $element = str_replace(
  700. '<extended>', '', $element
  701. );
  702. $extended = TRUE;
  703. }
  704. $classes = array($element);
  705. if ($extended) {
  706. $classes = array_merge(
  707. $classes,
  708. class_implements($element),
  709. class_parents($element)
  710. );
  711. }
  712. foreach ($classes as $className) {
  713. if (!class_exists($className) &&
  714. !interface_exists($className) &&
  715. !trait_exists($className)) {
  716. throw new PHPUnit_Framework_InvalidCoversTargetException(
  717. sprintf(
  718. 'Trying to @cover or @use not existing class or ' .
  719. 'interface "%s".',
  720. $className
  721. )
  722. );
  723. }
  724. $codeToCoverList[] = new ReflectionClass($className);
  725. }
  726. }
  727. return $codeToCoverList;
  728. }
  729. private static function resolveReflectionObjectsToLines(array $reflectors)
  730. {
  731. $result = array();
  732. foreach ($reflectors as $reflector) {
  733. $filename = $reflector->getFileName();
  734. if (!isset($result[$filename])) {
  735. $result[$filename] = array();
  736. }
  737. $result[$filename] = array_unique(
  738. array_merge(
  739. $result[$filename],
  740. range(
  741. $reflector->getStartLine(), $reflector->getEndLine()
  742. )
  743. )
  744. );
  745. }
  746. return $result;
  747. }
  748. }