Skip to content

Commit 1526642

Browse files
authored
Merge pull request #642 from PHPCSStandards/feature/utilitymethodtestcase-add-safeguard-against-duplicate-markers
UtilityMethodTestCase: safeguard against duplicate test markers
2 parents 33dd777 + 41d251c commit 1526642

15 files changed

+286
-3
lines changed

PHPCSUtils/TestUtils/UtilityMethodTestCase.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,57 @@ public static function usesPhp8NameTokens()
378378
return \version_compare(Helper::getVersion(), '3.99.99', '>=');
379379
}
380380

381+
/**
382+
* Test QA: verify that a test case file does not contain any duplicate test markers.
383+
*
384+
* When a test case file contains a lot of test cases, it is easy to overlook that a test marker name
385+
* is already in use.
386+
* A test wouldn't necessarily fail on this, but would not be testing what is intended to be tested as
387+
* it would be verifying token properties for the wrong token.
388+
*
389+
* This test safeguards against this.
390+
*
391+
* @since 1.1.0
392+
*
393+
* @coversNothing
394+
*
395+
* @return void
396+
*/
397+
public function testTestMarkersAreUnique()
398+
{
399+
$this->assertTestMarkersAreUnique(self::$phpcsFile);
400+
}
401+
402+
/**
403+
* Assertion to verify that a test case file does not contain any duplicate test markers.
404+
*
405+
* @since 1.1.0
406+
*
407+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file to validate.
408+
*
409+
* @return void
410+
*/
411+
public static function assertTestMarkersAreUnique(File $phpcsFile)
412+
{
413+
$tokens = $phpcsFile->getTokens();
414+
415+
// Collect all marker comments in the file.
416+
$seenComments = [];
417+
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
418+
if ($tokens[$i]['code'] !== \T_COMMENT) {
419+
continue;
420+
}
421+
422+
if (\stripos($tokens[$i]['content'], '/* test') !== 0) {
423+
continue;
424+
}
425+
426+
$seenComments[] = $tokens[$i]['content'];
427+
}
428+
429+
self::assertSame(\array_unique($seenComments), $seenComments, 'Duplicate test markers found.');
430+
}
431+
381432
/**
382433
* Get the token pointer for a target token based on a specific comment.
383434
*

Tests/BackCompat/BCFile/GetMethodParametersTest.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ function messyDeclaration(
118118
?\MyNS /* comment */
119119
\ SubCat // phpcs:ignore Standard.Cat.Sniff -- for reasons.
120120
\ MyClass $a,
121-
$b /* test */ = /* test */ 'default' /* test*/,
121+
$b /* comment */ = /* comment */ 'default' /* comment*/,
122122
// phpcs:ignore Stnd.Cat.Sniff -- For reasons.
123123
? /*comment*/
124124
bool // phpcs:disable Stnd.Cat.Sniff -- For reasons.

Tests/BackCompat/BCFile/GetMethodParametersTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,8 +1226,8 @@ public function testMessyDeclaration()
12261226
$expected[1] = [
12271227
'token' => ($php8Names === true) ? 28 : 29,
12281228
'name' => '$b',
1229-
'content' => "\$b /* test */ = /* test */ 'default' /* test*/",
1230-
'default' => "'default' /* test*/",
1229+
'content' => "\$b /* comment */ = /* comment */ 'default' /* comment*/",
1230+
'default' => "'default' /* comment*/",
12311231
'default_token' => ($php8Names === true) ? 36 : 37,
12321232
'default_equal_token' => ($php8Names === true) ? 32 : 33,
12331233
'has_attributes' => false,

Tests/TestUtils/UtilityMethodTestCase/ExpectPhpcsExceptionTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ public static function resetTestFile()
6060
// Deliberately left empty.
6161
}
6262

63+
/**
64+
* Overload the "normal" test marker QA check - this test class does not have a File object.
65+
*
66+
* @coversNothing
67+
* @doesNotPerformAssertions
68+
*
69+
* @return void
70+
*/
71+
public function testTestMarkersAreUnique()
72+
{
73+
// Deliberately left empty.
74+
}
75+
6376
/**
6477
* Test that the helper method to handle cross-version testing of exceptions in PHPUnit
6578
* works correctly.

Tests/TestUtils/UtilityMethodTestCase/FailedToTokenizeTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ public static function setUpTestFile()
3535
// Deliberately left empty.
3636
}
3737

38+
/**
39+
* Overload the "normal" test marker QA check - this test class does not have a valid File object.
40+
*
41+
* @coversNothing
42+
* @doesNotPerformAssertions
43+
*
44+
* @return void
45+
*/
46+
public function testTestMarkersAreUnique()
47+
{
48+
// Deliberately left empty.
49+
}
50+
3851
/**
3952
* Test that the setUpTestFile() fails a test when the tokenizer errored out.
4053
*

Tests/TestUtils/UtilityMethodTestCase/GetTargetTokenFileNotFoundTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ public static function setUpTestFile()
3434
// Deliberately left empty.
3535
}
3636

37+
/**
38+
* Overload the "normal" test marker QA check - this test class does not have a File object.
39+
*
40+
* @coversNothing
41+
* @doesNotPerformAssertions
42+
*
43+
* @return void
44+
*/
45+
public function testTestMarkersAreUnique()
46+
{
47+
// Deliberately left empty.
48+
}
49+
3750
/**
3851
* Test the behaviour of the getTargetToken() method when the test case file has not been tokenized.
3952
*

Tests/TestUtils/UtilityMethodTestCase/MissingCaseFileTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ public static function setUpTestFile()
3535
// Deliberately left empty.
3636
}
3737

38+
/**
39+
* Overload the "normal" test marker QA check - this test class does not have a File object.
40+
*
41+
* @coversNothing
42+
* @doesNotPerformAssertions
43+
*
44+
* @return void
45+
*/
46+
public function testTestMarkersAreUnique()
47+
{
48+
// Deliberately left empty.
49+
}
50+
3851
/**
3952
* Test that the setUpTestFile() fails a test when the test case file is missing.
4053
*

Tests/TestUtils/UtilityMethodTestCase/ResetTestFileTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ final class ResetTestFileTest extends PolyfilledTestCase
2828
/**
2929
* Overload the "normal" set up as it needs to be run from within the actual test(s) to ensure we have a valid test.
3030
*
31+
* Note: We don't rely on this method being called "before class" as the tests in this class reset the statics on
32+
* the UtilityMethodTestCase, so we need to be sure the static $caseFile property is set (again) before parsing
33+
* a file and do that by calling this method directly from within each of the tests.
34+
*
3135
* @beforeClass
3236
*
3337
* @return void
@@ -38,6 +42,20 @@ public static function setUpTestFile()
3842
// Deliberately not running the actual setUpTestFile() method.
3943
}
4044

45+
/**
46+
* Overload the "normal" test marker QA check - this test class resets the property containing the File object,
47+
* so there will be no valid File + the case file is already validated in the `SetUpTestFileTest` class anyway.
48+
*
49+
* @coversNothing
50+
* @doesNotPerformAssertions
51+
*
52+
* @return void
53+
*/
54+
public function testTestMarkersAreUnique()
55+
{
56+
// Deliberately left empty.
57+
}
58+
4159
/**
4260
* Test that the static class properties in the class are correctly reset.
4361
*
@@ -48,6 +66,7 @@ public function testTearDownCleansUpStaticTestCaseClassProperties()
4866
// Initialize a test, which should change the values of most static properties.
4967
self::$tabWidth = 2;
5068
self::$selectedSniff = ['Test.Test.Test'];
69+
self::setUpTestFile();
5170
parent::setUpTestFile();
5271

5372
// Verify that (most) properties no longer have their original value.

Tests/TestUtils/UtilityMethodTestCase/SetUpTestFileTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ public static function setUpTestFile()
3737
// Deliberately not running the actual setUpTestFile() method.
3838
}
3939

40+
/**
41+
* Overload the "normal" test marker QA check - this test class does not have a File object in the $phpcsFile property.
42+
*
43+
* @coversNothing
44+
*
45+
* @return void
46+
*/
47+
public function testTestMarkersAreUnique()
48+
{
49+
parent::setUpTestFile();
50+
51+
$this->assertTestMarkersAreUnique(self::$phpcsFile);
52+
53+
parent::resetTestFile();
54+
}
55+
4056
/**
4157
* Test that the setUpTestFile() method works correctly.
4258
*
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
/* testMarkerA */
4+
echo 'hello';
5+
6+
/* testMarkerB */
7+
echo 'hello';
8+
9+
/* testMarkerA */
10+
echo 'hello';
11+
12+
/* testMarkerB */
13+
echo 'hello';
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\TestUtils\UtilityMethodTestCase;
12+
13+
use PHPCSUtils\Tests\PolyfilledTestCase;
14+
15+
/**
16+
* Tests for the \PHPCSUtils\TestUtils\UtilityMethodTestCase class.
17+
*
18+
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::testTestMarkersAreUnique
19+
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::assertTestMarkersAreUnique
20+
*
21+
* @since 1.1.0
22+
*/
23+
final class TestMarkersAreUniqueFailsTest extends PolyfilledTestCase
24+
{
25+
26+
/**
27+
* Overload the "normal" test marker QA check - this test class does not have a valid File object.
28+
*
29+
* @return void
30+
*/
31+
public function testTestMarkersAreUnique()
32+
{
33+
$msg = "Duplicate test markers found.\nFailed asserting that ";
34+
$exception = 'PHPUnit\Framework\AssertionFailedError';
35+
if (\class_exists('PHPUnit_Framework_AssertionFailedError')) {
36+
// PHPUnit < 6.
37+
$exception = 'PHPUnit_Framework_AssertionFailedError';
38+
}
39+
40+
$this->expectException($exception);
41+
$this->expectExceptionMessage($msg);
42+
43+
parent::testTestMarkersAreUnique();
44+
}
45+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
echo 'hello';
4+
echo 'hello';
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\TestUtils\UtilityMethodTestCase;
12+
13+
use PHPCSUtils\Tests\PolyfilledTestCase;
14+
15+
/**
16+
* Tests for the \PHPCSUtils\TestUtils\UtilityMethodTestCase class.
17+
*
18+
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::testTestMarkersAreUnique
19+
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::assertTestMarkersAreUnique
20+
*
21+
* @since 1.1.0
22+
*/
23+
final class TestMarkersAreUniqueNoMarkersTest extends PolyfilledTestCase
24+
{
25+
26+
/**
27+
* Overload the "normal" test marker QA check, but only to overload the `@covers` tags.
28+
*
29+
* @return void
30+
*/
31+
public function testTestMarkersAreUnique()
32+
{
33+
parent::testTestMarkersAreUnique();
34+
}
35+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
/* testMarkerA */
4+
echo 'hello';
5+
6+
/* testMarkerB */
7+
echo 'hello';
8+
9+
/* notAMarker */
10+
echo 'hello';
11+
12+
/* testMarkerD */
13+
echo 'hello';
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\TestUtils\UtilityMethodTestCase;
12+
13+
use PHPCSUtils\Tests\PolyfilledTestCase;
14+
15+
/**
16+
* Tests for the \PHPCSUtils\TestUtils\UtilityMethodTestCase class.
17+
*
18+
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::testTestMarkersAreUnique
19+
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::assertTestMarkersAreUnique
20+
*
21+
* @since 1.1.0
22+
*/
23+
final class TestMarkersAreUniqueTest extends PolyfilledTestCase
24+
{
25+
26+
/**
27+
* Overload the "normal" test marker QA check, but only to overload the `@covers` tags.
28+
*
29+
* @return void
30+
*/
31+
public function testTestMarkersAreUnique()
32+
{
33+
parent::testTestMarkersAreUnique();
34+
}
35+
}

0 commit comments

Comments
 (0)