diff --git a/tests/Core/Ruleset/Fixtures/InvalidNoSniffsDir/Sniffs b/tests/Core/Ruleset/Fixtures/InvalidNoSniffsDir/Sniffs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/Core/Ruleset/Fixtures/InvalidNoSniffsDir/ruleset.xml b/tests/Core/Ruleset/Fixtures/InvalidNoSniffsDir/ruleset.xml new file mode 100644 index 0000000000..b85e7486e8 --- /dev/null +++ b/tests/Core/Ruleset/Fixtures/InvalidNoSniffsDir/ruleset.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml b/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml new file mode 100644 index 0000000000..6968808664 --- /dev/null +++ b/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Core/Ruleset/ProcessRulesetExcludeSniffGroupTest.xml b/tests/Core/Ruleset/ProcessRulesetExcludeSniffGroupTest.xml new file mode 100644 index 0000000000..a83347ce8d --- /dev/null +++ b/tests/Core/Ruleset/ProcessRulesetExcludeSniffGroupTest.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/Core/Ruleset/ProcessRulesetInvalidNoSniffsDirTest.xml b/tests/Core/Ruleset/ProcessRulesetInvalidNoSniffsDirTest.xml new file mode 100644 index 0000000000..daa07f2e2a --- /dev/null +++ b/tests/Core/Ruleset/ProcessRulesetInvalidNoSniffsDirTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tests/Core/Ruleset/ProcessRulesetMiscTest.xml b/tests/Core/Ruleset/ProcessRulesetMiscTest.xml new file mode 100644 index 0000000000..56bf689818 --- /dev/null +++ b/tests/Core/Ruleset/ProcessRulesetMiscTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Core/Ruleset/ProcessRulesetTest.php b/tests/Core/Ruleset/ProcessRulesetTest.php new file mode 100644 index 0000000000..45bdb2734b --- /dev/null +++ b/tests/Core/Ruleset/ProcessRulesetTest.php @@ -0,0 +1,258 @@ + + * @copyright 2024 PHPCSStandards and contributors + * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Ruleset; + +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Tests\ConfigDouble; +use PHPUnit\Framework\TestCase; + +/** + * Test various aspects of the Ruleset::processRuleset() method not covered via other tests. + * + * @covers \PHP_CodeSniffer\Ruleset::processRuleset + */ +final class ProcessRulesetTest extends TestCase +{ + + + /** + * Verify that a registered standard which doesn't have a "Sniffs" directory, but does have a file + * called "Sniffs" doesn't result in any errors being thrown. + * + * @return void + */ + public function testSniffsFileNotDirectory() + { + // Set up the ruleset. + $standard = __DIR__.'/ProcessRulesetInvalidNoSniffsDirTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $expected = ['Generic.PHP.BacktickOperator' => 'PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\BacktickOperatorSniff']; + + $this->assertSame($expected, $ruleset->sniffCodes); + + }//end testSniffsFileNotDirectory() + + + /** + * Verify that all sniffs in a registered standard included in a ruleset automatically get added. + * + * @return void + */ + public function testAutoExpandSniffsDirectory() + { + // Set up the ruleset. + $standard = __DIR__.'/ProcessRulesetAutoExpandSniffsDirectoryTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $std = 'TestStandard'; + $sniffDir = 'Fixtures\TestStandard\Sniffs'; + $expected = [ + "$std.Deprecated.WithLongReplacement" => "$sniffDir\Deprecated\WithLongReplacementSniff", + "$std.Deprecated.WithoutReplacement" => "$sniffDir\Deprecated\WithoutReplacementSniff", + "$std.Deprecated.WithReplacementContainingLinuxNewlines" => "$sniffDir\Deprecated\WithReplacementContainingLinuxNewlinesSniff", + "$std.Deprecated.WithReplacementContainingNewlines" => "$sniffDir\Deprecated\WithReplacementContainingNewlinesSniff", + "$std.Deprecated.WithReplacement" => "$sniffDir\Deprecated\WithReplacementSniff", + "$std.DeprecatedInvalid.EmptyDeprecationVersion" => "$sniffDir\DeprecatedInvalid\EmptyDeprecationVersionSniff", + "$std.DeprecatedInvalid.EmptyRemovalVersion" => "$sniffDir\DeprecatedInvalid\EmptyRemovalVersionSniff", + "$std.DeprecatedInvalid.InvalidDeprecationMessage" => "$sniffDir\DeprecatedInvalid\InvalidDeprecationMessageSniff", + "$std.DeprecatedInvalid.InvalidDeprecationVersion" => "$sniffDir\DeprecatedInvalid\InvalidDeprecationVersionSniff", + "$std.DeprecatedInvalid.InvalidRemovalVersion" => "$sniffDir\DeprecatedInvalid\InvalidRemovalVersionSniff", + "$std.SetProperty.AllowedAsDeclared" => "$sniffDir\SetProperty\AllowedAsDeclaredSniff", + "$std.SetProperty.AllowedViaMagicMethod" => "$sniffDir\SetProperty\AllowedViaMagicMethodSniff", + "$std.SetProperty.AllowedViaStdClass" => "$sniffDir\SetProperty\AllowedViaStdClassSniff", + "$std.SetProperty.NotAllowedViaAttribute" => "$sniffDir\SetProperty\NotAllowedViaAttributeSniff", + "$std.SetProperty.PropertyTypeHandling" => "$sniffDir\SetProperty\PropertyTypeHandlingSniff", + ]; + + // Sort the value to make the tests stable as different OSes will read directories + // in a different order and the order is not relevant for these tests. Just the values. + $actual = $ruleset->sniffCodes; + ksort($actual); + + $this->assertSame($expected, $actual); + + }//end testAutoExpandSniffsDirectory() + + + /** + * Verify handling of exclusions of groups of sniffs after inclusion via an even larger "group". + * + * @return void + */ + public function testExcludeSniffGroup() + { + // Set up the ruleset. + $standard = __DIR__.'/ProcessRulesetExcludeSniffGroupTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $expected = [ + 'PSR1.Classes.ClassDeclaration' => 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Classes\ClassDeclarationSniff', + 'PSR1.Methods.CamelCapsMethodName' => 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Methods\CamelCapsMethodNameSniff', + ]; + + $this->assertSame($expected, $ruleset->sniffCodes); + + }//end testExcludeSniffGroup() + + + /* + * No test for without "name" as there is nothing we can assert to verify it's being ignored. + */ + + + /** + * Test that an `` directive without a "value" attribute will be set to the ini equivalent of `true`. + * + * @return void + */ + public function testIniWithoutValue() + { + $originalValue = ini_get('user_agent'); + + // Set up the ruleset. + $this->getMiscRuleset(); + + $actualValue = ini_get('user_agent'); + // Reset the ini to its original value before the assertion to ensure it's never left in an incorrect state. + if ($originalValue !== false) { + ini_set('user_agent', $originalValue); + } + + $this->assertSame('1', $actualValue); + + }//end testIniWithoutValue() + + + /** + * Verify that inclusion of a single error code: + * - Includes the sniff, but sets "severity" for the sniff to 0; + * - Sets "severity" for the specific error code included to 5.; + * + * @return void + */ + public function testIncludeSingleErrorCode() + { + // Set up the ruleset. + $ruleset = $this->getMiscRuleset(); + + $key = 'severity'; + + $sniffCode = 'Generic.PHP.RequireStrictTypes'; + $this->assertArrayHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode not registered"); + $this->assertTrue(is_array($ruleset->ruleset[$sniffCode]), "Sniff $sniffCode is not an array"); + $this->assertArrayHasKey($key, $ruleset->ruleset[$sniffCode], "Directive $key not registered for sniff $sniffCode"); + $this->assertSame(0, $ruleset->ruleset[$sniffCode][$key], "$key has unexpected value for sniff $sniffCode"); + + $sniffCode = 'Generic.PHP.RequireStrictTypes.MissingDeclaration'; + $this->assertArrayHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode not registered"); + $this->assertTrue(is_array($ruleset->ruleset[$sniffCode]), "Sniff $sniffCode is not an array"); + $this->assertArrayHasKey($key, $ruleset->ruleset[$sniffCode], "Directive $key not registered for sniff $sniffCode"); + $this->assertSame(5, $ruleset->ruleset[$sniffCode][$key], "$key has unexpected value for sniff $sniffCode"); + + }//end testIncludeSingleErrorCode() + + + /** + * Verify that if all error codes, save one, from a sniff were previously excluded, an include for an additional + * error code from that same sniff will be respected. + * + * @return void + */ + public function testErrorCodeIncludeAfterExclude() + { + // Set up the ruleset. + $ruleset = $this->getMiscRuleset(); + + $key = 'severity'; + + $sniffCode = 'PEAR.Files.IncludingFile'; + $this->assertArrayHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode not registered"); + $this->assertTrue(is_array($ruleset->ruleset[$sniffCode]), "Sniff $sniffCode is not an array"); + $this->assertArrayHasKey($key, $ruleset->ruleset[$sniffCode], "Directive $key not registered for sniff $sniffCode"); + $this->assertSame(0, $ruleset->ruleset[$sniffCode][$key], "$key has unexpected value for sniff $sniffCode"); + + $sniffCode = 'PEAR.Files.IncludingFile.BracketsNotRequired'; + $this->assertArrayHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode not registered"); + $this->assertTrue(is_array($ruleset->ruleset[$sniffCode]), "Sniff $sniffCode is not an array"); + $this->assertArrayHasKey($key, $ruleset->ruleset[$sniffCode], "Directive $key not registered for sniff $sniffCode"); + $this->assertSame(5, $ruleset->ruleset[$sniffCode][$key], "$key has unexpected value for sniff $sniffCode"); + + $sniffCode = 'PEAR.Files.IncludingFile.UseRequire'; + $this->assertArrayHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode not registered"); + $this->assertTrue(is_array($ruleset->ruleset[$sniffCode]), "Sniff $sniffCode is not an array"); + $this->assertArrayHasKey($key, $ruleset->ruleset[$sniffCode], "Directive $key not registered for sniff $sniffCode"); + $this->assertSame(5, $ruleset->ruleset[$sniffCode][$key], "$key has unexpected value for sniff $sniffCode"); + + }//end testErrorCodeIncludeAfterExclude() + + + /** + * Verify that a element without a "ref" is completely ignored. + * + * @return void + */ + public function testRuleWithoutRefIsIgnored() + { + // Set up the ruleset. + $ruleset = $this->getMiscRuleset(); + + $sniffCode = 'Generic.Metrics.CyclomaticComplexity'; + $this->assertArrayNotHasKey($sniffCode, $ruleset->sniffCodes, "Sniff $sniffCode registered"); + $this->assertArrayNotHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode adjusted"); + + }//end testRuleWithoutRefIsIgnored() + + + /** + * Verify that no "ruleset adjustments" are registered via an `` without a "name". + * + * @return void + */ + public function testRuleExcludeWithoutNameIsIgnored() + { + // Set up the ruleset. + $ruleset = $this->getMiscRuleset(); + + $sniffCode = 'Generic.PHP.BacktickOperator'; + $this->assertArrayHasKey($sniffCode, $ruleset->sniffCodes, "Sniff $sniffCode not registered"); + $this->assertArrayNotHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode adjusted"); + + $sniffCode = 'Generic.PHP.BacktickOperator.Found'; + $this->assertArrayNotHasKey($sniffCode, $ruleset->ruleset, "Sniff $sniffCode adjusted"); + + }//end testRuleExcludeWithoutNameIsIgnored() + + + /** + * Test Helper. + * + * @return \PHP_CodeSniffer\Sniffs\Sniff + */ + private function getMiscRuleset() + { + static $ruleset; + + if (isset($ruleset) === false) { + // Set up the ruleset. + $standard = __DIR__.'/ProcessRulesetMiscTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + } + + return $ruleset; + + }//end getMiscRuleset() + + +}//end class