DocumentationTest.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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\Console\Report\FixReport\ReporterFactory;
  14. use PhpCsFixer\Documentation\DocumentationLocator;
  15. use PhpCsFixer\Documentation\FixerDocumentGenerator;
  16. use PhpCsFixer\Documentation\RuleSetDocumentationGenerator;
  17. use PhpCsFixer\Fixer\FixerInterface;
  18. use PhpCsFixer\FixerFactory;
  19. use PhpCsFixer\RuleSet\RuleSets;
  20. use PhpCsFixer\Tests\TestCase;
  21. use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
  22. use Symfony\Component\Finder\Finder;
  23. /**
  24. * @internal
  25. *
  26. * @coversNothing
  27. *
  28. * @group legacy
  29. * @group auto-review
  30. */
  31. final class DocumentationTest extends TestCase
  32. {
  33. use ExpectDeprecationTrait;
  34. /**
  35. * @dataProvider provideFixerDocumentationFileIsUpToDateCases
  36. */
  37. public function testFixerDocumentationFileIsUpToDate(FixerInterface $fixer): void
  38. {
  39. // @TODO 4.0 Remove this expectations
  40. $this->expectDeprecation('Rule set "@PER" is deprecated. Use "@PER-CS" instead.');
  41. $this->expectDeprecation('Rule set "@PER:risky" is deprecated. Use "@PER-CS:risky" instead.');
  42. $locator = new DocumentationLocator();
  43. $generator = new FixerDocumentGenerator($locator);
  44. $path = $locator->getFixerDocumentationFilePath($fixer);
  45. self::assertFileExists($path);
  46. $expected = $generator->generateFixerDocumentation($fixer);
  47. $actual = file_get_contents($path);
  48. $expected = preg_replace_callback(
  49. '/
  50. # an example
  51. (?<before>
  52. Example\ \#\d\n
  53. ~+\n
  54. \n
  55. (?:\*Default\*\ configuration\.\n\n)?
  56. (?:With\ configuration:.*?\n\n)?
  57. )
  58. # with a diff that could not be generated
  59. \.\.\ error::\n
  60. \ \ \ Cannot\ generate\ diff\ for\ code\ sample\ \#\d\ of\ rule\ .+?:\n
  61. \ \ \ the\ sample\ is\ not\ suitable\ for\ current\ version\ of\ PHP\ \(.+?\).\n
  62. # followed by another title or end of file
  63. (?<after>
  64. \n
  65. [^ \n].*?
  66. \n
  67. |$
  68. )
  69. /x',
  70. static function (array $matches) use ($actual): string {
  71. $before = preg_quote($matches['before'], '/');
  72. $after = preg_quote($matches['after'], '/');
  73. $replacement = '[UNAVAILABLE EXAMPLE DIFF]';
  74. if (1 === preg_match("/{$before}(\\.\\. code-block:: diff.*?){$after}/s", $actual, $actualMatches)) {
  75. $replacement = $actualMatches[1];
  76. }
  77. return $matches[1].$replacement.$matches[2];
  78. },
  79. $expected
  80. );
  81. self::assertSame($expected, $actual);
  82. }
  83. public static function provideFixerDocumentationFileIsUpToDateCases(): iterable
  84. {
  85. foreach (self::getFixers() as $fixer) {
  86. yield $fixer->getName() => [$fixer];
  87. }
  88. }
  89. public function testFixersDocumentationIndexFileIsUpToDate(): void
  90. {
  91. $locator = new DocumentationLocator();
  92. $generator = new FixerDocumentGenerator($locator);
  93. self::assertFileEqualsString(
  94. $generator->generateFixersDocumentationIndex(self::getFixers()),
  95. $locator->getFixersDocumentationIndexFilePath()
  96. );
  97. }
  98. public function testFixersDocumentationDirectoryHasNoExtraFiles(): void
  99. {
  100. $generator = new DocumentationLocator();
  101. self::assertCount(
  102. \count(self::getFixers()) + 1,
  103. (new Finder())->files()->in($generator->getFixersDocumentationDirectoryPath())
  104. );
  105. }
  106. public function testRuleSetsDocumentationIsUpToDate(): void
  107. {
  108. $locator = new DocumentationLocator();
  109. $generator = new RuleSetDocumentationGenerator($locator);
  110. $fixers = self::getFixers();
  111. $paths = [];
  112. foreach (RuleSets::getSetDefinitions() as $name => $definition) {
  113. $path = $locator->getRuleSetsDocumentationFilePath($name);
  114. $paths[$path] = $definition;
  115. self::assertFileEqualsString(
  116. $generator->generateRuleSetsDocumentation($definition, $fixers),
  117. $path,
  118. sprintf('RuleSet documentation is generated (please see CONTRIBUTING.md), file "%s".', $path)
  119. );
  120. }
  121. $indexFilePath = $locator->getRuleSetsDocumentationIndexFilePath();
  122. self::assertFileEqualsString(
  123. $generator->generateRuleSetsDocumentationIndex($paths),
  124. $indexFilePath,
  125. sprintf('RuleSet documentation is generated (please CONTRIBUTING.md), file "%s".', $indexFilePath)
  126. );
  127. }
  128. public function testRuleSetsDocumentationDirectoryHasNoExtraFiles(): void
  129. {
  130. $generator = new DocumentationLocator();
  131. self::assertCount(
  132. \count(RuleSets::getSetDefinitions()) + 1,
  133. (new Finder())->files()->in($generator->getRuleSetsDocumentationDirectoryPath())
  134. );
  135. }
  136. public function testInstallationDocHasCorrectMinimumVersion(): void
  137. {
  138. $composerJsonContent = file_get_contents(__DIR__.'/../../composer.json');
  139. $composerJson = json_decode($composerJsonContent, true, 512, JSON_THROW_ON_ERROR);
  140. $phpVersion = $composerJson['require']['php'];
  141. $minimumVersion = ltrim(substr($phpVersion, 0, strpos($phpVersion, ' ')), '^');
  142. $minimumVersionInformation = sprintf('PHP needs to be a minimum version of PHP %s.', $minimumVersion);
  143. $installationDocPath = realpath(__DIR__.'/../../doc/installation.rst');
  144. self::assertStringContainsString(
  145. $minimumVersionInformation,
  146. file_get_contents($installationDocPath),
  147. sprintf('Files %s needs to contain information "%s"', $installationDocPath, $minimumVersionInformation)
  148. );
  149. }
  150. public function testAllReportFormatsAreInUsageDoc(): void
  151. {
  152. $locator = new DocumentationLocator();
  153. $usage = $locator->getUsageFilePath();
  154. self::assertFileExists($usage);
  155. $usage = file_get_contents($usage);
  156. self::assertIsString($usage);
  157. $reporterFactory = new ReporterFactory();
  158. $reporterFactory->registerBuiltInReporters();
  159. $formats = array_filter(
  160. $reporterFactory->getFormats(),
  161. static fn (string $format): bool => 'txt' !== $format,
  162. );
  163. foreach ($formats as $format) {
  164. self::assertStringContainsString(sprintf('* ``%s``', $format), $usage);
  165. }
  166. $lastFormat = array_pop($formats);
  167. $expectedContent = 'Supported formats are ``txt`` (default one), ';
  168. foreach ($formats as $format) {
  169. $expectedContent .= '``'.$format.'``, ';
  170. }
  171. $expectedContent = substr($expectedContent, 0, -2);
  172. $expectedContent .= ' and ``'.$lastFormat.'``.';
  173. self::assertStringContainsString($expectedContent, $usage);
  174. }
  175. private static function assertFileEqualsString(string $expectedString, string $actualFilePath, string $message = ''): void
  176. {
  177. self::assertFileExists($actualFilePath, $message);
  178. self::assertSame($expectedString, file_get_contents($actualFilePath), $message);
  179. }
  180. /**
  181. * @return list<FixerInterface>
  182. */
  183. private static function getFixers(): array
  184. {
  185. $factory = new FixerFactory();
  186. $factory->registerBuiltInFixers();
  187. return $factory->getFixers();
  188. }
  189. }