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

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

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