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..d53889a88c
--- /dev/null
+++ b/tests/Core/Ruleset/ProcessRulesetTest.php
@@ -0,0 +1,263 @@
+
+ * @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.WithReplacement" => "$sniffDir\Deprecated\WithReplacementSniff",
+ "$std.Deprecated.WithReplacementContainingLinuxNewlines" => "$sniffDir\Deprecated\WithReplacementContainingLinuxNewlinesSniff",
+ "$std.Deprecated.WithReplacementContainingNewlines" => "$sniffDir\Deprecated\WithReplacementContainingNewlinesSniff",
+ "$std.Deprecated.WithoutReplacement" => "$sniffDir\Deprecated\WithoutReplacementSniff",
+ "$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',
+ ];
+
+ // 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 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