PageRenderTime 74ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/faisaliqbal/mytripsorter
PHP | 1103 lines | 877 code | 76 blank | 150 comment | 80 complexity | b39f651783dbb9b56f2f32c6a7ef35d1 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. if (isset($annotations[$element][$size])) {
  613. $groups[] = $size;
  614. break 2;
  615. }
  616. }
  617. }
  618. return array_unique($groups);
  619. }
  620. /**
  621. * Returns the size of the test.
  622. *
  623. * @param string $className
  624. * @param string $methodName
  625. *
  626. * @return int
  627. *
  628. * @since Method available since Release 3.6.0
  629. */
  630. public static function getSize($className, $methodName)
  631. {
  632. $groups = array_flip(self::getGroups($className, $methodName));
  633. $size = self::UNKNOWN;
  634. $class = new ReflectionClass($className);
  635. if (isset($groups['large']) ||
  636. (class_exists('PHPUnit_Extensions_Database_TestCase', false) &&
  637. $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase'))) {
  638. $size = self::LARGE;
  639. } elseif (isset($groups['medium'])) {
  640. $size = self::MEDIUM;
  641. } elseif (isset($groups['small'])) {
  642. $size = self::SMALL;
  643. }
  644. return $size;
  645. }
  646. /**
  647. * Returns the tickets for a test class or method.
  648. *
  649. * @param string $className
  650. * @param string $methodName
  651. *
  652. * @return array
  653. *
  654. * @since Method available since Release 3.4.0
  655. */
  656. public static function getTickets($className, $methodName)
  657. {
  658. $annotations = self::parseTestMethodAnnotations(
  659. $className,
  660. $methodName
  661. );
  662. $tickets = [];
  663. if (isset($annotations['class']['ticket'])) {
  664. $tickets = $annotations['class']['ticket'];
  665. }
  666. if (isset($annotations['method']['ticket'])) {
  667. $tickets = array_merge($tickets, $annotations['method']['ticket']);
  668. }
  669. return array_unique($tickets);
  670. }
  671. /**
  672. * Returns the process isolation settings for a test.
  673. *
  674. * @param string $className
  675. * @param string $methodName
  676. *
  677. * @return bool
  678. *
  679. * @since Method available since Release 3.4.1
  680. */
  681. public static function getProcessIsolationSettings($className, $methodName)
  682. {
  683. $annotations = self::parseTestMethodAnnotations(
  684. $className,
  685. $methodName
  686. );
  687. if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
  688. isset($annotations['method']['runInSeparateProcess'])) {
  689. return true;
  690. } else {
  691. return false;
  692. }
  693. }
  694. /**
  695. * Returns the preserve global state settings for a test.
  696. *
  697. * @param string $className
  698. * @param string $methodName
  699. *
  700. * @return bool
  701. *
  702. * @since Method available since Release 3.4.0
  703. */
  704. public static function getPreserveGlobalStateSettings($className, $methodName)
  705. {
  706. return self::getBooleanAnnotationSetting(
  707. $className,
  708. $methodName,
  709. 'preserveGlobalState'
  710. );
  711. }
  712. /**
  713. * @param string $className
  714. *
  715. * @return array
  716. *
  717. * @since Method available since Release 4.0.8
  718. */
  719. public static function getHookMethods($className)
  720. {
  721. if (!class_exists($className, false)) {
  722. return self::emptyHookMethodsArray();
  723. }
  724. if (!isset(self::$hookMethods[$className])) {
  725. self::$hookMethods[$className] = self::emptyHookMethodsArray();
  726. try {
  727. $class = new ReflectionClass($className);
  728. foreach ($class->getMethods() as $method) {
  729. if (self::isBeforeClassMethod($method)) {
  730. self::$hookMethods[$className]['beforeClass'][] = $method->getName();
  731. }
  732. if (self::isBeforeMethod($method)) {
  733. self::$hookMethods[$className]['before'][] = $method->getName();
  734. }
  735. if (self::isAfterMethod($method)) {
  736. self::$hookMethods[$className]['after'][] = $method->getName();
  737. }
  738. if (self::isAfterClassMethod($method)) {
  739. self::$hookMethods[$className]['afterClass'][] = $method->getName();
  740. }
  741. }
  742. } catch (ReflectionException $e) {
  743. }
  744. }
  745. return self::$hookMethods[$className];
  746. }
  747. /**
  748. * @return array
  749. *
  750. * @since Method available since Release 4.0.9
  751. */
  752. private static function emptyHookMethodsArray()
  753. {
  754. return [
  755. 'beforeClass' => ['setUpBeforeClass'],
  756. 'before' => ['setUp'],
  757. 'after' => ['tearDown'],
  758. 'afterClass' => ['tearDownAfterClass']
  759. ];
  760. }
  761. /**
  762. * @param string $className
  763. * @param string $methodName
  764. * @param string $settingName
  765. *
  766. * @return bool
  767. *
  768. * @since Method available since Release 3.4.0
  769. */
  770. private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
  771. {
  772. $annotations = self::parseTestMethodAnnotations(
  773. $className,
  774. $methodName
  775. );
  776. $result = null;
  777. if (isset($annotations['class'][$settingName])) {
  778. if ($annotations['class'][$settingName][0] == 'enabled') {
  779. $result = true;
  780. } elseif ($annotations['class'][$settingName][0] == 'disabled') {
  781. $result = false;
  782. }
  783. }
  784. if (isset($annotations['method'][$settingName])) {
  785. if ($annotations['method'][$settingName][0] == 'enabled') {
  786. $result = true;
  787. } elseif ($annotations['method'][$settingName][0] == 'disabled') {
  788. $result = false;
  789. }
  790. }
  791. return $result;
  792. }
  793. /**
  794. * @param string $element
  795. *
  796. * @return array
  797. *
  798. * @throws PHPUnit_Framework_InvalidCoversTargetException
  799. *
  800. * @since Method available since Release 4.0.0
  801. */
  802. private static function resolveElementToReflectionObjects($element)
  803. {
  804. $codeToCoverList = [];
  805. if (strpos($element, '\\') !== false && function_exists($element)) {
  806. $codeToCoverList[] = new ReflectionFunction($element);
  807. } elseif (strpos($element, '::') !== false) {
  808. list($className, $methodName) = explode('::', $element);
  809. if (isset($methodName[0]) && $methodName[0] == '<') {
  810. $classes = [$className];
  811. foreach ($classes as $className) {
  812. if (!class_exists($className) &&
  813. !interface_exists($className) &&
  814. !trait_exists($className)) {
  815. throw new PHPUnit_Framework_InvalidCoversTargetException(
  816. sprintf(
  817. 'Trying to @cover or @use not existing class or ' .
  818. 'interface "%s".',
  819. $className
  820. )
  821. );
  822. }
  823. $class = new ReflectionClass($className);
  824. $methods = $class->getMethods();
  825. $inverse = isset($methodName[1]) && $methodName[1] == '!';
  826. if (strpos($methodName, 'protected')) {
  827. $visibility = 'isProtected';
  828. } elseif (strpos($methodName, 'private')) {
  829. $visibility = 'isPrivate';
  830. } elseif (strpos($methodName, 'public')) {
  831. $visibility = 'isPublic';
  832. }
  833. foreach ($methods as $method) {
  834. if ($inverse && !$method->$visibility()) {
  835. $codeToCoverList[] = $method;
  836. } elseif (!$inverse && $method->$visibility()) {
  837. $codeToCoverList[] = $method;
  838. }
  839. }
  840. }
  841. } else {
  842. $classes = [$className];
  843. foreach ($classes as $className) {
  844. if ($className == '' && function_exists($methodName)) {
  845. $codeToCoverList[] = new ReflectionFunction(
  846. $methodName
  847. );
  848. } else {
  849. if (!((class_exists($className) ||
  850. interface_exists($className) ||
  851. trait_exists($className)) &&
  852. method_exists($className, $methodName))) {
  853. throw new PHPUnit_Framework_InvalidCoversTargetException(
  854. sprintf(
  855. 'Trying to @cover or @use not existing method "%s::%s".',
  856. $className,
  857. $methodName
  858. )
  859. );
  860. }
  861. $codeToCoverList[] = new ReflectionMethod(
  862. $className,
  863. $methodName
  864. );
  865. }
  866. }
  867. }
  868. } else {
  869. $extended = false;
  870. if (strpos($element, '<extended>') !== false) {
  871. $element = str_replace('<extended>', '', $element);
  872. $extended = true;
  873. }
  874. $classes = [$element];
  875. if ($extended) {
  876. $classes = array_merge(
  877. $classes,
  878. class_implements($element),
  879. class_parents($element)
  880. );
  881. }
  882. foreach ($classes as $className) {
  883. if (!class_exists($className) &&
  884. !interface_exists($className) &&
  885. !trait_exists($className)) {
  886. throw new PHPUnit_Framework_InvalidCoversTargetException(
  887. sprintf(
  888. 'Trying to @cover or @use not existing class or ' .
  889. 'interface "%s".',
  890. $className
  891. )
  892. );
  893. }
  894. $codeToCoverList[] = new ReflectionClass($className);
  895. }
  896. }
  897. return $codeToCoverList;
  898. }
  899. /**
  900. * @param array $reflectors
  901. *
  902. * @return array
  903. */
  904. private static function resolveReflectionObjectsToLines(array $reflectors)
  905. {
  906. $result = [];
  907. foreach ($reflectors as $reflector) {
  908. $filename = $reflector->getFileName();
  909. if (!isset($result[$filename])) {
  910. $result[$filename] = [];
  911. }
  912. $result[$filename] = array_unique(
  913. array_merge(
  914. $result[$filename],
  915. range($reflector->getStartLine(), $reflector->getEndLine())
  916. )
  917. );
  918. }
  919. return $result;
  920. }
  921. /**
  922. * @param ReflectionMethod $method
  923. *
  924. * @return bool
  925. *
  926. * @since Method available since Release 4.0.8
  927. */
  928. private static function isBeforeClassMethod(ReflectionMethod $method)
  929. {
  930. return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false;
  931. }
  932. /**
  933. * @param ReflectionMethod $method
  934. *
  935. * @return bool
  936. *
  937. * @since Method available since Release 4.0.8
  938. */
  939. private static function isBeforeMethod(ReflectionMethod $method)
  940. {
  941. return preg_match('/@before\b/', $method->getDocComment());
  942. }
  943. /**
  944. * @param ReflectionMethod $method
  945. *
  946. * @return bool
  947. *
  948. * @since Method available since Release 4.0.8
  949. */
  950. private static function isAfterClassMethod(ReflectionMethod $method)
  951. {
  952. return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false;
  953. }
  954. /**
  955. * @param ReflectionMethod $method
  956. *
  957. * @return bool
  958. *
  959. * @since Method available since Release 4.0.8
  960. */
  961. private static function isAfterMethod(ReflectionMethod $method)
  962. {
  963. return preg_match('/@after\b/', $method->getDocComment());
  964. }
  965. }