* @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\Config;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Tests\ConfigDouble;
use PHPUnit\Framework\TestCase;
/**
* Test handling of `phpc(cs|cbf)-only` instructions at ruleset level.
*
* @covers \PHP_CodeSniffer\Ruleset::processRuleset
* @covers \PHP_CodeSniffer\Ruleset::shouldProcessElement
*/
final class ProcessRulesetShouldProcessElementTest extends TestCase
{
/**
* Cache to store the original ini values for ini settings being changed in these tests.
*
* @var array
*/
private static $originalIniValues = [
'bcmath.scale' => null,
'docref_root' => null,
'user_agent' => null,
];
/**
* The Config object.
*
* @var \PHP_CodeSniffer\Tests\ConfigDouble
*/
private static $config;
/**
* The Ruleset object.
*
* @var \PHP_CodeSniffer\Ruleset
*/
private static $ruleset;
/**
* Store the original ini values to allow for restoring them after the tests.
*
* @beforeClass
*
* @return void
*/
public static function saveOriginalIniValues()
{
foreach (self::$originalIniValues as $name => $null) {
$value = ini_get($name);
if ($value !== false) {
self::$originalIniValues[$name] = $value;
}
}
}//end saveOriginalIniValues()
/**
* Initialize the config and ruleset objects for this test only once (but do allow recording code coverage).
*
* @before
*
* @return void
*/
protected function initializeConfigAndRuleset()
{
if (isset(self::$ruleset) === false) {
// Set up the ruleset.
$standard = __DIR__.'/ProcessRulesetShouldProcessElementTest.xml';
self::$config = new ConfigDouble(["--standard=$standard"]);
self::$ruleset = new Ruleset(self::$config);
}
}//end initializeConfigAndRuleset()
/**
* Destroy the Config object and restore the ini values after the tests.
*
* @afterClass
*
* @return void
*/
public static function restoreOriginalValues()
{
// Explicitly trigger __destruct() on the ConfigDouble to reset the Config statics.
// The explicit method call prevents potential stray test-local references to the $config object
// preventing the destructor from running the clean up (which without stray references would be
// automagically triggered when this object is destroyed, but we can't definitively rely on that).
if (isset(self::$config) === true) {
self::$config->__destruct();
}
foreach (self::$originalIniValues as $name => $value) {
if ($value === null) {
continue;
}
ini_set($name, $value);
}
}//end restoreOriginalValues()
/**
* Verify that in CS mode, phpcs-only directives are respected and phpcbf-only
* directives are ignored.
*
* @return void
*/
public function testShouldProcessConfigCsonly()
{
if (PHP_CODESNIFFER_CBF === true) {
$this->markTestSkipped('This test needs CS mode to run');
}
$this->assertSame('true', Config::getConfigData('neither'), 'Non-selective config directive was not applied.');
$this->assertSame('true', Config::getConfigData('csOnly'), 'CS-only config directive was not applied.');
$this->assertSame(null, Config::getConfigData('cbfOnly'), 'CBF-only config directive was applied, while it shouldn\'t have been.');
}//end testShouldProcessConfigCsonly()
/**
* Verify that in CBF mode, phpcbf-only directives are respected and phpcs-only
* directives are ignored.
*
* @group CBF
*
* @return void
*/
public function testShouldProcessConfigCbfonly()
{
if (PHP_CODESNIFFER_CBF === false) {
$this->markTestSkipped('This test needs CBF mode to run');
}
$this->assertSame('true', Config::getConfigData('neither'), 'Non-selective config directive was not applied.');
$this->assertSame(null, Config::getConfigData('csOnly'), 'CS-only config directive was applied, while it shouldn\'t have been.');
$this->assertSame('true', Config::getConfigData('cbfOnly'), 'CBF-only config directive was not applied.');
}//end testShouldProcessConfigCbfonly()
/**
* Verify that in CS mode, phpcs-only directives are respected and phpcbf-only
* directives are ignored.
*
* @return void
*/
public function testShouldProcessArgCsonly()
{
if (PHP_CODESNIFFER_CBF === true) {
$this->markTestSkipped('This test needs CS mode to run');
}
$expectedExtensions = [
'php' => 'PHP',
'phpt' => 'PHP',
];
$expectedReports = ['full' => null];
$this->assertSame($expectedExtensions, self::$config->extensions, 'Non-selective arg directive was not applied.');
$this->assertTrue(self::$config->showProgress, 'Non-selective short arg directive was not applied [1].');
$this->assertTrue(self::$config->showSources, 'Non-selective short arg directive was not applied [2].');
$this->assertTrue(self::$config->colors, 'CS-only arg directive was not applied.');
$this->assertSame($expectedReports, self::$config->reports, 'CBF-only arg directive was applied, while it shouldn\'t have been.');
}//end testShouldProcessArgCsonly()
/**
* Verify that in CBF mode, phpcbf-only directives are respected and phpcs-only
* directives are ignored.
*
* @group CBF
*
* @return void
*/
public function testShouldProcessArgCbfonly()
{
if (PHP_CODESNIFFER_CBF === false) {
$this->markTestSkipped('This test needs CBF mode to run');
}
$expectedExtensions = [
'php' => 'PHP',
'phpt' => 'PHP',
];
$expectedReports = ['summary' => null];
$this->assertSame($expectedExtensions, self::$config->extensions, 'Non-selective arg directive was not applied.');
$this->assertTrue(self::$config->showProgress, 'Non-selective short arg directive was not applied [1].');
$this->assertTrue(self::$config->showSources, 'Non-selective short arg directive was not applied [2].');
$this->assertFalse(self::$config->colors, 'CS-only arg directive was applied, while it shouldn\'t have been.');
$this->assertSame($expectedReports, self::$config->reports, 'CBF-only arg directive was not applied.');
}//end testShouldProcessArgCbfonly()
/**
* Verify that in CS mode, phpcs-only directives are respected and phpcbf-only
* directives are ignored.
*
* @requires extension bcmath
*
* @return void
*/
public function testShouldProcessIniCsonly()
{
if (PHP_CODESNIFFER_CBF === true) {
$this->markTestSkipped('This test needs CS mode to run');
}
$this->assertSame('2', ini_get('bcmath.scale'), 'Non-selective ini directive was not applied.');
$this->assertSame('path/to/docs/', ini_get('docref_root'), 'CS-only ini directive was not applied.');
$this->assertSame('', ini_get('user_agent'), 'CBF-only ini directive was applied, while it shouldn\'t have been.');
}//end testShouldProcessIniCsonly()
/**
* Verify that in CBF mode, phpcbf-only directives are respected and phpcs-only
* directives are ignored.
*
* @group CBF
* @requires extension bcmath
*
* @return void
*/
public function testShouldProcessIniCbfonly()
{
if (PHP_CODESNIFFER_CBF === false) {
$this->markTestSkipped('This test needs CBF mode to run');
}
$this->assertSame('2', ini_get('bcmath.scale'), 'Non-selective ini directive was not applied.');
$this->assertSame('', ini_get('docref_root'), 'CS-only ini directive was applied, while it shouldn\'t have been..');
$this->assertSame('Never mind', ini_get('user_agent'), 'CBF-only ini directive was not applied.');
}//end testShouldProcessIniCbfonly()
/**
* Verify that in CS mode, phpcs-only directives are respected and phpcbf-only
* directives are ignored.
*
* @return void
*/
public function testShouldProcessExcludePatternCsonly()
{
if (PHP_CODESNIFFER_CBF === true) {
$this->markTestSkipped('This test needs CS mode to run');
}
$expected = [
'./tests/' => 'absolute',
'./vendor/' => 'absolute',
];
$this->assertSame($expected, self::$ruleset->ignorePatterns);
}//end testShouldProcessExcludePatternCsonly()
/**
* Verify that in CBF mode, phpcbf-only directives are respected and phpcs-only
* directives are ignored.
*
* @group CBF
*
* @return void
*/
public function testShouldProcessExcludePatternCbfonly()
{
if (PHP_CODESNIFFER_CBF === false) {
$this->markTestSkipped('This test needs CBF mode to run');
}
$expected = [
'./tests/' => 'absolute',
'./node-modules/' => 'absolute',
];
$this->assertSame($expected, self::$ruleset->ignorePatterns);
}//end testShouldProcessExcludePatternCbfonly()
/**
* Verify that in CS mode, phpcs-only directives are respected and phpcbf-only
* directives are ignored.
*
* @return void
*/
public function testShouldProcessRuleCsonly()
{
if (PHP_CODESNIFFER_CBF === true) {
$this->markTestSkipped('This test needs CS mode to run');
}
$this->assertArrayHasKey('PEAR.Formatting.MultiLineAssignment', self::$ruleset->sniffCodes);
$this->assertArrayHasKey('Generic.Arrays.ArrayIndent', self::$ruleset->sniffCodes);
$this->assertArrayNotHasKey('PSR2.Classes.ClassDeclaration', self::$ruleset->sniffCodes);
}//end testShouldProcessRuleCsonly()
/**
* Verify that in CBF mode, phpcbf-only directives are respected and phpcs-only
* directives are ignored.
*
* @group CBF
*
* @return void
*/
public function testShouldProcessRuleCbfonly()
{
if (PHP_CODESNIFFER_CBF === false) {
$this->markTestSkipped('This test needs CBF mode to run');
}
$this->assertArrayHasKey('PEAR.Formatting.MultiLineAssignment', self::$ruleset->sniffCodes);
$this->assertArrayNotHasKey('Generic.Arrays.ArrayIndent', self::$ruleset->sniffCodes);
$this->assertArrayHasKey('PSR2.Classes.ClassDeclaration', self::$ruleset->sniffCodes);
}//end testShouldProcessRuleCbfonly()
/**
* Verify that in CS mode, phpcs-only in directives are respected and phpcbf-only in
* directives are ignored.
*
* @return void
*/
public function testShouldProcessRuleExcludeCsonly()
{
if (PHP_CODESNIFFER_CBF === true) {
$this->markTestSkipped('This test needs CS mode to run');
}
$expected = [
'PEAR.Formatting.MultiLineAssignment.Indent' => [
'severity' => 0,
],
];
$this->assertSame($expected, self::$ruleset->ruleset);
}//end testShouldProcessRuleExcludeCsonly()
/**
* Verify that in CBF mode, phpcbf-only in directives are respected and phpcs-only in
* directives are ignored.
*
* @group CBF
*
* @return void
*/
public function testShouldProcessRuleExcludeCbfonly()
{
if (PHP_CODESNIFFER_CBF === false) {
$this->markTestSkipped('This test needs CBF mode to run');
}
$expected = [
'PEAR.Formatting.MultiLineAssignment.EqualSignLine' => [
'severity' => 0,
],
];
$this->assertSame($expected, self::$ruleset->ruleset);
}//end testShouldProcessRuleExcludeCbfonly()
}//end class