ProjectCodeTest.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Tests\AutoReview;
  13. use PhpCsFixer\DocBlock\DocBlock;
  14. use PhpCsFixer\FixerFactory;
  15. use PhpCsFixer\Preg;
  16. use PhpCsFixer\Tests\TestCase;
  17. use PhpCsFixer\Tokenizer\Token;
  18. use PhpCsFixer\Tokenizer\Tokens;
  19. use PhpCsFixer\Utils;
  20. use Symfony\Component\Finder\Finder;
  21. use Symfony\Component\Finder\SplFileInfo;
  22. /**
  23. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  24. *
  25. * @internal
  26. *
  27. * @coversNothing
  28. * @group auto-review
  29. * @group covers-nothing
  30. */
  31. final class ProjectCodeTest extends TestCase
  32. {
  33. /**
  34. * This structure contains older classes that are not yet covered by tests.
  35. *
  36. * It may only shrink, never add anything to it.
  37. *
  38. * @var string[]
  39. */
  40. private static $classesWithoutTests = [
  41. \PhpCsFixer\Console\Command\DocumentationCommand::class,
  42. \PhpCsFixer\Console\SelfUpdate\GithubClient::class,
  43. \PhpCsFixer\Doctrine\Annotation\Tokens::class,
  44. \PhpCsFixer\Documentation\DocumentationGenerator::class,
  45. \PhpCsFixer\Runner\FileCachingLintingIterator::class,
  46. ];
  47. public function testThatClassesWithoutTestsVarIsProper(): void
  48. {
  49. $unknownClasses = array_filter(
  50. self::$classesWithoutTests,
  51. static function (string $class) { return !class_exists($class) && !trait_exists($class); }
  52. );
  53. static::assertSame([], $unknownClasses);
  54. }
  55. /**
  56. * @dataProvider provideSrcConcreteClassCases
  57. */
  58. public function testThatSrcClassHaveTestClass(string $className): void
  59. {
  60. $testClassName = 'PhpCsFixer\\Tests'.substr($className, 10).'Test';
  61. if (\in_array($className, self::$classesWithoutTests, true)) {
  62. static::assertFalse(class_exists($testClassName), sprintf('Class "%s" already has tests, so it should be removed from "%s::$classesWithoutTests".', $className, __CLASS__));
  63. static::markTestIncomplete(sprintf('Class "%s" has no tests yet, please help and add it.', $className));
  64. }
  65. static::assertTrue(class_exists($testClassName), sprintf('Expected test class "%s" for "%s" not found.', $testClassName, $className));
  66. static::assertTrue(is_subclass_of($testClassName, TestCase::class), sprintf('Expected test class "%s" to be a subclass of "\PhpCsFixer\Tests\TestCase".', $testClassName));
  67. }
  68. /**
  69. * @dataProvider provideSrcClassesNotAbuseInterfacesCases
  70. */
  71. public function testThatSrcClassesNotAbuseInterfaces(string $className): void
  72. {
  73. $rc = new \ReflectionClass($className);
  74. $allowedMethods = array_map(
  75. function (\ReflectionClass $interface) {
  76. return $this->getPublicMethodNames($interface);
  77. },
  78. $rc->getInterfaces()
  79. );
  80. if (\count($allowedMethods)) {
  81. $allowedMethods = array_unique(array_merge(...array_values($allowedMethods)));
  82. }
  83. $allowedMethods[] = '__construct';
  84. $allowedMethods[] = '__destruct';
  85. $allowedMethods[] = '__wakeup';
  86. $exceptionMethods = [
  87. 'configure', // due to AbstractFixer::configure
  88. 'getConfigurationDefinition', // due to AbstractFixer::getConfigurationDefinition
  89. 'getDefaultConfiguration', // due to AbstractFixer::getDefaultConfiguration
  90. 'setWhitespacesConfig', // due to AbstractFixer::setWhitespacesConfig
  91. ];
  92. $definedMethods = $this->getPublicMethodNames($rc);
  93. $extraMethods = array_diff(
  94. $definedMethods,
  95. $allowedMethods,
  96. $exceptionMethods
  97. );
  98. sort($extraMethods);
  99. static::assertEmpty(
  100. $extraMethods,
  101. sprintf(
  102. "Class '%s' should not have public methods that are not part of implemented interfaces.\nViolations:\n%s",
  103. $className,
  104. implode("\n", array_map(static function (string $item) {
  105. return " * {$item}";
  106. }, $extraMethods))
  107. )
  108. );
  109. }
  110. /**
  111. * @dataProvider provideSrcClassCases
  112. */
  113. public function testThatSrcClassesNotExposeProperties(string $className): void
  114. {
  115. $rc = new \ReflectionClass($className);
  116. static::assertEmpty(
  117. $rc->getProperties(\ReflectionProperty::IS_PUBLIC),
  118. sprintf('Class \'%s\' should not have public properties.', $className)
  119. );
  120. if ($rc->isFinal()) {
  121. return;
  122. }
  123. $allowedProps = [];
  124. $definedProps = $rc->getProperties(\ReflectionProperty::IS_PROTECTED);
  125. if (false !== $rc->getParentClass()) {
  126. $allowedProps = $rc->getParentClass()->getProperties(\ReflectionProperty::IS_PROTECTED);
  127. }
  128. $allowedProps = array_map(static function (\ReflectionProperty $item) {
  129. return $item->getName();
  130. }, $allowedProps);
  131. $definedProps = array_map(static function (\ReflectionProperty $item) {
  132. return $item->getName();
  133. }, $definedProps);
  134. $exceptionPropsPerClass = [
  135. \PhpCsFixer\AbstractPhpdocTypesFixer::class => ['tags'],
  136. \PhpCsFixer\AbstractFixer::class => ['configuration', 'configurationDefinition', 'whitespacesConfig'],
  137. \PhpCsFixer\AbstractProxyFixer::class => ['proxyFixers'],
  138. ];
  139. $extraProps = array_diff(
  140. $definedProps,
  141. $allowedProps,
  142. isset($exceptionPropsPerClass[$className]) ? $exceptionPropsPerClass[$className] : []
  143. );
  144. sort($extraProps);
  145. static::assertEmpty(
  146. $extraProps,
  147. sprintf(
  148. "Class '%s' should not have protected properties.\nViolations:\n%s",
  149. $className,
  150. implode("\n", array_map(static function (string $item) {
  151. return " * {$item}";
  152. }, $extraProps))
  153. )
  154. );
  155. }
  156. /**
  157. * @dataProvider provideTestClassCases
  158. */
  159. public function testThatTestClassesAreTraitOrAbstractOrFinal(string $testClassName): void
  160. {
  161. $rc = new \ReflectionClass($testClassName);
  162. static::assertTrue(
  163. $rc->isTrait() || $rc->isAbstract() || $rc->isFinal(),
  164. sprintf('Test class %s should be trait, abstract or final.', $testClassName)
  165. );
  166. }
  167. /**
  168. * @dataProvider provideTestClassCases
  169. */
  170. public function testThatTestClassesAreInternal(string $testClassName): void
  171. {
  172. $rc = new \ReflectionClass($testClassName);
  173. $doc = new DocBlock($rc->getDocComment());
  174. static::assertNotEmpty(
  175. $doc->getAnnotationsOfType('internal'),
  176. sprintf('Test class %s should have internal annotation.', $testClassName)
  177. );
  178. }
  179. /**
  180. * @dataProvider provideTestClassCases
  181. */
  182. public function testThatPublicMethodsAreCorrectlyNamed(string $testClassName): void
  183. {
  184. $reflectionClass = new \ReflectionClass($testClassName);
  185. $publicMethods = array_filter(
  186. $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC),
  187. static function (\ReflectionMethod $reflectionMethod) use ($reflectionClass) {
  188. return $reflectionMethod->getDeclaringClass()->getName() === $reflectionClass->getName();
  189. }
  190. );
  191. if ([] === $publicMethods) {
  192. $this->addToAssertionCount(1); // no methods to test, all good!
  193. }
  194. foreach ($publicMethods as $method) {
  195. static::assertMatchesRegularExpression(
  196. '/^(test|expect|provide|setUpBeforeClass$|tearDownAfterClass$)/',
  197. $method->getName(),
  198. sprintf('Public method "%s::%s" is not properly named.', $reflectionClass->getName(), $method->getName())
  199. );
  200. }
  201. }
  202. /**
  203. * @dataProvider provideTestClassCases
  204. */
  205. public function testThatDataProvidersAreCorrectlyNamed(string $testClassName): void
  206. {
  207. $usedDataProviderMethodNames = $this->getUsedDataProviderMethodNames($testClassName);
  208. if (empty($usedDataProviderMethodNames)) {
  209. $this->addToAssertionCount(1); // no data providers to test, all good!
  210. return;
  211. }
  212. foreach ($usedDataProviderMethodNames as $dataProviderMethodName) {
  213. static::assertMatchesRegularExpression('/^provide[A-Z]\S+Cases$/', $dataProviderMethodName, sprintf(
  214. 'Data provider in "%s" with name "%s" is not correctly named.',
  215. $testClassName,
  216. $dataProviderMethodName
  217. ));
  218. }
  219. }
  220. /**
  221. * @dataProvider provideTestClassCases
  222. */
  223. public function testThatDataProvidersAreUsed(string $testClassName): void
  224. {
  225. $reflectionClass = new \ReflectionClass($testClassName);
  226. $definedDataProviders = array_filter(
  227. $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC),
  228. static function (\ReflectionMethod $reflectionMethod) use ($reflectionClass) {
  229. return $reflectionMethod->getDeclaringClass()->getName() === $reflectionClass->getName()
  230. && 'provide' === substr($reflectionMethod->getName(), 0, 7);
  231. }
  232. );
  233. if ([] === $definedDataProviders) {
  234. $this->addToAssertionCount(1); // no methods to test, all good!
  235. }
  236. $usedDataProviderMethodNames = $this->getUsedDataProviderMethodNames($testClassName);
  237. foreach ($definedDataProviders as $definedDataProvider) {
  238. static::assertContains(
  239. $definedDataProvider->getName(),
  240. $usedDataProviderMethodNames,
  241. sprintf('Data provider in "%s" with name "%s" is not used.', $definedDataProvider->getDeclaringClass()->getName(), $definedDataProvider->getName())
  242. );
  243. }
  244. }
  245. /**
  246. * @dataProvider provideTestClassCases
  247. */
  248. public function testThatTestClassCoversAreCorrect(string $testClassName): void
  249. {
  250. $reflectionClass = new \ReflectionClass($testClassName);
  251. if ($reflectionClass->isAbstract() || $reflectionClass->isInterface()) {
  252. self::addToAssertionCount(1);
  253. return;
  254. }
  255. $doc = $reflectionClass->getDocComment();
  256. static::assertNotFalse($doc);
  257. if (1 === Preg::match('/@coversNothing/', $doc, $matches)) {
  258. self::addToAssertionCount(1);
  259. return;
  260. }
  261. $covers = Preg::match('/@covers (\S*)/', $doc, $matches);
  262. static::assertNotFalse($covers, sprintf('Missing @covers in PHPDoc of test class "%s".', $testClassName));
  263. array_shift($matches);
  264. $class = '\\'.str_replace('PhpCsFixer\Tests\\', 'PhpCsFixer\\', substr($testClassName, 0, -4));
  265. $parentClass = (new \ReflectionClass($class))->getParentClass();
  266. $parentClassName = false === $parentClass ? null : '\\'.$parentClass->getName();
  267. foreach ($matches as $match) {
  268. if ($match === $class || $parentClassName === $match) {
  269. $this->addToAssertionCount(1);
  270. continue;
  271. }
  272. static::fail(sprintf('Unexpected @covers "%s" for "%s".', $match, $testClassName));
  273. }
  274. }
  275. /**
  276. * @dataProvider provideClassesWherePregFunctionsAreForbiddenCases
  277. */
  278. public function testThereIsNoPregFunctionUsedDirectly(string $className): void
  279. {
  280. $rc = new \ReflectionClass($className);
  281. $tokens = Tokens::fromCode(file_get_contents($rc->getFileName()));
  282. $stringTokens = array_filter(
  283. $tokens->toArray(),
  284. static function (Token $token) {
  285. return $token->isGivenKind(T_STRING);
  286. }
  287. );
  288. $strings = array_map(
  289. static function (Token $token) {
  290. return $token->getContent();
  291. },
  292. $stringTokens
  293. );
  294. $strings = array_unique($strings);
  295. $message = sprintf('Class %s must not use preg_*, it shall use Preg::* instead.', $className);
  296. static::assertNotContains('preg_filter', $strings, $message);
  297. static::assertNotContains('preg_grep', $strings, $message);
  298. static::assertNotContains('preg_match', $strings, $message);
  299. static::assertNotContains('preg_match_all', $strings, $message);
  300. static::assertNotContains('preg_replace', $strings, $message);
  301. static::assertNotContains('preg_replace_callback', $strings, $message);
  302. static::assertNotContains('preg_split', $strings, $message);
  303. }
  304. /**
  305. * @dataProvider provideTestClassCases
  306. */
  307. public function testExpectedInputOrder(string $testClassName): void
  308. {
  309. $reflectionClass = new \ReflectionClass($testClassName);
  310. $publicMethods = array_filter(
  311. $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC),
  312. static function (\ReflectionMethod $reflectionMethod) use ($reflectionClass) {
  313. return $reflectionMethod->getDeclaringClass()->getName() === $reflectionClass->getName();
  314. }
  315. );
  316. if ([] === $publicMethods) {
  317. $this->addToAssertionCount(1); // no methods to test, all good!
  318. return;
  319. }
  320. /** @var \ReflectionMethod $method */
  321. foreach ($publicMethods as $method) {
  322. $parameters = $method->getParameters();
  323. if (\count($parameters) < 2) {
  324. $this->addToAssertionCount(1); // not enough parameters to test, all good!
  325. continue;
  326. }
  327. $expected = [
  328. 'expected' => false,
  329. 'input' => false,
  330. ];
  331. for ($i = \count($parameters) - 1; $i >= 0; --$i) {
  332. $name = $parameters[$i]->getName();
  333. if (isset($expected[$name])) {
  334. $expected[$name] = $i;
  335. }
  336. }
  337. $expected = array_filter($expected);
  338. if (\count($expected) < 2) {
  339. $this->addToAssertionCount(1); // not enough parameters to test, all good!
  340. continue;
  341. }
  342. static::assertLessThan(
  343. $expected['input'],
  344. $expected['expected'],
  345. sprintf('Public method "%s::%s" has parameter \'input\' before \'expected\'.', $reflectionClass->getName(), $method->getName())
  346. );
  347. }
  348. }
  349. /**
  350. * @dataProvider provideSrcClassCases
  351. * @dataProvider provideTestClassCases
  352. */
  353. public function testAllCodeContainSingleClassy(string $className): void
  354. {
  355. $headerTypes = [
  356. T_ABSTRACT,
  357. T_AS,
  358. T_COMMENT,
  359. T_DECLARE,
  360. T_DOC_COMMENT,
  361. T_FINAL,
  362. T_LNUMBER,
  363. T_NAMESPACE,
  364. T_NS_SEPARATOR,
  365. T_OPEN_TAG,
  366. T_STRING,
  367. T_USE,
  368. T_WHITESPACE,
  369. ];
  370. $rc = new \ReflectionClass($className);
  371. $file = $rc->getFileName();
  372. $tokens = Tokens::fromCode(file_get_contents($file));
  373. $classyIndex = null;
  374. static::assertTrue($tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds()), sprintf('File "%s" should contains a classy.', $file));
  375. foreach ($tokens as $index => $token) {
  376. if ($token->isClassy()) {
  377. $classyIndex = $index;
  378. break;
  379. }
  380. if (!$token->isGivenKind($headerTypes) && !$token->equalsAny([';', '=', '(', ')'])) {
  381. static::fail(sprintf('File "%s" should only contains single classy, found "%s" @ %d.', $file, $token->toJson(), $index));
  382. }
  383. }
  384. static::assertNotNull($classyIndex, sprintf('File "%s" does not contain a classy.', $file));
  385. $nextTokenOfKind = $tokens->getNextTokenOfKind($classyIndex, ['{']);
  386. if (!\is_int($nextTokenOfKind)) {
  387. throw new \UnexpectedValueException('Classy without {} - braces.');
  388. }
  389. $classyEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextTokenOfKind);
  390. static::assertNull($tokens->getNextNonWhitespace($classyEndIndex), sprintf('File "%s" should only contains a single classy.', $file));
  391. }
  392. /**
  393. * @dataProvider provideSrcClassCases
  394. *
  395. * @param string $className
  396. */
  397. public function testThereIsNoTriggerErrorUsedDirectly($className): void
  398. {
  399. if (Utils::class === $className) {
  400. $this->addToAssertionCount(1); // This is where "trigger_error" should be
  401. return;
  402. }
  403. $rc = new \ReflectionClass($className);
  404. $tokens = Tokens::fromCode(file_get_contents($rc->getFileName()));
  405. $triggerErrors = array_filter(
  406. $tokens->toArray(),
  407. static function (Token $token) {
  408. return $token->equals([T_STRING, 'trigger_error'], false);
  409. }
  410. );
  411. static::assertCount(
  412. 0,
  413. $triggerErrors,
  414. sprintf('Class "%s" must not use "trigger_error", it shall use "Util::triggerDeprecation" instead.', $className)
  415. );
  416. }
  417. public function provideSrcClassCases()
  418. {
  419. return array_map(
  420. static function (string $item) {
  421. return [$item];
  422. },
  423. $this->getSrcClasses()
  424. );
  425. }
  426. public function provideSrcClassesNotAbuseInterfacesCases()
  427. {
  428. return array_map(
  429. static function (string $item) {
  430. return [$item];
  431. },
  432. array_filter($this->getSrcClasses(), static function (string $className) {
  433. $rc = new \ReflectionClass($className);
  434. $doc = false !== $rc->getDocComment()
  435. ? new DocBlock($rc->getDocComment())
  436. : null;
  437. if (
  438. $rc->isInterface()
  439. || ($doc && \count($doc->getAnnotationsOfType('internal')))
  440. || \in_array($className, [
  441. \PhpCsFixer\Finder::class,
  442. \PhpCsFixer\Tests\Test\AbstractFixerTestCase::class,
  443. \PhpCsFixer\Tests\Test\AbstractIntegrationTestCase::class,
  444. \PhpCsFixer\Tokenizer\Tokens::class,
  445. ], true)
  446. ) {
  447. return false;
  448. }
  449. $interfaces = $rc->getInterfaces();
  450. $interfacesCount = \count($interfaces);
  451. if (0 === $interfacesCount) {
  452. return false;
  453. }
  454. if (1 === $interfacesCount) {
  455. $interface = reset($interfaces);
  456. if ('Stringable' === $interface->getName()) {
  457. return false;
  458. }
  459. }
  460. return true;
  461. })
  462. );
  463. }
  464. public function provideSrcConcreteClassCases()
  465. {
  466. return array_map(
  467. static function (string $item) { return [$item]; },
  468. array_filter(
  469. $this->getSrcClasses(),
  470. static function (string $className) {
  471. $rc = new \ReflectionClass($className);
  472. return !$rc->isAbstract() && !$rc->isInterface();
  473. }
  474. )
  475. );
  476. }
  477. public function provideTestClassCases()
  478. {
  479. return array_map(
  480. static function (string $item) {
  481. return [$item];
  482. },
  483. $this->getTestClasses()
  484. );
  485. }
  486. public function provideClassesWherePregFunctionsAreForbiddenCases()
  487. {
  488. return array_map(
  489. static function (string $item) {
  490. return [$item];
  491. },
  492. array_filter(
  493. $this->getSrcClasses(),
  494. static function (string $className) {
  495. return Preg::class !== $className;
  496. }
  497. )
  498. );
  499. }
  500. /**
  501. * @dataProvider providePhpUnitFixerExtendsAbstractPhpUnitFixerCases
  502. */
  503. public function testPhpUnitFixerExtendsAbstractPhpUnitFixer(string $className): void
  504. {
  505. $reflection = new \ReflectionClass($className);
  506. static::assertTrue($reflection->isSubclassOf(\PhpCsFixer\Fixer\AbstractPhpUnitFixer::class));
  507. }
  508. public function providePhpUnitFixerExtendsAbstractPhpUnitFixerCases()
  509. {
  510. $factory = new FixerFactory();
  511. $factory->registerBuiltInFixers();
  512. foreach ($factory->getFixers() as $fixer) {
  513. if (0 !== strpos($fixer->getName(), 'php_unit_')) {
  514. continue;
  515. }
  516. // this one fixes usage of PHPUnit classes
  517. if ($fixer instanceof \PhpCsFixer\Fixer\PhpUnit\PhpUnitNamespacedFixer) {
  518. continue;
  519. }
  520. if ($fixer instanceof \PhpCsFixer\AbstractProxyFixer) {
  521. continue;
  522. }
  523. yield [\get_class($fixer)];
  524. }
  525. }
  526. private function getUsedDataProviderMethodNames(string $testClassName)
  527. {
  528. $dataProviderMethodNames = [];
  529. $tokens = Tokens::fromCode(file_get_contents(
  530. str_replace('\\', \DIRECTORY_SEPARATOR, preg_replace('#^PhpCsFixer\\\Tests#', 'tests', $testClassName)).'.php'
  531. ));
  532. foreach ($tokens as $token) {
  533. if ($token->isGivenKind(T_DOC_COMMENT)) {
  534. $docBlock = new DocBlock($token->getContent());
  535. $dataProviderAnnotations = $docBlock->getAnnotationsOfType('dataProvider');
  536. foreach ($dataProviderAnnotations as $dataProviderAnnotation) {
  537. if (1 === preg_match('/@dataProvider\s+(?P<methodName>\w+)/', $dataProviderAnnotation->getContent(), $matches)) {
  538. $dataProviderMethodNames[] = $matches['methodName'];
  539. }
  540. }
  541. }
  542. }
  543. return array_unique($dataProviderMethodNames);
  544. }
  545. private function getSrcClasses()
  546. {
  547. static $classes;
  548. if (null !== $classes) {
  549. return $classes;
  550. }
  551. $finder = Finder::create()
  552. ->files()
  553. ->name('*.php')
  554. ->in(__DIR__.'/../../src')
  555. ->exclude([
  556. 'Resources',
  557. ])
  558. ;
  559. $classes = array_map(
  560. static function (SplFileInfo $file) {
  561. return sprintf(
  562. '%s\\%s%s%s',
  563. 'PhpCsFixer',
  564. strtr($file->getRelativePath(), \DIRECTORY_SEPARATOR, '\\'),
  565. $file->getRelativePath() ? '\\' : '',
  566. $file->getBasename('.'.$file->getExtension())
  567. );
  568. },
  569. iterator_to_array($finder, false)
  570. );
  571. sort($classes);
  572. return $classes;
  573. }
  574. private function getTestClasses()
  575. {
  576. static $classes;
  577. if (null !== $classes) {
  578. return $classes;
  579. }
  580. $finder = Finder::create()
  581. ->files()
  582. ->name('*.php')
  583. ->in(__DIR__.'/..')
  584. ->exclude([
  585. 'Fixtures',
  586. ])
  587. ;
  588. $classes = array_map(
  589. static function (SplFileInfo $file) {
  590. return sprintf(
  591. 'PhpCsFixer\\Tests\\%s%s%s',
  592. strtr($file->getRelativePath(), \DIRECTORY_SEPARATOR, '\\'),
  593. $file->getRelativePath() ? '\\' : '',
  594. $file->getBasename('.'.$file->getExtension())
  595. );
  596. },
  597. iterator_to_array($finder, false)
  598. );
  599. $classes = array_filter($classes, static function (string $class): bool {
  600. return is_subclass_of($class, TestCase::class);
  601. });
  602. sort($classes);
  603. return $classes;
  604. }
  605. /**
  606. * @return string[]
  607. */
  608. private function getPublicMethodNames(\ReflectionClass $rc): array
  609. {
  610. return array_map(
  611. static function (\ReflectionMethod $rm) {
  612. return $rm->getName();
  613. },
  614. $rc->getMethods(\ReflectionMethod::IS_PUBLIC)
  615. );
  616. }
  617. }