Missing dependancies
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Nexus CS Config.
|
||||
*
|
||||
* (c) 2020 John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nexus\CsConfig\Test;
|
||||
|
||||
use Nexus\CsConfig\Fixer\AbstractCustomFixer;
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionInterface;
|
||||
use PhpCsFixer\FixerDefinition\CodeSampleInterface;
|
||||
use PhpCsFixer\FixerDefinition\FileSpecificCodeSampleInterface;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSampleInterface;
|
||||
use PhpCsFixer\FixerNameValidator;
|
||||
use PhpCsFixer\Linter\CachingLinter;
|
||||
use PhpCsFixer\Linter\LinterInterface;
|
||||
use PhpCsFixer\Linter\ProcessLinter;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Used for testing the fixers.
|
||||
*
|
||||
* Most of the tests here are directly from `PhpCsFixer\Tests\Test\AbstractFixerTestCase`
|
||||
* with some modifications and additions, since the test case is not shipped to production.
|
||||
*
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
* @author John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
||||
*/
|
||||
abstract class AbstractCustomFixerTestCase extends TestCase
|
||||
{
|
||||
protected AbstractCustomFixer $fixer;
|
||||
protected LinterInterface $linter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->fixer = $this->createFixer();
|
||||
$this->linter = $this->getLinter();
|
||||
}
|
||||
|
||||
private static function assertTokens(Tokens $expectedTokens, Tokens $inputTokens): void
|
||||
{
|
||||
self::assertSame($expectedTokens->count(), $inputTokens->count(), 'Both Tokens collections should have the same size.');
|
||||
|
||||
/** @var Token $expectedToken */
|
||||
foreach ($expectedTokens as $index => $expectedToken) {
|
||||
/** @var Token $inputToken */
|
||||
$inputToken = $inputTokens[$index];
|
||||
|
||||
self::assertTrue(
|
||||
$expectedToken->equals($inputToken),
|
||||
sprintf("Token at index %d must be:\n%s,\ngot:\n%s.", $index, $expectedToken->toJson(), $inputToken->toJson()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertValidDescription(string $fixerName, string $descriptionType, string $description): void
|
||||
{
|
||||
self::assertMatchesRegularExpression('/^[A-Z`][^"]+\.$/', $description, sprintf('[%s] The %s must start with capital letter or a ` and end with dot.', $fixerName, $descriptionType));
|
||||
self::assertStringNotContainsString('phpdocs', $description, sprintf('[%s] `PHPDoc` must not be in the plural in %s.', $fixerName, $descriptionType));
|
||||
self::assertCorrectCasing($description, 'PHPDoc', sprintf('[%s] `PHPDoc` must be in correct casing in %s.', $fixerName, $descriptionType));
|
||||
self::assertCorrectCasing($description, 'PHPUnit', sprintf('[%s] `PHPUnit` must be in correct casing in %s.', $fixerName, $descriptionType));
|
||||
self::assertFalse(strpos($descriptionType, '``'), sprintf('[%s] The %s must not contain sequential backticks.', $fixerName, $descriptionType));
|
||||
}
|
||||
|
||||
private static function assertCorrectCasing(string $needle, string $haystack, string $message): void
|
||||
{
|
||||
self::assertSame(substr_count(strtolower($haystack), strtolower($needle)), substr_count($haystack, $needle), $message);
|
||||
}
|
||||
|
||||
final public function testIsRisky(): void
|
||||
{
|
||||
$riskyDescription = $this->fixer->getDefinition()->getRiskyDescription();
|
||||
|
||||
if ($this->fixer->isRisky()) {
|
||||
self::assertIsString($riskyDescription);
|
||||
self::assertValidDescription($this->fixer->getName(), 'risky description', (string) $riskyDescription);
|
||||
} else {
|
||||
self::assertNull($riskyDescription, sprintf('[%s] Fixer is not risky so no description of it is expected.', $this->fixer->getName()));
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionMethod($this->fixer, 'isRisky');
|
||||
|
||||
self::assertSame(
|
||||
! $this->fixer->isRisky(),
|
||||
$reflection->getDeclaringClass()->getName() === AbstractFixer::class,
|
||||
sprintf(
|
||||
'[%s] Fixer is %s so the method "AbstractFixer::isRisky()" must be %s.',
|
||||
$this->fixer->getName(),
|
||||
$this->fixer->isRisky() ? 'risky' : 'not risky',
|
||||
$this->fixer->isRisky() ? 'overridden' : 'used',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final public function testNameIsValid(): void
|
||||
{
|
||||
$nameValidator = new FixerNameValidator();
|
||||
$customFixerName = $this->fixer->getName();
|
||||
|
||||
self::assertTrue(
|
||||
$nameValidator->isValid($customFixerName, true),
|
||||
sprintf('Fixer name "%s" is not valid.', $customFixerName),
|
||||
);
|
||||
}
|
||||
|
||||
final public function testFixerIsFinal(): void
|
||||
{
|
||||
self::assertTrue(
|
||||
(new \ReflectionClass($this->fixer))->isFinal(),
|
||||
sprintf('Fixer "%s" must be declared "final".', $this->fixer->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
final public function testDeprecatedFixersHaveCorrectSummary(): void
|
||||
{
|
||||
self::assertStringNotContainsString(
|
||||
'DEPRECATED',
|
||||
$this->fixer->getDefinition()->getSummary(),
|
||||
'Fixer cannot contain word "DEPRECATED" in summary',
|
||||
);
|
||||
|
||||
$comment = (new \ReflectionClass($this->fixer))->getDocComment();
|
||||
self::assertIsString($comment, sprintf('[%s] Fixer is missing a class-level PHPDoc.', $this->fixer->getName()));
|
||||
$comment = (string) $comment;
|
||||
|
||||
if ($this->fixer instanceof DeprecatedFixerInterface) {
|
||||
self::assertStringContainsString('@deprecated', $comment);
|
||||
} else {
|
||||
self::assertStringNotContainsString('@deprecated', $comment);
|
||||
}
|
||||
}
|
||||
|
||||
final public function testFixerConfigurationDefinitions(): void
|
||||
{
|
||||
if (! $this->fixer instanceof ConfigurableFixerInterface) {
|
||||
$this->addToAssertionCount(1); // not applied to the fixer without configuration
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$configurationDefinition = $this->fixer->getConfigurationDefinition();
|
||||
|
||||
self::assertInstanceOf(FixerConfigurationResolverInterface::class, $configurationDefinition);
|
||||
|
||||
foreach ($configurationDefinition->getOptions() as $option) {
|
||||
self::assertInstanceOf(FixerOptionInterface::class, $option);
|
||||
self::assertNotEmpty($option->getDescription());
|
||||
|
||||
self::assertTrue(
|
||||
$option->hasDefault(),
|
||||
sprintf(
|
||||
'Option `%s` of fixer `%s` should have a default value.',
|
||||
$option->getName(),
|
||||
$this->fixer->getName(),
|
||||
),
|
||||
);
|
||||
|
||||
self::assertStringNotContainsString(
|
||||
'DEPRECATED',
|
||||
$option->getDescription(),
|
||||
'Option description cannot contain word "DEPRECATED"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final public function testFixerDefinitions(): void
|
||||
{
|
||||
$fixerName = $this->fixer->getName();
|
||||
$definition = $this->fixer->getDefinition();
|
||||
$fixerIsConfigurable = $this->fixer instanceof ConfigurableFixerInterface;
|
||||
|
||||
self::assertValidDescription($fixerName, 'summary', $definition->getSummary());
|
||||
|
||||
$samples = $definition->getCodeSamples();
|
||||
self::assertNotEmpty($samples, sprintf('[%s] Code samples are required.', $fixerName));
|
||||
|
||||
$configSamplesProvided = [];
|
||||
$dummyFileInfo = new \SplFileInfo(__FILE__);
|
||||
|
||||
foreach ($samples as $counter => $sample) {
|
||||
self::assertIsInt($counter);
|
||||
|
||||
++$counter;
|
||||
self::assertInstanceOf(CodeSampleInterface::class, $sample, sprintf('[%s] Sample #%d must be an instance of "%s".', $fixerName, $counter, CodeSampleInterface::class));
|
||||
|
||||
$code = $sample->getCode();
|
||||
self::assertNotEmpty($code, sprintf('[%s] Code provided by sample #%d must not be empty.', $fixerName, $counter));
|
||||
self::assertSame("\n", substr($code, -1), sprintf('[%s] Sample #%d must end with linebreak', $fixerName, $counter));
|
||||
|
||||
$config = $sample->getConfiguration();
|
||||
|
||||
if (null !== $config) {
|
||||
self::assertTrue($fixerIsConfigurable, sprintf('[%s] Sample #%d has configuration, but the fixer is not configurable.', $fixerName, $counter));
|
||||
self::assertIsArray($config, sprintf('[%s] Sample #%d configuration must be an array or null.', $fixerName, $counter));
|
||||
|
||||
$configSamplesProvided[$counter] = $config;
|
||||
} elseif ($fixerIsConfigurable) {
|
||||
if (! $sample instanceof VersionSpecificCodeSampleInterface) {
|
||||
self::assertArrayNotHasKey('default', $configSamplesProvided, sprintf('[%s] Multiple non-versioned samples with default configuration.', $fixerName));
|
||||
}
|
||||
|
||||
$configSamplesProvided['default'] = true;
|
||||
}
|
||||
|
||||
if ($sample instanceof VersionSpecificCodeSampleInterface && ! $sample->isSuitableFor(\PHP_VERSION_ID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($fixerIsConfigurable) {
|
||||
// always re-configure as the fixer might have been configured with diff. configuration from previous sample
|
||||
$this->fixer->configure(null === $config ? [] : $config);
|
||||
}
|
||||
|
||||
Tokens::clearCache();
|
||||
$tokens = Tokens::fromCode($code);
|
||||
|
||||
$this->fixer->fix(
|
||||
$sample instanceof FileSpecificCodeSampleInterface ? $sample->getSplFileInfo() : $dummyFileInfo,
|
||||
$tokens,
|
||||
);
|
||||
|
||||
self::assertTrue($tokens->isChanged(), sprintf('[%s] Sample #%d is not changed during fixing.', $fixerName, $counter));
|
||||
|
||||
$duplicatedCodeSample = array_search(
|
||||
$sample,
|
||||
\array_slice($samples, 0, $counter - 1),
|
||||
false,
|
||||
);
|
||||
|
||||
self::assertFalse(
|
||||
$duplicatedCodeSample,
|
||||
sprintf('[%s] Sample #%d duplicates #%d.', $fixerName, $counter, ++$duplicatedCodeSample),
|
||||
);
|
||||
}
|
||||
|
||||
if ($fixerIsConfigurable) {
|
||||
if (isset($configSamplesProvided['default'])) {
|
||||
reset($configSamplesProvided);
|
||||
self::assertSame('default', key($configSamplesProvided), sprintf('[%s] First sample must be for the default configuration.', $fixerName));
|
||||
}
|
||||
|
||||
if (\count($configSamplesProvided) < 2) {
|
||||
self::fail(sprintf('[%s] Configurable fixer only provides a default configuration sample and none for its configuration options.', $fixerName));
|
||||
}
|
||||
|
||||
$options = $this->fixer->getConfigurationDefinition()->getOptions();
|
||||
|
||||
foreach ($options as $option) {
|
||||
self::assertMatchesRegularExpression('/^[a-z_]+[a-z]$/', $option->getName(), sprintf('[%s] Option %s is not snake_case.', $fixerName, $option->getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a fixer fixes a given string to match the expected result.
|
||||
*
|
||||
* It is used both if you want to test if something is fixed or if it is not touched by the fixer.
|
||||
*
|
||||
* It also makes sure that the expected output does not change when run through the fixer. That means that you
|
||||
* do not need two test cases like [$expected] and [$expected, $input] (where $expected is the same in both cases)
|
||||
* as the latter covers both of them.
|
||||
*
|
||||
* This method throws an exception if $expected and $input are equal to prevent test cases that accidentally do
|
||||
* not test anything.
|
||||
*
|
||||
* @param string $expected The expected fixer output
|
||||
* @param null|string $input The fixer input, or null if it should intentionally be equal to the output
|
||||
*/
|
||||
protected function doTest(string $expected, ?string $input = null): void
|
||||
{
|
||||
if ($expected === $input) {
|
||||
throw new \LogicException('Input parameter must not be equal to expected parameter.'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$file = new \SplFileInfo(__FILE__);
|
||||
|
||||
if (null !== $input) {
|
||||
self::assertNull($this->lintSource($input));
|
||||
|
||||
Tokens::clearCache();
|
||||
$tokens = Tokens::fromCode($input);
|
||||
|
||||
self::assertTrue($this->fixer->isCandidate($tokens), 'Fixer must be a candidate for input code.');
|
||||
self::assertFalse($tokens->isChanged(), 'Fixer must not touch Tokens on candidate check.');
|
||||
$this->fixer->fix($file, $tokens);
|
||||
|
||||
self::assertSame(
|
||||
$expected,
|
||||
$tokens->generateCode(),
|
||||
'Code build on input code must match expected code.',
|
||||
);
|
||||
self::assertTrue($tokens->isChanged(), 'Tokens collection built on input code must be marked as changed after fixing.');
|
||||
|
||||
$tokens->clearEmptyTokens();
|
||||
|
||||
/** @var Token[] $tokensArray */
|
||||
$tokensArray = $tokens->toArray();
|
||||
|
||||
self::assertSame(
|
||||
\count($tokens),
|
||||
\count(array_unique(array_map(static fn (Token $token): string => spl_object_hash($token), $tokensArray))),
|
||||
'Token items inside Tokens collection must be unique.',
|
||||
);
|
||||
|
||||
unset($tokensArray);
|
||||
Tokens::clearCache();
|
||||
$expectedTokens = Tokens::fromCode($expected);
|
||||
self::assertTokens($expectedTokens, $tokens);
|
||||
}
|
||||
|
||||
self::assertNull($this->lintSource($expected));
|
||||
|
||||
Tokens::clearCache();
|
||||
$tokens = Tokens::fromCode($expected);
|
||||
$this->fixer->fix($file, $tokens);
|
||||
|
||||
self::assertSame(
|
||||
$expected,
|
||||
$tokens->generateCode(),
|
||||
'Code build on expected code must not change.',
|
||||
);
|
||||
self::assertFalse($tokens->isChanged(), 'Tokens collection built on expected code must not be marked as changed after fixing.');
|
||||
}
|
||||
|
||||
protected function createFixer(): AbstractCustomFixer
|
||||
{
|
||||
/** @phpstan-var class-string<AbstractCustomFixer> $customFixer */
|
||||
$customFixer = Preg::replace('/^(Nexus\\\\CsConfig)\\\\Tests(\\\\.+)Test$/', '$1$2', static::class);
|
||||
|
||||
return new $customFixer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected function lintSource(string $source): ?string
|
||||
{
|
||||
try {
|
||||
$this->linter->lintSource($source)->check();
|
||||
|
||||
return null;
|
||||
} catch (\Throwable $e) {
|
||||
return sprintf('Linting "%s" failed with message: %s.', $source, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function getLinter(): LinterInterface
|
||||
{
|
||||
static $linter = null;
|
||||
|
||||
if (null === $linter) {
|
||||
$linter = new CachingLinter(new ProcessLinter());
|
||||
}
|
||||
|
||||
return $linter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Nexus CS Config.
|
||||
*
|
||||
* (c) 2020 John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nexus\CsConfig\Test;
|
||||
|
||||
use Nexus\CsConfig\Ruleset\RulesetInterface;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\FixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOptionInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Used for testing the rulesets.
|
||||
*/
|
||||
abstract class AbstractRulesetTestCase extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var array<string, FixerInterface>
|
||||
*/
|
||||
private static array $builtInFixers = [];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static array $configuredFixers = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, bool|string|string[]>|bool>
|
||||
*/
|
||||
private static array $enabledFixers = [];
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
$fixerProvider = FixerProvider::create(static::createRuleset());
|
||||
self::$builtInFixers = $fixerProvider->builtin();
|
||||
self::$configuredFixers = $fixerProvider->configured();
|
||||
self::$enabledFixers = $fixerProvider->enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
FixerProvider::reset();
|
||||
self::$builtInFixers = [];
|
||||
self::$configuredFixers = [];
|
||||
self::$enabledFixers = [];
|
||||
}
|
||||
|
||||
protected static function createRuleset(): RulesetInterface
|
||||
{
|
||||
/** @phpstan-var class-string<RulesetInterface> $className */
|
||||
$className = Preg::replace('/^(Nexus\\\\CsConfig)\\\\Tests(\\\\.+)Test$/', '$1$2', static::class);
|
||||
|
||||
return new $className();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// TESTS
|
||||
// =========================================================================
|
||||
|
||||
final public function testAllConfiguredFixersAreNotUsingPresets(): void
|
||||
{
|
||||
$fixersThatArePresets = array_filter(
|
||||
self::$enabledFixers,
|
||||
static fn (string $fixer): bool => substr($fixer, 0, 1) === '@',
|
||||
ARRAY_FILTER_USE_KEY,
|
||||
);
|
||||
|
||||
self::assertEmpty($fixersThatArePresets, sprintf(
|
||||
'[%s] Ruleset should not be using rule sets (presets) as fixers. Found: "%s".',
|
||||
static::createRuleset()->getName(),
|
||||
implode('", "', array_keys($fixersThatArePresets)),
|
||||
));
|
||||
}
|
||||
|
||||
final public function testAllBuiltInFixersNotDeprecatedAreConfiguredInThisRuleset(): void
|
||||
{
|
||||
$fixersNotConfigured = array_diff(array_keys(self::$builtInFixers), self::$configuredFixers);
|
||||
|
||||
sort($fixersNotConfigured);
|
||||
$c = \count($fixersNotConfigured);
|
||||
|
||||
self::assertEmpty($fixersNotConfigured, sprintf(
|
||||
'[%s] Non-deprecated built-in %s "%s" %s not configured in the ruleset.',
|
||||
static::createRuleset()->getName(),
|
||||
$c > 1 ? 'fixers' : 'fixer',
|
||||
implode('", "', $fixersNotConfigured),
|
||||
$c > 1 ? 'are' : 'is',
|
||||
));
|
||||
}
|
||||
|
||||
final public function testAllConfiguredFixersInThisRulesetAreBuiltInAndNotDeprecated(): void
|
||||
{
|
||||
$fixersNotBuiltIn = array_diff(self::$configuredFixers, array_keys(self::$builtInFixers));
|
||||
|
||||
sort($fixersNotBuiltIn);
|
||||
$c = \count($fixersNotBuiltIn);
|
||||
|
||||
self::assertEmpty($fixersNotBuiltIn, sprintf(
|
||||
'[%s] Ruleset used %s "%s" which %s unknown and/or deprecated in PhpCsFixer.',
|
||||
static::createRuleset()->getName(),
|
||||
$c > 1 ? 'fixers' : 'fixer',
|
||||
implode('", "', $fixersNotBuiltIn),
|
||||
$c > 1 ? 'are' : 'is',
|
||||
));
|
||||
}
|
||||
|
||||
final public function testAllConfiguredFixersInThisRulesetAreSortedByName(): void
|
||||
{
|
||||
$fixers = self::$configuredFixers;
|
||||
$sorted = $fixers;
|
||||
sort($sorted);
|
||||
|
||||
self::assertSame($sorted, $fixers, sprintf(
|
||||
'[%s] Fixers are not sorted by name.',
|
||||
static::createRuleset()->getName(),
|
||||
));
|
||||
}
|
||||
|
||||
final public function testHeaderCommentFixerIsDisabledByDefault(): void
|
||||
{
|
||||
self::assertArrayHasKey('header_comment', self::$enabledFixers);
|
||||
self::assertFalse(self::$enabledFixers['header_comment']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function provideConfigurableFixersCases(): iterable
|
||||
{
|
||||
$fixers = FixerProvider::create(static::createRuleset())->builtin();
|
||||
ksort($fixers);
|
||||
|
||||
foreach ($fixers as $name => $fixer) {
|
||||
if ($fixer instanceof ConfigurableFixerInterface) {
|
||||
$options = $fixer->getConfigurationDefinition()->getOptions();
|
||||
|
||||
$goodOptions = array_map(
|
||||
static fn (FixerOptionInterface $option): string => $option->getName(),
|
||||
array_filter(
|
||||
$options,
|
||||
static fn (FixerOptionInterface $option): bool => ! $option instanceof DeprecatedFixerOptionInterface,
|
||||
),
|
||||
);
|
||||
|
||||
$deprecatedOptions = array_map(
|
||||
static fn (FixerOptionInterface $option): string => $option->getName(),
|
||||
array_filter(
|
||||
$options,
|
||||
static fn (FixerOptionInterface $option): bool => $option instanceof DeprecatedFixerOptionInterface,
|
||||
),
|
||||
);
|
||||
|
||||
yield $name => [$name, $goodOptions, $deprecatedOptions];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideConfigurableFixersCases
|
||||
*/
|
||||
final public function testEnabledConfigurableFixerUsesAllAvailableOptionsNotDeprecated(string $name, array $goodOptions, array $deprecatedOptions): void
|
||||
{
|
||||
/** @var null|array<string, bool|string|string[]>|bool $ruleConfiguration */
|
||||
$ruleConfiguration = self::$enabledFixers[$name] ?? null;
|
||||
|
||||
if (null === $ruleConfiguration) {
|
||||
self::markTestSkipped(sprintf('`%s` is not yet defined in this ruleset.', $name)); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (false === $ruleConfiguration) {
|
||||
// fixer is turned off
|
||||
$this->addToAssertionCount(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ruleConfiguration = \is_array($ruleConfiguration) ? $ruleConfiguration : [];
|
||||
$ruleConfiguration = array_keys($ruleConfiguration);
|
||||
|
||||
$missingOptions = array_diff($goodOptions, $ruleConfiguration);
|
||||
$usedDeprecatedOptions = array_intersect($deprecatedOptions, $ruleConfiguration);
|
||||
$extraUsedOptions = array_diff($ruleConfiguration, $goodOptions);
|
||||
|
||||
self::assertEmpty($missingOptions, sprintf(
|
||||
'Enabled configurable fixer "%s" does not use its available array %s "%s". Missing %s: "%s".',
|
||||
$name,
|
||||
\count($goodOptions) > 1 ? 'options' : 'option',
|
||||
implode('", "', $goodOptions),
|
||||
\count($missingOptions) > 1 ? 'options' : 'option',
|
||||
implode('", "', $missingOptions),
|
||||
));
|
||||
self::assertEmpty($usedDeprecatedOptions, sprintf(
|
||||
'Enabled configurable fixer "%s" uses deprecated %s: "%s".',
|
||||
$name,
|
||||
\count($usedDeprecatedOptions) > 1 ? 'options' : 'option',
|
||||
implode('", "', $usedDeprecatedOptions),
|
||||
));
|
||||
self::assertEmpty($extraUsedOptions, sprintf(
|
||||
'%s "%s" for enabled configurable fixer "%s" %s not defined by PhpCsFixer.',
|
||||
\count($extraUsedOptions) > 1 ? 'Options' : 'Option',
|
||||
implode('", "', $extraUsedOptions),
|
||||
$name,
|
||||
\count($extraUsedOptions) > 1 ? 'are' : 'is',
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Nexus CS Config.
|
||||
*
|
||||
* (c) 2020 John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nexus\CsConfig\Test;
|
||||
|
||||
use Nexus\CsConfig\Ruleset\RulesetInterface;
|
||||
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
|
||||
use PhpCsFixer\Fixer\FixerInterface;
|
||||
use PhpCsFixer\FixerFactory;
|
||||
use PhpCsFixer\RuleSet\RuleSet;
|
||||
|
||||
final class FixerProvider
|
||||
{
|
||||
/**
|
||||
* Built-in fixers from php-cs-fixer.
|
||||
*
|
||||
* @var array<string, FixerInterface>
|
||||
*/
|
||||
private static array $builtIn = [];
|
||||
|
||||
/**
|
||||
* Configured fixers from a ruleset.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private array $configured = [];
|
||||
|
||||
/**
|
||||
* Enabled fixers from a ruleset.
|
||||
*
|
||||
* @var array<string, array<string, bool|string|string[]>|bool>
|
||||
*/
|
||||
private array $enabled = [];
|
||||
|
||||
/**
|
||||
* @param array<int, string> $configured
|
||||
* @param array<string, array<string, bool|string|string[]>|bool> $enabled
|
||||
*/
|
||||
private function __construct(array $configured, array $enabled)
|
||||
{
|
||||
$this->configured = $configured;
|
||||
$this->enabled = $enabled;
|
||||
}
|
||||
|
||||
public static function create(RulesetInterface $ruleset): self
|
||||
{
|
||||
if ([] === self::$builtIn) {
|
||||
$fixers = array_filter(
|
||||
(new FixerFactory())->registerBuiltInFixers()->getFixers(),
|
||||
static fn (FixerInterface $fixer): bool => ! $fixer instanceof DeprecatedFixerInterface,
|
||||
);
|
||||
|
||||
foreach ($fixers as $fixer) {
|
||||
// workaround for using `array_combine` with PHPStan on PHP < 80000
|
||||
self::$builtIn[$fixer->getName()] = $fixer;
|
||||
}
|
||||
}
|
||||
|
||||
$rules = $ruleset->getRules();
|
||||
|
||||
$configured = array_map(static function ($ruleConfiguration): bool {
|
||||
// force enable all rules
|
||||
return true;
|
||||
}, $rules);
|
||||
|
||||
return new self(array_keys((new RuleSet($configured))->getRules()), $rules);
|
||||
}
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$builtIn = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names and instances of built-in fixers.
|
||||
*
|
||||
* @return array<string, FixerInterface>
|
||||
*/
|
||||
public function builtin(): array
|
||||
{
|
||||
return self::$builtIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the configured fixers.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function configured(): array
|
||||
{
|
||||
return $this->configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enabled rules from a ruleset and
|
||||
* their configuration.
|
||||
*
|
||||
* @return array<string, array<string, bool|string|string[]>|bool>
|
||||
*/
|
||||
public function enabled(): array
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user