PageRenderTime 28ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpunit/phpunit/src/Util/Test.php

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