diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceCaseMismatch1Test.xml b/tests/Core/Ruleset/ExpandRulesetReferenceCaseMismatch1Test.xml new file mode 100644 index 0000000000..b42437419a --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceCaseMismatch1Test.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceCaseMismatch2Test.xml b/tests/Core/Ruleset/ExpandRulesetReferenceCaseMismatch2Test.xml new file mode 100644 index 0000000000..180b99ba23 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceCaseMismatch2Test.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceHomePathFailTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceHomePathFailTest.xml new file mode 100644 index 0000000000..803c848de6 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceHomePathFailTest.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceHomePathTest.php b/tests/Core/Ruleset/ExpandRulesetReferenceHomePathTest.php new file mode 100644 index 0000000000..a644af08c7 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceHomePathTest.php @@ -0,0 +1,120 @@ + + * @copyright 2025 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 PHP_CodeSniffer\Tests\Core\Ruleset\AbstractRulesetTestCase; + +/** + * Test home path handling in the Ruleset::expandRulesetReference() method. + * + * @covers \PHP_CodeSniffer\Ruleset::expandRulesetReference + */ +final class ExpandRulesetReferenceHomePathTest extends AbstractRulesetTestCase +{ + + /** + * Original value of the user's home path environment variable. + * + * @var string|false Path or false is the `HOME` environment variable is not available. + */ + private static $homepath = false; + + + /** + * Store the user's home path. + * + * @beforeClass + * + * @return void + */ + protected function storeHomePath() + { + $this->homepath = getenv('HOME'); + + }//end storeHomePath() + + + /** + * Restore the user's home path environment variable in case the test changed it or created it. + * + * @afterClass + * + * @return void + */ + protected function restoreHomePath() + { + if (is_string($this->homepath) === true) { + putenv('HOME='.$this->homepath); + } else { + // Remove the environment variable as it didn't exist before. + putenv('HOME'); + } + + }//end restoreHomePath() + + + /** + * Set the home path to an alternative location. + * + * @before + * + * @return void + */ + protected function setHomePath() + { + $fakeHomePath = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'FakeHomePath'; + putenv("HOME=$fakeHomePath"); + + }//end setHomePath() + + + /** + * Verify that a sniff reference with the magic "home path" placeholder gets expanded correctly + * and finds sniffs if the path exists underneath the "home path". + * + * @return void + */ + public function testHomePathRefGetsExpandedAndFindsSniff() + { + // Set up the ruleset. + $standard = __DIR__.'/ExpandRulesetReferenceHomePathTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $expected = ['MyStandard.Category.Valid' => 'FakeHomePath\\MyStandard\\Sniffs\\Category\\ValidSniff']; + + $this->assertSame($expected, $ruleset->sniffCodes); + + }//end testHomePathRefGetsExpandedAndFindsSniff() + + + /** + * Verify that a sniff reference with the magic "home path" placeholder gets expanded correctly + * and still fails to find sniffs if the path doesn't exists underneath the "home path". + * + * @return void + */ + public function testHomePathRefGetsExpandedAndThrowsExceptionWhenPathIsInvalid() + { + // Set up the ruleset. + $standard = __DIR__.'/ExpandRulesetReferenceHomePathFailTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + + $exceptionMessage = 'Referenced sniff "~/src/MyStandard/Sniffs/DoesntExist/" does not exist'; + $this->expectRuntimeExceptionMessage($exceptionMessage); + + new Ruleset($config); + + }//end testHomePathRefGetsExpandedAndThrowsExceptionWhenPathIsInvalid() + + +}//end class diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceHomePathTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceHomePathTest.xml new file mode 100644 index 0000000000..8d39a26935 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceHomePathTest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInternalIgnoreTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceInternalIgnoreTest.xml new file mode 100644 index 0000000000..9398e402b7 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInternalIgnoreTest.xml @@ -0,0 +1,15 @@ + + + + + + + 0 + + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInternalStandardTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceInternalStandardTest.xml new file mode 100644 index 0000000000..0493e253c6 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInternalStandardTest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInternalTest.php b/tests/Core/Ruleset/ExpandRulesetReferenceInternalTest.php new file mode 100644 index 0000000000..edac7490d0 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInternalTest.php @@ -0,0 +1,66 @@ + + * @copyright 2025 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 PHP_CodeSniffer\Tests\Core\Ruleset\AbstractRulesetTestCase; + +/** + * Test handling of "internal" references in the Ruleset::expandRulesetReference() method. + * + * @covers \PHP_CodeSniffer\Ruleset::expandRulesetReference + */ +final class ExpandRulesetReferenceInternalTest extends AbstractRulesetTestCase +{ + + + /** + * Verify that a ruleset reference starting with "Internal." (including the dot) doesn't cause any sniffs to be registered. + * + * @return void + */ + public function testInternalRefDoesNotGetExpanded() + { + // Set up the ruleset. + $standard = __DIR__.'/ExpandRulesetReferenceInternalIgnoreTest.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 testInternalRefDoesNotGetExpanded() + + + /** + * While definitely not recommended, including a standard named "Internal", _does_ allow for sniffs to be registered. + * + * Note: customizations (exclusions/property setting etc) for individual sniffs may not always be handled correctly, + * which is why naming a standard "Internal" is definitely not recommended. + * + * @return void + */ + public function testInternalStandardDoesGetExpanded() + { + // Set up the ruleset. + $standard = __DIR__.'/ExpandRulesetReferenceInternalStandardTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $expected = ['Internal.Valid.Valid' => 'Fixtures\\Internal\\Sniffs\\Valid\\ValidSniff']; + + $this->assertSame($expected, $ruleset->sniffCodes); + + }//end testInternalStandardDoesGetExpanded() + + +}//end class diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode1Test.xml b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode1Test.xml new file mode 100644 index 0000000000..f864f35136 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode1Test.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode2Test.xml b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode2Test.xml new file mode 100644 index 0000000000..49814db7b5 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode2Test.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode3Test.xml b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode3Test.xml new file mode 100644 index 0000000000..439fe9a672 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidErrorCode3Test.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceInvalidHomePathRefTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidHomePathRefTest.xml new file mode 100644 index 0000000000..801ead6dd7 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceInvalidHomePathRefTest.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceMissingFileTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceMissingFileTest.xml new file mode 100644 index 0000000000..c7d95e431f --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceMissingFileTest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceTest.php b/tests/Core/Ruleset/ExpandRulesetReferenceTest.php new file mode 100644 index 0000000000..adf1d3e32a --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceTest.php @@ -0,0 +1,133 @@ + + * @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 PHP_CodeSniffer\Tests\Core\Ruleset\AbstractRulesetTestCase; + +/** + * Test various aspects of the Ruleset::expandRulesetReference() method not covered by other tests. + * + * @covers \PHP_CodeSniffer\Ruleset::expandRulesetReference + */ +final class ExpandRulesetReferenceTest extends AbstractRulesetTestCase +{ + + + /** + * Test handling of path references relative to the originally included ruleset. + * + * @return void + */ + public function testRulesetRelativePathReferences() + { + // Set up the ruleset. + $standard = __DIR__.'/ExpandRulesetReferenceTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $expected = [ + 'ExternalA.CheckSomething.Valid' => 'Fixtures\\ExternalA\\Sniffs\\CheckSomething\\ValidSniff', + 'TestStandard.ValidSniffs.RegisterEmptyArray' => 'Fixtures\\TestStandard\\Sniffs\\ValidSniffs\\RegisterEmptyArraySniff', + 'ExternalB.CheckMore.Valid' => 'Fixtures\\ExternalB\\Sniffs\\CheckMore\\ValidSniff', + ]; + + $this->assertSame($expected, $ruleset->sniffCodes); + + }//end testRulesetRelativePathReferences() + + + /** + * Test that an exception is thrown if a ruleset contains an unresolvable reference. + * + * @param string $standard The standard to use for the test. + * @param string $replacement The reference which will be used in the exception message. + * + * @dataProvider dataUnresolvableReferenceThrowsException + * + * @return void + */ + public function testUnresolvableReferenceThrowsException($standard, $replacement) + { + // Set up the ruleset. + $standard = __DIR__.'/'.$standard; + $config = new ConfigDouble(["--standard=$standard"]); + + $exceptionMessage = 'Referenced sniff "%s" does not exist'; + $this->expectRuntimeExceptionMessage(sprintf($exceptionMessage, $replacement)); + + new Ruleset($config); + + }//end testUnresolvableReferenceThrowsException() + + + /** + * Data provider. + * + * @see testUnresolvableReferenceThrowsException() + * + * @return array> + */ + public static function dataUnresolvableReferenceThrowsException() + { + $data = [ + 'Referencing a non-existent XML file' => [ + 'standard' => 'ExpandRulesetReferenceMissingFileTest.xml', + 'replacement' => './MissingFile.xml', + ], + 'Referencing an invalid directory starting with "~"' => [ + 'standard' => 'ExpandRulesetReferenceInvalidHomePathRefTest.xml', + 'replacement' => '~/src/Standards/Squiz/Sniffs/Files/', + ], + 'Referencing an unknown standard' => [ + 'standard' => 'ExpandRulesetReferenceUnknownStandardTest.xml', + 'replacement' => 'UnknownStandard', + ], + 'Referencing a non-existent category in a known standard' => [ + 'standard' => 'ExpandRulesetReferenceUnknownCategoryTest.xml', + 'replacement' => 'TestStandard.UnknownCategory', + ], + 'Referencing a non-existent sniff in a known standard' => [ + 'standard' => 'ExpandRulesetReferenceUnknownSniffTest.xml', + 'replacement' => 'TestStandard.InvalidSniffs.UnknownRule', + ], + 'Referencing an invalid error code - no standard name' => [ + 'standard' => 'ExpandRulesetReferenceInvalidErrorCode1Test.xml', + 'replacement' => '.Invalid.Undetermined.Found', + ], + 'Referencing an invalid error code - no category name' => [ + 'standard' => 'ExpandRulesetReferenceInvalidErrorCode2Test.xml', + 'replacement' => 'Standard..Undetermined.Found', + ], + 'Referencing an invalid error code - no sniff name' => [ + 'standard' => 'ExpandRulesetReferenceInvalidErrorCode3Test.xml', + 'replacement' => 'Standard.Invalid..Found', + ], + ]; + + // Add tests which are only relevant for case-sensitive OSes. + if (stripos(PHP_OS, 'WIN') === false) { + $data['Referencing an existing sniff, but there is a case mismatch (OS-dependent) [1]'] = [ + 'standard' => 'ExpandRulesetReferenceCaseMismatch1Test.xml', + 'replacement' => 'psr12.functions.nullabletypedeclaration', + ]; + $data['Referencing an existing sniff, but there is a case mismatch (OS-dependent) [2]'] = [ + 'standard' => 'ExpandRulesetReferenceCaseMismatch2Test.xml', + 'replacement' => 'PSR12.Functions.ReturntypeDeclaration', + ]; + } + + return $data; + + }//end dataUnresolvableReferenceThrowsException() + + +}//end class diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceTest.xml new file mode 100644 index 0000000000..aaa23759ee --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceTest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceUnknownCategoryTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceUnknownCategoryTest.xml new file mode 100644 index 0000000000..26f8c8491b --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceUnknownCategoryTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceUnknownSniffTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceUnknownSniffTest.xml new file mode 100644 index 0000000000..8b9b3ad197 --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceUnknownSniffTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/tests/Core/Ruleset/ExpandRulesetReferenceUnknownStandardTest.xml b/tests/Core/Ruleset/ExpandRulesetReferenceUnknownStandardTest.xml new file mode 100644 index 0000000000..8563f860dc --- /dev/null +++ b/tests/Core/Ruleset/ExpandRulesetReferenceUnknownStandardTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/tests/Core/Ruleset/Fixtures/ExternalA/Sniffs/CheckSomething/ValidSniff.php b/tests/Core/Ruleset/Fixtures/ExternalA/Sniffs/CheckSomething/ValidSniff.php new file mode 100644 index 0000000000..51061e3b5e --- /dev/null +++ b/tests/Core/Ruleset/Fixtures/ExternalA/Sniffs/CheckSomething/ValidSniff.php @@ -0,0 +1,25 @@ + + + + diff --git a/tests/Core/Ruleset/Fixtures/ExternalB/Sniffs/CheckMore/ValidSniff.php b/tests/Core/Ruleset/Fixtures/ExternalB/Sniffs/CheckMore/ValidSniff.php new file mode 100644 index 0000000000..1584f70504 --- /dev/null +++ b/tests/Core/Ruleset/Fixtures/ExternalB/Sniffs/CheckMore/ValidSniff.php @@ -0,0 +1,25 @@ + + + + diff --git a/tests/Core/Ruleset/Fixtures/FakeHomePath/src/MyStandard/Sniffs/Category/ValidSniff.php b/tests/Core/Ruleset/Fixtures/FakeHomePath/src/MyStandard/Sniffs/Category/ValidSniff.php new file mode 100644 index 0000000000..9346df9eb1 --- /dev/null +++ b/tests/Core/Ruleset/Fixtures/FakeHomePath/src/MyStandard/Sniffs/Category/ValidSniff.php @@ -0,0 +1,25 @@ + + + + diff --git a/tests/Core/Ruleset/Fixtures/Internal/Sniffs/Valid/ValidSniff.php b/tests/Core/Ruleset/Fixtures/Internal/Sniffs/Valid/ValidSniff.php new file mode 100644 index 0000000000..e7a04f495a --- /dev/null +++ b/tests/Core/Ruleset/Fixtures/Internal/Sniffs/Valid/ValidSniff.php @@ -0,0 +1,25 @@ + + + + + +