DocumentationTest.php 8.0 KB

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