Skip to content

Commit ff2765d

Browse files
authored
Merge pull request #275 from PHPCSStandards/feature/tests-stablelize-config-use
Tests: stabelize tests using the `Config` class
2 parents 074ad2a + 16ad55b commit ff2765d

11 files changed

+239
-95
lines changed

tests/ConfigDouble.php

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<?php
2+
/**
3+
* Config class for use in the tests.
4+
*
5+
* The Config class contains a number of static properties.
6+
* As the value of these static properties will be retained between instantiations of the class,
7+
* config values set in one test can influence the results for another test, which makes tests unstable.
8+
*
9+
* This class is a "double" of the Config class which prevents this from happening.
10+
* In _most_ cases, tests should be using this class instead of the "normal" Config,
11+
* with the exception of select tests for the Config class itself.
12+
*
13+
* @author Juliette Reinders Folmer <[email protected]>
14+
* @copyright 2024 Juliette Reinders Folmer. All rights reserved.
15+
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
16+
*/
17+
18+
namespace PHP_CodeSniffer\Tests;
19+
20+
use PHP_CodeSniffer\Config;
21+
use ReflectionProperty;
22+
23+
final class ConfigDouble extends Config
24+
{
25+
26+
/**
27+
* Whether or not the setting of a standard should be skipped.
28+
*
29+
* @var boolean
30+
*/
31+
private $skipSettingStandard = false;
32+
33+
34+
/**
35+
* Creates a clean Config object and populates it with command line values.
36+
*
37+
* @param array<string> $cliArgs An array of values gathered from CLI args.
38+
* @param bool $skipSettingStandard Whether to skip setting a standard to prevent
39+
* the Config class trying to auto-discover a ruleset file.
40+
* Should only be set to `true` for tests which actually test
41+
* the ruleset auto-discovery.
42+
* Note: there is no need to set this to `true` when a standard
43+
* is being passed via the `$cliArgs`. Those settings will always
44+
* respected.
45+
* Defaults to `false`. Will result in the standard being set
46+
* to "PSR1" if not provided via `$cliArgs`.
47+
* @param bool $skipSettingReportWidth Whether to skip setting a report-width to prevent
48+
* the Config class trying to auto-discover the screen width.
49+
* Should only be set to `true` for tests which actually test
50+
* the screen width auto-discovery.
51+
* Note: there is no need to set this to `true` when a report-width
52+
* is being passed via the `$cliArgs`. Those settings will always
53+
* respected.
54+
* Defaults to `false`. Will result in the reportWidth being set
55+
* to "80" if not provided via `$cliArgs`.
56+
*
57+
* @return void
58+
*/
59+
public function __construct(array $cliArgs=[], $skipSettingStandard=false, $skipSettingReportWidth=false)
60+
{
61+
$this->skipSettingStandard = $skipSettingStandard;
62+
63+
$this->resetSelectProperties();
64+
$this->preventReadingCodeSnifferConfFile();
65+
66+
parent::__construct($cliArgs);
67+
68+
if ($skipSettingReportWidth !== true) {
69+
$this->preventAutoDiscoveryScreenWidth();
70+
}
71+
72+
}//end __construct()
73+
74+
75+
/**
76+
* Sets the command line values and optionally prevents a file system search for a custom ruleset.
77+
*
78+
* @param array<string> $args An array of command line arguments to set.
79+
*
80+
* @return void
81+
*/
82+
public function setCommandLineValues($args)
83+
{
84+
parent::setCommandLineValues($args);
85+
86+
if ($this->skipSettingStandard !== true) {
87+
$this->preventSearchingForRuleset();
88+
}
89+
90+
}//end setCommandLineValues()
91+
92+
93+
/**
94+
* Reset a few properties on the Config class to their default values.
95+
*
96+
* @return void
97+
*/
98+
private function resetSelectProperties()
99+
{
100+
$this->setStaticConfigProperty('overriddenDefaults', []);
101+
$this->setStaticConfigProperty('executablePaths', []);
102+
103+
}//end resetSelectProperties()
104+
105+
106+
/**
107+
* Prevent the values in a potentially available user-specific `CodeSniffer.conf` file
108+
* from influencing the tests.
109+
*
110+
* This also prevents some file system calls which can influence the test runtime.
111+
*
112+
* @return void
113+
*/
114+
private function preventReadingCodeSnifferConfFile()
115+
{
116+
$this->setStaticConfigProperty('configData', []);
117+
$this->setStaticConfigProperty('configDataFile', '');
118+
119+
}//end preventReadingCodeSnifferConfFile()
120+
121+
122+
/**
123+
* Prevent searching for a custom ruleset by setting a standard, but only if the test
124+
* being run doesn't set a standard itself.
125+
*
126+
* This also prevents some file system calls which can influence the test runtime.
127+
*
128+
* The standard being set is the smallest one available so the ruleset initialization
129+
* will be the fastest possible.
130+
*
131+
* @return void
132+
*/
133+
private function preventSearchingForRuleset()
134+
{
135+
$overriddenDefaults = $this->getStaticConfigProperty('overriddenDefaults');
136+
if (isset($overriddenDefaults['standards']) === false) {
137+
$this->standards = ['PSR1'];
138+
$overriddenDefaults['standards'] = true;
139+
}
140+
141+
self::setStaticConfigProperty('overriddenDefaults', $overriddenDefaults);
142+
143+
}//end preventSearchingForRuleset()
144+
145+
146+
/**
147+
* Prevent a call to stty to figure out the screen width, but only if the test being run
148+
* doesn't set a report width itself.
149+
*
150+
* @return void
151+
*/
152+
private function preventAutoDiscoveryScreenWidth()
153+
{
154+
$settings = $this->getSettings();
155+
if ($settings['reportWidth'] === 'auto') {
156+
$this->reportWidth = self::DEFAULT_REPORT_WIDTH;
157+
}
158+
159+
}//end preventAutoDiscoveryScreenWidth()
160+
161+
162+
/**
163+
* Helper function to retrieve the value of a private static property on the Config class.
164+
*
165+
* @param string $name The name of the property to retrieve.
166+
*
167+
* @return mixed
168+
*/
169+
private function getStaticConfigProperty($name)
170+
{
171+
$property = new ReflectionProperty('PHP_CodeSniffer\Config', $name);
172+
$property->setAccessible(true);
173+
return $property->getValue();
174+
175+
}//end getStaticConfigProperty()
176+
177+
178+
/**
179+
* Helper function to set the value of a private static property on the Config class.
180+
*
181+
* @param string $name The name of the property to set.
182+
* @param mixed $value The value to set the property to.
183+
*
184+
* @return void
185+
*/
186+
private function setStaticConfigProperty($name, $value)
187+
{
188+
$property = new ReflectionProperty('PHP_CodeSniffer\Config', $name);
189+
$property->setAccessible(true);
190+
$property->setValue(null, $value);
191+
$property->setAccessible(false);
192+
193+
}//end setStaticConfigProperty()
194+
195+
196+
}//end class

tests/Core/AbstractMethodUnitTest.php

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99

1010
namespace PHP_CodeSniffer\Tests\Core;
1111

12-
use PHP_CodeSniffer\Config;
1312
use PHP_CodeSniffer\Ruleset;
1413
use PHP_CodeSniffer\Files\DummyFile;
14+
use PHP_CodeSniffer\Tests\ConfigDouble;
1515
use PHPUnit\Framework\TestCase;
1616
use ReflectionProperty;
1717

@@ -57,22 +57,7 @@ abstract class AbstractMethodUnitTest extends TestCase
5757
*/
5858
public static function initializeFile()
5959
{
60-
/*
61-
* Set the static properties in the Config class to specific values for performance
62-
* and to clear out values from other tests.
63-
*/
64-
65-
self::setStaticConfigProperty('executablePaths', []);
66-
67-
// Set to a usable value to circumvent Config trying to find a phpcs.xml config file.
68-
self::setStaticConfigProperty('overriddenDefaults', ['standards' => ['PSR1']]);
69-
70-
// Set to values which prevent the test-runner user's `CodeSniffer.conf` file
71-
// from being read and influencing the tests. Also prevent an `exec()` call to stty.
72-
self::setStaticConfigProperty('configData', ['report_width' => 80]);
73-
self::setStaticConfigProperty('configDataFile', '');
74-
75-
$config = new Config();
60+
$config = new ConfigDouble();
7661
// Also set a tab-width to enable testing tab-replaced vs `orig_content`.
7762
$config->tabWidth = static::$tabWidth;
7863

@@ -93,44 +78,6 @@ public static function initializeFile()
9378
}//end initializeFile()
9479

9580

96-
/**
97-
* Clean up after finished test.
98-
*
99-
* @afterClass
100-
*
101-
* @return void
102-
*/
103-
public static function resetFile()
104-
{
105-
self::$phpcsFile = null;
106-
107-
// Reset the static properties in the Config class to their defaults to prevent tests influencing each other.
108-
self::setStaticConfigProperty('overriddenDefaults', []);
109-
self::setStaticConfigProperty('executablePaths', []);
110-
self::setStaticConfigProperty('configData', null);
111-
self::setStaticConfigProperty('configDataFile', null);
112-
113-
}//end resetFile()
114-
115-
116-
/**
117-
* Helper function to set the value of a private static property on the Config class.
118-
*
119-
* @param string $name The name of the property to set.
120-
* @param mixed $value The value to set the property to.
121-
*
122-
* @return void
123-
*/
124-
public static function setStaticConfigProperty($name, $value)
125-
{
126-
$property = new ReflectionProperty('PHP_CodeSniffer\Config', $name);
127-
$property->setAccessible(true);
128-
$property->setValue(null, $value);
129-
$property->setAccessible(false);
130-
131-
}//end setStaticConfigProperty()
132-
133-
13481
/**
13582
* Get the token pointer for a target token based on a specific comment found on the line before.
13683
*

0 commit comments

Comments
 (0)