PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

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

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