Skip to content

Commit 78ecda4

Browse files
authored
Merge pull request #524 from PHPCSStandards/feature/common-add-tests-and-simplify
Common::getSniffCode(): add tests, more defensive coding and minor simplification
2 parents a26fd33 + 7449b29 commit 78ecda4

File tree

3 files changed

+204
-11
lines changed

3 files changed

+204
-11
lines changed

src/Files/File.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -873,9 +873,13 @@ protected function addMessage($error, $message, $line, $column, $code, $data, $s
873873
$parts = explode('.', $code);
874874
if ($parts[0] === 'Internal') {
875875
// An internal message.
876-
$listenerCode = Common::getSniffCode($this->activeListener);
877-
$sniffCode = $code;
878-
$checkCodes = [$sniffCode];
876+
$listenerCode = '';
877+
if ($this->activeListener !== '') {
878+
$listenerCode = Common::getSniffCode($this->activeListener);
879+
}
880+
881+
$sniffCode = $code;
882+
$checkCodes = [$sniffCode];
879883
} else {
880884
if ($parts[0] !== $code) {
881885
// The full message code has been passed in.

src/Util/Common.php

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace PHP_CodeSniffer\Util;
1111

12+
use InvalidArgumentException;
1213
use Phar;
1314

1415
class Common
@@ -529,25 +530,41 @@ public static function suggestType($varType)
529530
* @param string $sniffClass The fully qualified sniff class name.
530531
*
531532
* @return string
533+
*
534+
* @throws \InvalidArgumentException When $sniffClass is not a non-empty string.
535+
* @throws \InvalidArgumentException When $sniffClass is not a FQN for a sniff(test) class.
532536
*/
533537
public static function getSniffCode($sniffClass)
534538
{
535-
$parts = explode('\\', $sniffClass);
536-
$sniff = array_pop($parts);
539+
if (is_string($sniffClass) === false || $sniffClass === '') {
540+
throw new InvalidArgumentException('The $sniffClass parameter must be a non-empty string');
541+
}
542+
543+
$parts = explode('\\', $sniffClass);
544+
$partsCount = count($parts);
545+
if ($partsCount < 4) {
546+
throw new InvalidArgumentException(
547+
'The $sniffClass parameter was not passed a fully qualified sniff(test) class name. Received: '.$sniffClass
548+
);
549+
}
550+
551+
$sniff = $parts[($partsCount - 1)];
537552

538553
if (substr($sniff, -5) === 'Sniff') {
539554
// Sniff class name.
540555
$sniff = substr($sniff, 0, -5);
541-
} else {
556+
} else if (substr($sniff, -8) === 'UnitTest') {
542557
// Unit test class name.
543558
$sniff = substr($sniff, 0, -8);
559+
} else {
560+
throw new InvalidArgumentException(
561+
'The $sniffClass parameter was not passed a fully qualified sniff(test) class name. Received: '.$sniffClass
562+
);
544563
}
545564

546-
$category = array_pop($parts);
547-
$sniffDir = array_pop($parts);
548-
$standard = array_pop($parts);
549-
$code = $standard.'.'.$category.'.'.$sniff;
550-
return $code;
565+
$standard = $parts[($partsCount - 4)];
566+
$category = $parts[($partsCount - 2)];
567+
return $standard.'.'.$category.'.'.$sniff;
551568

552569
}//end getSniffCode()
553570

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
/**
3+
* Tests for the \PHP_CodeSniffer\Util\Common::getSniffCode() method.
4+
*
5+
* @author Juliette Reinders Folmer <[email protected]>
6+
* @copyright 2024 PHPCSStandards and contributors
7+
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\Util\Common;
11+
12+
use PHP_CodeSniffer\Util\Common;
13+
use PHPUnit\Framework\TestCase;
14+
15+
/**
16+
* Tests for the \PHP_CodeSniffer\Util\Common::getSniffCode() method.
17+
*
18+
* @covers \PHP_CodeSniffer\Util\Common::getSniffCode
19+
*/
20+
final class GetSniffCodeTest extends TestCase
21+
{
22+
23+
24+
/**
25+
* Test receiving an expected exception when the $sniffClass parameter is not passed a string value or is passed an empty string.
26+
*
27+
* @param string $input NOT a fully qualified sniff class name.
28+
*
29+
* @dataProvider dataGetSniffCodeThrowsExceptionOnInvalidInput
30+
*
31+
* @return void
32+
*/
33+
public function testGetSniffCodeThrowsExceptionOnInvalidInput($input)
34+
{
35+
$exception = 'InvalidArgumentException';
36+
$message = 'The $sniffClass parameter must be a non-empty string';
37+
38+
if (method_exists($this, 'expectException') === true) {
39+
// PHPUnit 5+.
40+
$this->expectException($exception);
41+
$this->expectExceptionMessage($message);
42+
} else {
43+
// PHPUnit 4.
44+
$this->setExpectedException($exception, $message);
45+
}
46+
47+
Common::getSniffCode($input);
48+
49+
}//end testGetSniffCodeThrowsExceptionOnInvalidInput()
50+
51+
52+
/**
53+
* Data provider.
54+
*
55+
* @see testGetSniffCodeThrowsExceptionOnInvalidInput()
56+
*
57+
* @return array<string, array<string>>
58+
*/
59+
public static function dataGetSniffCodeThrowsExceptionOnInvalidInput()
60+
{
61+
return [
62+
'Class name is not a string' => [true],
63+
'Class name is empty' => [''],
64+
];
65+
66+
}//end dataGetSniffCodeThrowsExceptionOnInvalidInput()
67+
68+
69+
/**
70+
* Test receiving an expected exception when the $sniffClass parameter is not passed a value which
71+
* could be a fully qualified sniff(test) class name.
72+
*
73+
* @param string $input String input which can not be a fully qualified sniff(test) class name.
74+
*
75+
* @dataProvider dataGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass
76+
*
77+
* @return void
78+
*/
79+
public function testGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass($input)
80+
{
81+
$exception = 'InvalidArgumentException';
82+
$message = 'The $sniffClass parameter was not passed a fully qualified sniff(test) class name. Received:';
83+
84+
if (method_exists($this, 'expectException') === true) {
85+
// PHPUnit 5+.
86+
$this->expectException($exception);
87+
$this->expectExceptionMessage($message);
88+
} else {
89+
// PHPUnit 4.
90+
$this->setExpectedException($exception, $message);
91+
}
92+
93+
Common::getSniffCode($input);
94+
95+
}//end testGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass()
96+
97+
98+
/**
99+
* Data provider.
100+
*
101+
* @see testGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass()
102+
*
103+
* @return array<string, array<string>>
104+
*/
105+
public static function dataGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass()
106+
{
107+
return [
108+
'Unqualified class name' => ['ClassName'],
109+
'Fully qualified class name, not enough parts' => ['Fully\\Qualified\\ClassName'],
110+
'Fully qualified class name, doesn\'t end on Sniff or UnitTest' => ['Fully\\Sniffs\\Qualified\\ClassName'],
111+
];
112+
113+
}//end dataGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass()
114+
115+
116+
/**
117+
* Test transforming a sniff class name to a sniff code.
118+
*
119+
* @param string $fqnClass A fully qualified sniff class name.
120+
* @param string $expected Expected function output.
121+
*
122+
* @dataProvider dataGetSniffCode
123+
*
124+
* @return void
125+
*/
126+
public function testGetSniffCode($fqnClass, $expected)
127+
{
128+
$this->assertSame($expected, Common::getSniffCode($fqnClass));
129+
130+
}//end testGetSniffCode()
131+
132+
133+
/**
134+
* Data provider.
135+
*
136+
* @see testGetSniffCode()
137+
*
138+
* @return array<string, array<string, string>>
139+
*/
140+
public static function dataGetSniffCode()
141+
{
142+
return [
143+
'PHPCS native sniff' => [
144+
'fqnClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\Arrays\\ArrayIndentSniff',
145+
'expected' => 'Generic.Arrays.ArrayIndent',
146+
],
147+
'Class is a PHPCS native test class' => [
148+
'fqnClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Tests\\Arrays\\ArrayIndentUnitTest',
149+
'expected' => 'Generic.Arrays.ArrayIndent',
150+
],
151+
'Sniff in external standard without namespace prefix' => [
152+
'fqnClass' => 'MyStandard\\Sniffs\\PHP\\MyNameSniff',
153+
'expected' => 'MyStandard.PHP.MyName',
154+
],
155+
'Test in external standard without namespace prefix' => [
156+
'fqnClass' => 'MyStandard\\Tests\\PHP\\MyNameSniff',
157+
'expected' => 'MyStandard.PHP.MyName',
158+
],
159+
'Sniff in external standard with namespace prefix' => [
160+
'fqnClass' => 'Vendor\\Package\\MyStandard\\Sniffs\\Category\\AnalyzeMeSniff',
161+
'expected' => 'MyStandard.Category.AnalyzeMe',
162+
],
163+
'Test in external standard with namespace prefix' => [
164+
'fqnClass' => 'Vendor\\Package\\MyStandard\\Tests\\Category\\AnalyzeMeUnitTest',
165+
'expected' => 'MyStandard.Category.AnalyzeMe',
166+
],
167+
];
168+
169+
}//end dataGetSniffCode()
170+
171+
172+
}//end class

0 commit comments

Comments
 (0)