From 92e2535104f873c5bbb0f412bf2192c381c502f1 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 4 Feb 2022 05:45:59 +0100 Subject: [PATCH 1/3] TestCase: introduce new `configShowToPathsArray()` method ... as a helper to allow for comparing expected and actual registered paths from `--config-show`. --- tests/TestCase.php | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/TestCase.php b/tests/TestCase.php index 57b00c04..540e9112 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -579,4 +579,47 @@ protected function standardsPhraseToArray($phrase) return $standards; } + + /** + * Retrieve a list of the paths registered with PHPCS based on the output of `phpcs --config-show`. + * + * @param string $configShow The `stdout` output of `phpcs --config-show`. + * + * @return array Numerically indexed array of paths, stripped of absolute/relative path differences, + * natural sort applied. + * + * @throws RuntimeException When the passed argument is not a string. + */ + protected function configShowToPathsArray($configShow) + { + if (is_string($configShow) === false) { + throw new RuntimeException('The config-show input must be a string.'); + } + + if (preg_match('`installed_paths:\s+([^\n\r]+)\s+`', $configShow, $matches) !== 1) { + return array(); + } + + $pathsAsArray = explode(',', $matches[1]); + $pathsAsArray = array_map( + function ($value) { + $search = array( + '`^[^\r\n]+/vendor/`', + '`^\.\./\.\./`', + ); + + $replaced = preg_replace($search, '/', $value); + if ($replaced === null) { + return $value; + } + + return trim($replaced); // Trim off whitespace just to be on the safe side. + }, + $pathsAsArray + ); + + sort($pathsAsArray, \SORT_NATURAL); + + return $pathsAsArray; + } } From d7ea8aee268256bdf1d0a80cfe92c0113bdd50c6 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 5 Feb 2022 08:01:52 +0100 Subject: [PATCH 2/3] RegisterExternalStandardsTest: add test registering one standard with multiple rulesets * As the behaviour shouldn't be different between Composer local/global, the test is only run against one of these (local). * The test is, however, run against multiple PHPCS versions to ensure the path to the standard as registered allows for picking up all expected rulesets in all supported PHPCS versions. Includes adding a fake standard with multiple rulesets as a fixture for this test. --- .../RegisterExternalStandardsTest.php | 79 +++++++++++++++++++ tests/fixtures/README.md | 11 +++ .../My-Third-Standard/ruleset.xml | 5 ++ .../multistandard/MyFirstStandard/ruleset.xml | 5 ++ .../MySecondStandard/ruleset.xml | 5 ++ tests/fixtures/multistandard/composer.json | 16 ++++ 6 files changed, 121 insertions(+) create mode 100644 tests/fixtures/multistandard/My-Third-Standard/ruleset.xml create mode 100644 tests/fixtures/multistandard/MyFirstStandard/ruleset.xml create mode 100644 tests/fixtures/multistandard/MySecondStandard/ruleset.xml create mode 100644 tests/fixtures/multistandard/composer.json diff --git a/tests/IntegrationTest/RegisterExternalStandardsTest.php b/tests/IntegrationTest/RegisterExternalStandardsTest.php index e2841dd6..fd16252a 100644 --- a/tests/IntegrationTest/RegisterExternalStandardsTest.php +++ b/tests/IntegrationTest/RegisterExternalStandardsTest.php @@ -27,6 +27,14 @@ final class RegisterExternalStandardsTest extends TestCase ), ); + private $configOneStandardMultiRuleset = array( + 'name' => 'phpcs-composer-installer/register-external-stnds-multistnd', + 'require-dev' => array( + 'squizlabs/php_codesniffer' => null, + 'phpcs-composer-installer/multistandard' => '*', + ), + ); + /** * Set up test environment before each test. */ @@ -188,4 +196,75 @@ public function dataRegisterOneStandard() $versions = PHPCSVersions::get(2, true, true); return PHPCSVersions::toDataprovider($versions); } + + /** + * Test registering one external standard with multiple rulesets. + * + * @dataProvider dataRegisterOneStandardMultipleRulesets + * + * @param string $phpcsVersion PHPCS version to use in this test. + * This version is randomly selected from the PHPCS versions compatible + * with the PHP version used in the test. + * + * @return void + */ + public function testRegisterOneStandardWithMultipleRulesets($phpcsVersion) + { + $config = $this->configOneStandardMultiRuleset; + $config['require-dev']['squizlabs/php_codesniffer'] = $phpcsVersion; + + $this->writeComposerJsonFile($config, static::$tempLocalPath); + + // Install the dependencies and verify that the plugin has run. + $expectedStdOut = $this->willPluginOutputShow() ? 'PHP CodeSniffer Config installed_paths set to ' : null; + $this->assertExecute( + sprintf('composer install -v --no-ansi --working-dir=%s', escapeshellarg(static::$tempLocalPath)), + 0, // Expected exit code. + $expectedStdOut, // Expectation for stdout. + null, // No stderr expectation. + 'Failed to install dependencies.' + ); + + // Verify that only the one path is registered. + $result = $this->executeCliCommand('"vendor/bin/phpcs" --config-show', static::$tempLocalPath); + $this->assertSame(0, $result['exitcode'], 'Exitcode for "phpcs --config-show" did not match 0'); + + $expected = array( + '/phpcs-composer-installer/multistandard', + ); + + $this->assertSame( + $expected, + $this->configShowToPathsArray($result['stdout']), + 'Paths as updated by the plugin does not contain the expected path' + ); + + // Verify that PHPCS sees all three external standards. + $result = $this->executeCliCommand('"vendor/bin/phpcs" -i', static::$tempLocalPath); + $this->assertSame(0, $result['exitcode'], 'Exitcode for "phpcs -i" did not match 0'); + + $expected = PHPCSVersions::getStandards($phpcsVersion); + $expected[] = 'MyFirstStandard'; + $expected[] = 'MySecondStandard'; + $expected[] = 'My-Third-Standard'; + sort($expected, \SORT_NATURAL); + + $this->assertSame( + $expected, + $this->standardsPhraseToArray($result['stdout']), + 'Installed standards do not match the expected standards.' + ); + } + + /** + * Data provider. + * + * @return array + */ + public function dataRegisterOneStandardMultipleRulesets() + { + // Test against the highest and lowest supported PHPCS version of each major + PHPCS 4.x dev. + $versions = PHPCSVersions::getHighLowEachMajor(false, true); + return PHPCSVersions::toDataprovider($versions); + } } diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md index 6a470872..5d2843b3 100644 --- a/tests/fixtures/README.md +++ b/tests/fixtures/README.md @@ -40,3 +40,14 @@ An external PHPCS standard with the `ruleset.xml` file in a subdirectory ("norma | **Includes sniff(s):** | :heavy_checkmark: One sniff - `DummySubDir.Demo.Demo` - which is PHPCS cross-version compatible. | | **Requires the plugin:** | :x: | +### Package name: `phpcs-composer-installer/multistandard` + +**Description:** +An external PHPCS standard with multiple rulesets, each in a subdirectory ("normal" standard setup). + +| Characteristics | Notes | +|--------------------------|------------------------------------------------------------| +| **Standard(s):** | `MyFirstStandard`, `MySecondStandard`, `My-Third-Standard` | +| **Includes sniff(s):** | :x: | +| **Requires the plugin:** | :heavy_checkmark: | + diff --git a/tests/fixtures/multistandard/My-Third-Standard/ruleset.xml b/tests/fixtures/multistandard/My-Third-Standard/ruleset.xml new file mode 100644 index 00000000..ca6a9818 --- /dev/null +++ b/tests/fixtures/multistandard/My-Third-Standard/ruleset.xml @@ -0,0 +1,5 @@ + + + + Dummy PHPCS standard for testing. + diff --git a/tests/fixtures/multistandard/MyFirstStandard/ruleset.xml b/tests/fixtures/multistandard/MyFirstStandard/ruleset.xml new file mode 100644 index 00000000..424ac051 --- /dev/null +++ b/tests/fixtures/multistandard/MyFirstStandard/ruleset.xml @@ -0,0 +1,5 @@ + + + + Dummy PHPCS standard for testing. + diff --git a/tests/fixtures/multistandard/MySecondStandard/ruleset.xml b/tests/fixtures/multistandard/MySecondStandard/ruleset.xml new file mode 100644 index 00000000..462d4141 --- /dev/null +++ b/tests/fixtures/multistandard/MySecondStandard/ruleset.xml @@ -0,0 +1,5 @@ + + + + Dummy PHPCS standard for testing. + diff --git a/tests/fixtures/multistandard/composer.json b/tests/fixtures/multistandard/composer.json new file mode 100644 index 00000000..f56d04c3 --- /dev/null +++ b/tests/fixtures/multistandard/composer.json @@ -0,0 +1,16 @@ +{ + "name" : "phpcs-composer-installer/multistandard", + "description" : "Dummy PHPCS standard with multiple rulesets each in a subdirectory for use in the tests.", + "type" : "phpcodesniffer-standard", + "license" : "MIT", + "require" : { + "php" : ">=5.4", + "squizlabs/php_codesniffer" : "*", + "dealerdirect/phpcodesniffer-composer-installer" : "*" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} From 3b87865b0044de9c89f420be5ec1732922c0b5f4 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 5 Feb 2022 08:13:37 +0100 Subject: [PATCH 3/3] RegisterExternalStandardsTest: add test registering standard with ruleset in nested dir * As the behaviour shouldn't be different between Composer local/global, the test is only run against one of these (global). * The test is run against one random PHPCS version as with the other tests in place, sufficient PHPCS versions are being tested. Even though this means there is only _one_ test case, I've elected to still use a data provider to ensure that failing tests will mention the PHPCS version in the test name, which should help with debugging. Includes adding a fake standard as a fixture for this test. --- .../RegisterExternalStandardsTest.php | 74 +++++++++++++++++++ tests/fixtures/README.md | 11 +++ tests/fixtures/dummy-src/composer.json | 16 ++++ .../dummy-src/src/DummySrcSubDir/ruleset.xml | 5 ++ 4 files changed, 106 insertions(+) create mode 100644 tests/fixtures/dummy-src/composer.json create mode 100644 tests/fixtures/dummy-src/src/DummySrcSubDir/ruleset.xml diff --git a/tests/IntegrationTest/RegisterExternalStandardsTest.php b/tests/IntegrationTest/RegisterExternalStandardsTest.php index fd16252a..63b5e62b 100644 --- a/tests/IntegrationTest/RegisterExternalStandardsTest.php +++ b/tests/IntegrationTest/RegisterExternalStandardsTest.php @@ -35,6 +35,14 @@ final class RegisterExternalStandardsTest extends TestCase ), ); + private $configOneStandardInSrcSubdir = array( + 'name' => 'phpcs-composer-installer/register-external-stnds-in-src-subdir', + 'require-dev' => array( + 'squizlabs/php_codesniffer' => null, + 'phpcs-composer-installer/dummy-src' => '*', + ), + ); + /** * Set up test environment before each test. */ @@ -267,4 +275,70 @@ public function dataRegisterOneStandardMultipleRulesets() $versions = PHPCSVersions::getHighLowEachMajor(false, true); return PHPCSVersions::toDataprovider($versions); } + + /** + * Test registering an external standard which has the ruleset in a subdirectory nested in `src`. + * + * @dataProvider dataRandomPHPCSVersion + * + * @param string $phpcsVersion PHPCS version to use in this test. + * + * @return void + */ + public function testRegisterOneStandardInSrcSubdir($phpcsVersion) + { + $config = $this->configOneStandardInSrcSubdir; + $config['require-dev']['squizlabs/php_codesniffer'] = $phpcsVersion; + + $this->writeComposerJsonFile($config, static::$tempGlobalPath); + + // Install the dependencies and verify that the plugin has run. + $this->assertExecute( + 'composer global install -v --no-ansi', + 0, // Expected exit code. + 'PHP CodeSniffer Config installed_paths set to ', // Expectation for stdout. + null, // No stderr expectation. + 'Failed to install dependencies.' + ); + + // Verify that the path for the directory above the ruleset is registered. + $result = $this->executeCliCommand('"vendor/bin/phpcs" --config-show', static::$tempGlobalPath); + $this->assertSame(0, $result['exitcode'], 'Exitcode for "phpcs --config-show" did not match 0'); + + $expected = array( + '/phpcs-composer-installer/dummy-src/src', + ); + + $this->assertSame( + $expected, + $this->configShowToPathsArray($result['stdout']), + 'Paths as updated by the plugin does not contain the expected path' + ); + + // Verify that PHPCS sees the external standard. + $result = $this->executeCliCommand('"vendor/bin/phpcs" -i', static::$tempGlobalPath); + $this->assertSame(0, $result['exitcode'], 'Exitcode for "phpcs -i" did not match 0'); + + $expected = PHPCSVersions::getStandards($phpcsVersion); + $expected[] = 'DummySrcSubDir'; + sort($expected, \SORT_NATURAL); + + $this->assertSame( + $expected, + $this->standardsPhraseToArray($result['stdout']), + 'Installed standards do not match the expected standards.' + ); + } + + /** + * Data provider. + * + * @return array + */ + public function dataRandomPHPCSVersion() + { + // Test against one random PHPCS version. + $versions = array(PHPCSVersions::getRandom(true, true)); + return PHPCSVersions::toDataprovider($versions); + } } diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md index 5d2843b3..92944a9c 100644 --- a/tests/fixtures/README.md +++ b/tests/fixtures/README.md @@ -51,3 +51,14 @@ An external PHPCS standard with multiple rulesets, each in a subdirectory ("norm | **Includes sniff(s):** | :x: | | **Requires the plugin:** | :heavy_checkmark: | +### Package name: `phpcs-composer-installer/dummy-src` + +**Description:** +An external PHPCS standard with the `ruleset.xml` file in a deeper nested subdirectory. + +| Characteristics | Notes | +|--------------------------|-------------------| +| **Standard(s):** | `DummySrcSubDir` | +| **Includes sniff(s):** | :x: | +| **Requires the plugin:** | :heavy_checkmark: | + diff --git a/tests/fixtures/dummy-src/composer.json b/tests/fixtures/dummy-src/composer.json new file mode 100644 index 00000000..9ded9e25 --- /dev/null +++ b/tests/fixtures/dummy-src/composer.json @@ -0,0 +1,16 @@ +{ + "name" : "phpcs-composer-installer/dummy-src", + "description" : "Dummy PHPCS standard with deeper nested subdirectory ruleset for use in the tests.", + "type" : "phpcodesniffer-standard", + "license" : "MIT", + "require" : { + "php" : ">=5.4", + "squizlabs/php_codesniffer" : "*", + "dealerdirect/phpcodesniffer-composer-installer" : "*" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/tests/fixtures/dummy-src/src/DummySrcSubDir/ruleset.xml b/tests/fixtures/dummy-src/src/DummySrcSubDir/ruleset.xml new file mode 100644 index 00000000..3e434f11 --- /dev/null +++ b/tests/fixtures/dummy-src/src/DummySrcSubDir/ruleset.xml @@ -0,0 +1,5 @@ + + + + Dummy PHPCS standard for testing. +