PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/rmoshiur81/Larave-Grading
PHP | 1140 lines | 802 code | 103 blank | 235 comment | 87 complexity | 3aef1e3819d72625ee282bc82ea1e520 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. if ($candidateRow[0] !== '[') {
  432. break;
  433. }
  434. $dataSet = json_decode($candidateRow, true);
  435. if (json_last_error() != JSON_ERROR_NONE) {
  436. throw new PHPUnit_Framework_Exception(
  437. 'The dataset for the @testWith annotation cannot be parsed: ' . json_last_error_msg()
  438. );
  439. }
  440. $data[] = $dataSet;
  441. }
  442. if (!$data) {
  443. throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.');
  444. }
  445. return $data;
  446. }
  447. }
  448. private static function cleanUpMultiLineAnnotation($docComment)
  449. {
  450. //removing initial ' * ' for docComment
  451. $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
  452. $docComment = substr($docComment, 0, -1);
  453. $docComment = rtrim($docComment, "\n");
  454. return $docComment;
  455. }
  456. /**
  457. * @param string $className
  458. * @param string $methodName
  459. *
  460. * @return array
  461. *
  462. * @throws ReflectionException
  463. *
  464. * @since Method available since Release 3.4.0
  465. */
  466. public static function parseTestMethodAnnotations($className, $methodName = '')
  467. {
  468. if (!isset(self::$annotationCache[$className])) {
  469. $class = new ReflectionClass($className);
  470. self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
  471. }
  472. if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
  473. try {
  474. $method = new ReflectionMethod($className, $methodName);
  475. $annotations = self::parseAnnotations($method->getDocComment());
  476. } catch (ReflectionException $e) {
  477. $annotations = [];
  478. }
  479. self::$annotationCache[$className . '::' . $methodName] = $annotations;
  480. }
  481. return [
  482. 'class' => self::$annotationCache[$className],
  483. 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : []
  484. ];
  485. }
  486. /**
  487. * @param string $className
  488. * @param string $methodName
  489. *
  490. * @return array
  491. *
  492. * @since Method available since Release 5.4.0
  493. */
  494. public static function getInlineAnnotations($className, $methodName)
  495. {
  496. $method = new ReflectionMethod($className, $methodName);
  497. $code = file($method->getFileName());
  498. $lineNumber = $method->getStartLine();
  499. $startLine = $method->getStartLine() - 1;
  500. $endLine = $method->getEndLine() - 1;
  501. $methodLines = array_slice($code, $startLine, $endLine - $startLine + 1);
  502. $annotations = [];
  503. foreach ($methodLines as $line) {
  504. if (preg_match('#/\*\*?\s*@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?\*/$#m', $line, $matches)) {
  505. $annotations[strtolower($matches['name'])] = [
  506. 'line' => $lineNumber,
  507. 'value' => $matches['value']
  508. ];
  509. }
  510. $lineNumber++;
  511. }
  512. return $annotations;
  513. }
  514. /**
  515. * @param string $docblock
  516. *
  517. * @return array
  518. *
  519. * @since Method available since Release 3.4.0
  520. */
  521. private static function parseAnnotations($docblock)
  522. {
  523. $annotations = [];
  524. // Strip away the docblock header and footer to ease parsing of one line annotations
  525. $docblock = substr($docblock, 3, -2);
  526. if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
  527. $numMatches = count($matches[0]);
  528. for ($i = 0; $i < $numMatches; ++$i) {
  529. $annotations[$matches['name'][$i]][] = $matches['value'][$i];
  530. }
  531. }
  532. return $annotations;
  533. }
  534. /**
  535. * Returns the backup settings for a test.
  536. *
  537. * @param string $className
  538. * @param string $methodName
  539. *
  540. * @return array
  541. *
  542. * @since Method available since Release 3.4.0
  543. */
  544. public static function getBackupSettings($className, $methodName)
  545. {
  546. return [
  547. 'backupGlobals' => self::getBooleanAnnotationSetting(
  548. $className,
  549. $methodName,
  550. 'backupGlobals'
  551. ),
  552. 'backupStaticAttributes' => self::getBooleanAnnotationSetting(
  553. $className,
  554. $methodName,
  555. 'backupStaticAttributes'
  556. )
  557. ];
  558. }
  559. /**
  560. * Returns the dependencies for a test class or method.
  561. *
  562. * @param string $className
  563. * @param string $methodName
  564. *
  565. * @return array
  566. *
  567. * @since Method available since Release 3.4.0
  568. */
  569. public static function getDependencies($className, $methodName)
  570. {
  571. $annotations = self::parseTestMethodAnnotations(
  572. $className,
  573. $methodName
  574. );
  575. $dependencies = [];
  576. if (isset($annotations['class']['depends'])) {
  577. $dependencies = $annotations['class']['depends'];
  578. }
  579. if (isset($annotations['method']['depends'])) {
  580. $dependencies = array_merge(
  581. $dependencies,
  582. $annotations['method']['depends']
  583. );
  584. }
  585. return array_unique($dependencies);
  586. }
  587. /**
  588. * Returns the error handler settings for a test.
  589. *
  590. * @param string $className
  591. * @param string $methodName
  592. *
  593. * @return bool
  594. *
  595. * @since Method available since Release 3.4.0
  596. */
  597. public static function getErrorHandlerSettings($className, $methodName)
  598. {
  599. return self::getBooleanAnnotationSetting(
  600. $className,
  601. $methodName,
  602. 'errorHandler'
  603. );
  604. }
  605. /**
  606. * Returns the groups for a test class or method.
  607. *
  608. * @param string $className
  609. * @param string $methodName
  610. *
  611. * @return array
  612. *
  613. * @since Method available since Release 3.2.0
  614. */
  615. public static function getGroups($className, $methodName = '')
  616. {
  617. $annotations = self::parseTestMethodAnnotations(
  618. $className,
  619. $methodName
  620. );
  621. $groups = [];
  622. if (isset($annotations['method']['author'])) {
  623. $groups = $annotations['method']['author'];
  624. } elseif (isset($annotations['class']['author'])) {
  625. $groups = $annotations['class']['author'];
  626. }
  627. if (isset($annotations['class']['group'])) {
  628. $groups = array_merge($groups, $annotations['class']['group']);
  629. }
  630. if (isset($annotations['method']['group'])) {
  631. $groups = array_merge($groups, $annotations['method']['group']);
  632. }
  633. if (isset($annotations['class']['ticket'])) {
  634. $groups = array_merge($groups, $annotations['class']['ticket']);
  635. }
  636. if (isset($annotations['method']['ticket'])) {
  637. $groups = array_merge($groups, $annotations['method']['ticket']);
  638. }
  639. foreach (['method', 'class'] as $element) {
  640. foreach (['small', 'medium', 'large'] as $size) {
  641. if (isset($annotations[$element][$size])) {
  642. $groups[] = $size;
  643. break 2;
  644. }
  645. }
  646. }
  647. return array_unique($groups);
  648. }
  649. /**
  650. * Returns the size of the test.
  651. *
  652. * @param string $className
  653. * @param string $methodName
  654. *
  655. * @return int
  656. *
  657. * @since Method available since Release 3.6.0
  658. */
  659. public static function getSize($className, $methodName)
  660. {
  661. $groups = array_flip(self::getGroups($className, $methodName));
  662. $size = self::UNKNOWN;
  663. $class = new ReflectionClass($className);
  664. if (isset($groups['large']) ||
  665. (class_exists('PHPUnit_Extensions_Database_TestCase', false) &&
  666. $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase'))) {
  667. $size = self::LARGE;
  668. } elseif (isset($groups['medium'])) {
  669. $size = self::MEDIUM;
  670. } elseif (isset($groups['small'])) {
  671. $size = self::SMALL;
  672. }
  673. return $size;
  674. }
  675. /**
  676. * Returns the tickets for a test class or method.
  677. *
  678. * @param string $className
  679. * @param string $methodName
  680. *
  681. * @return array
  682. *
  683. * @since Method available since Release 3.4.0
  684. */
  685. public static function getTickets($className, $methodName)
  686. {
  687. $annotations = self::parseTestMethodAnnotations(
  688. $className,
  689. $methodName
  690. );
  691. $tickets = [];
  692. if (isset($annotations['class']['ticket'])) {
  693. $tickets = $annotations['class']['ticket'];
  694. }
  695. if (isset($annotations['method']['ticket'])) {
  696. $tickets = array_merge($tickets, $annotations['method']['ticket']);
  697. }
  698. return array_unique($tickets);
  699. }
  700. /**
  701. * Returns the process isolation settings for a test.
  702. *
  703. * @param string $className
  704. * @param string $methodName
  705. *
  706. * @return bool
  707. *
  708. * @since Method available since Release 3.4.1
  709. */
  710. public static function getProcessIsolationSettings($className, $methodName)
  711. {
  712. $annotations = self::parseTestMethodAnnotations(
  713. $className,
  714. $methodName
  715. );
  716. if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
  717. isset($annotations['method']['runInSeparateProcess'])) {
  718. return true;
  719. } else {
  720. return false;
  721. }
  722. }
  723. /**
  724. * Returns the preserve global state settings for a test.
  725. *
  726. * @param string $className
  727. * @param string $methodName
  728. *
  729. * @return bool
  730. *
  731. * @since Method available since Release 3.4.0
  732. */
  733. public static function getPreserveGlobalStateSettings($className, $methodName)
  734. {
  735. return self::getBooleanAnnotationSetting(
  736. $className,
  737. $methodName,
  738. 'preserveGlobalState'
  739. );
  740. }
  741. /**
  742. * @param string $className
  743. *
  744. * @return array
  745. *
  746. * @since Method available since Release 4.0.8
  747. */
  748. public static function getHookMethods($className)
  749. {
  750. if (!class_exists($className, false)) {
  751. return self::emptyHookMethodsArray();
  752. }
  753. if (!isset(self::$hookMethods[$className])) {
  754. self::$hookMethods[$className] = self::emptyHookMethodsArray();
  755. try {
  756. $class = new ReflectionClass($className);
  757. foreach ($class->getMethods() as $method) {
  758. if (self::isBeforeClassMethod($method)) {
  759. self::$hookMethods[$className]['beforeClass'][] = $method->getName();
  760. }
  761. if (self::isBeforeMethod($method)) {
  762. self::$hookMethods[$className]['before'][] = $method->getName();
  763. }
  764. if (self::isAfterMethod($method)) {
  765. self::$hookMethods[$className]['after'][] = $method->getName();
  766. }
  767. if (self::isAfterClassMethod($method)) {
  768. self::$hookMethods[$className]['afterClass'][] = $method->getName();
  769. }
  770. }
  771. } catch (ReflectionException $e) {
  772. }
  773. }
  774. return self::$hookMethods[$className];
  775. }
  776. /**
  777. * @return array
  778. *
  779. * @since Method available since Release 4.0.9
  780. */
  781. private static function emptyHookMethodsArray()
  782. {
  783. return [
  784. 'beforeClass' => ['setUpBeforeClass'],
  785. 'before' => ['setUp'],
  786. 'after' => ['tearDown'],
  787. 'afterClass' => ['tearDownAfterClass']
  788. ];
  789. }
  790. /**
  791. * @param string $className
  792. * @param string $methodName
  793. * @param string $settingName
  794. *
  795. * @return bool
  796. *
  797. * @since Method available since Release 3.4.0
  798. */
  799. private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
  800. {
  801. $annotations = self::parseTestMethodAnnotations(
  802. $className,
  803. $methodName
  804. );
  805. $result = null;
  806. if (isset($annotations['class'][$settingName])) {
  807. if ($annotations['class'][$settingName][0] == 'enabled') {
  808. $result = true;
  809. } elseif ($annotations['class'][$settingName][0] == 'disabled') {
  810. $result = false;
  811. }
  812. }
  813. if (isset($annotations['method'][$settingName])) {
  814. if ($annotations['method'][$settingName][0] == 'enabled') {
  815. $result = true;
  816. } elseif ($annotations['method'][$settingName][0] == 'disabled') {
  817. $result = false;
  818. }
  819. }
  820. return $result;
  821. }
  822. /**
  823. * @param string $element
  824. *
  825. * @return array
  826. *
  827. * @throws PHPUnit_Framework_InvalidCoversTargetException
  828. *
  829. * @since Method available since Release 4.0.0
  830. */
  831. private static function resolveElementToReflectionObjects($element)
  832. {
  833. $codeToCoverList = [];
  834. if (strpos($element, '\\') !== false && function_exists($element)) {
  835. $codeToCoverList[] = new ReflectionFunction($element);
  836. } elseif (strpos($element, '::') !== false) {
  837. list($className, $methodName) = explode('::', $element);
  838. if (isset($methodName[0]) && $methodName[0] == '<') {
  839. $classes = [$className];
  840. foreach ($classes as $className) {
  841. if (!class_exists($className) &&
  842. !interface_exists($className) &&
  843. !trait_exists($className)) {
  844. throw new PHPUnit_Framework_InvalidCoversTargetException(
  845. sprintf(
  846. 'Trying to @cover or @use not existing class or ' .
  847. 'interface "%s".',
  848. $className
  849. )
  850. );
  851. }
  852. $class = new ReflectionClass($className);
  853. $methods = $class->getMethods();
  854. $inverse = isset($methodName[1]) && $methodName[1] == '!';
  855. if (strpos($methodName, 'protected')) {
  856. $visibility = 'isProtected';
  857. } elseif (strpos($methodName, 'private')) {
  858. $visibility = 'isPrivate';
  859. } elseif (strpos($methodName, 'public')) {
  860. $visibility = 'isPublic';
  861. }
  862. foreach ($methods as $method) {
  863. if ($inverse && !$method->$visibility()) {
  864. $codeToCoverList[] = $method;
  865. } elseif (!$inverse && $method->$visibility()) {
  866. $codeToCoverList[] = $method;
  867. }
  868. }
  869. }
  870. } else {
  871. $classes = [$className];
  872. foreach ($classes as $className) {
  873. if ($className == '' && function_exists($methodName)) {
  874. $codeToCoverList[] = new ReflectionFunction(
  875. $methodName
  876. );
  877. } else {
  878. if (!((class_exists($className) ||
  879. interface_exists($className) ||
  880. trait_exists($className)) &&
  881. method_exists($className, $methodName))) {
  882. throw new PHPUnit_Framework_InvalidCoversTargetException(
  883. sprintf(
  884. 'Trying to @cover or @use not existing method "%s::%s".',
  885. $className,
  886. $methodName
  887. )
  888. );
  889. }
  890. $codeToCoverList[] = new ReflectionMethod(
  891. $className,
  892. $methodName
  893. );
  894. }
  895. }
  896. }
  897. } else {
  898. $extended = false;
  899. if (strpos($element, '<extended>') !== false) {
  900. $element = str_replace('<extended>', '', $element);
  901. $extended = true;
  902. }
  903. $classes = [$element];
  904. if ($extended) {
  905. $classes = array_merge(
  906. $classes,
  907. class_implements($element),
  908. class_parents($element)
  909. );
  910. }
  911. foreach ($classes as $className) {
  912. if (!class_exists($className) &&
  913. !interface_exists($className) &&
  914. !trait_exists($className)) {
  915. throw new PHPUnit_Framework_InvalidCoversTargetException(
  916. sprintf(
  917. 'Trying to @cover or @use not existing class or ' .
  918. 'interface "%s".',
  919. $className
  920. )
  921. );
  922. }
  923. $codeToCoverList[] = new ReflectionClass($className);
  924. }
  925. }
  926. return $codeToCoverList;
  927. }
  928. /**
  929. * @param array $reflectors
  930. *
  931. * @return array
  932. */
  933. private static function resolveReflectionObjectsToLines(array $reflectors)
  934. {
  935. $result = [];
  936. foreach ($reflectors as $reflector) {
  937. $filename = $reflector->getFileName();
  938. if (!isset($result[$filename])) {
  939. $result[$filename] = [];
  940. }
  941. $result[$filename] = array_unique(
  942. array_merge(
  943. $result[$filename],
  944. range($reflector->getStartLine(), $reflector->getEndLine())
  945. )
  946. );
  947. }
  948. return $result;
  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 isBeforeClassMethod(ReflectionMethod $method)
  958. {
  959. return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false;
  960. }
  961. /**
  962. * @param ReflectionMethod $method
  963. *
  964. * @return bool
  965. *
  966. * @since Method available since Release 4.0.8
  967. */
  968. private static function isBeforeMethod(ReflectionMethod $method)
  969. {
  970. return preg_match('/@before\b/', $method->getDocComment());
  971. }
  972. /**
  973. * @param ReflectionMethod $method
  974. *
  975. * @return bool
  976. *
  977. * @since Method available since Release 4.0.8
  978. */
  979. private static function isAfterClassMethod(ReflectionMethod $method)
  980. {
  981. return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false;
  982. }
  983. /**
  984. * @param ReflectionMethod $method
  985. *
  986. * @return bool
  987. *
  988. * @since Method available since Release 4.0.8
  989. */
  990. private static function isAfterMethod(ReflectionMethod $method)
  991. {
  992. return preg_match('/@after\b/', $method->getDocComment());
  993. }
  994. }