Skip to content

Commit

Permalink
Merge pull request #642 from PHPCSStandards/feature/utilitymethodtest…
Browse files Browse the repository at this point in the history
…case-add-safeguard-against-duplicate-markers

UtilityMethodTestCase: safeguard against duplicate test markers
  • Loading branch information
jrfnl authored Jan 23, 2025
2 parents 33dd777 + 41d251c commit 1526642
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 3 deletions.
51 changes: 51 additions & 0 deletions PHPCSUtils/TestUtils/UtilityMethodTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,57 @@ public static function usesPhp8NameTokens()
return \version_compare(Helper::getVersion(), '3.99.99', '>=');
}

/**
* Test QA: verify that a test case file does not contain any duplicate test markers.
*
* When a test case file contains a lot of test cases, it is easy to overlook that a test marker name
* is already in use.
* A test wouldn't necessarily fail on this, but would not be testing what is intended to be tested as
* it would be verifying token properties for the wrong token.
*
* This test safeguards against this.
*
* @since 1.1.0
*
* @coversNothing
*
* @return void
*/
public function testTestMarkersAreUnique()
{
$this->assertTestMarkersAreUnique(self::$phpcsFile);
}

/**
* Assertion to verify that a test case file does not contain any duplicate test markers.
*
* @since 1.1.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file to validate.
*
* @return void
*/
public static function assertTestMarkersAreUnique(File $phpcsFile)
{
$tokens = $phpcsFile->getTokens();

// Collect all marker comments in the file.
$seenComments = [];
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['code'] !== \T_COMMENT) {
continue;
}

if (\stripos($tokens[$i]['content'], '/* test') !== 0) {
continue;
}

$seenComments[] = $tokens[$i]['content'];
}

self::assertSame(\array_unique($seenComments), $seenComments, 'Duplicate test markers found.');
}

/**
* Get the token pointer for a target token based on a specific comment.
*
Expand Down
2 changes: 1 addition & 1 deletion Tests/BackCompat/BCFile/GetMethodParametersTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function messyDeclaration(
?\MyNS /* comment */
\ SubCat // phpcs:ignore Standard.Cat.Sniff -- for reasons.
\ MyClass $a,
$b /* test */ = /* test */ 'default' /* test*/,
$b /* comment */ = /* comment */ 'default' /* comment*/,
// phpcs:ignore Stnd.Cat.Sniff -- For reasons.
? /*comment*/
bool // phpcs:disable Stnd.Cat.Sniff -- For reasons.
Expand Down
4 changes: 2 additions & 2 deletions Tests/BackCompat/BCFile/GetMethodParametersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1226,8 +1226,8 @@ public function testMessyDeclaration()
$expected[1] = [
'token' => ($php8Names === true) ? 28 : 29,
'name' => '$b',
'content' => "\$b /* test */ = /* test */ 'default' /* test*/",
'default' => "'default' /* test*/",
'content' => "\$b /* comment */ = /* comment */ 'default' /* comment*/",
'default' => "'default' /* comment*/",
'default_token' => ($php8Names === true) ? 36 : 37,
'default_equal_token' => ($php8Names === true) ? 32 : 33,
'has_attributes' => false,
Expand Down
13 changes: 13 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/ExpectPhpcsExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ public static function resetTestFile()
// Deliberately left empty.
}

/**
* Overload the "normal" test marker QA check - this test class does not have a File object.
*
* @coversNothing
* @doesNotPerformAssertions
*
* @return void
*/
public function testTestMarkersAreUnique()
{
// Deliberately left empty.
}

/**
* Test that the helper method to handle cross-version testing of exceptions in PHPUnit
* works correctly.
Expand Down
13 changes: 13 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/FailedToTokenizeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ public static function setUpTestFile()
// Deliberately left empty.
}

/**
* Overload the "normal" test marker QA check - this test class does not have a valid File object.
*
* @coversNothing
* @doesNotPerformAssertions
*
* @return void
*/
public function testTestMarkersAreUnique()
{
// Deliberately left empty.
}

/**
* Test that the setUpTestFile() fails a test when the tokenizer errored out.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ public static function setUpTestFile()
// Deliberately left empty.
}

/**
* Overload the "normal" test marker QA check - this test class does not have a File object.
*
* @coversNothing
* @doesNotPerformAssertions
*
* @return void
*/
public function testTestMarkersAreUnique()
{
// Deliberately left empty.
}

/**
* Test the behaviour of the getTargetToken() method when the test case file has not been tokenized.
*
Expand Down
13 changes: 13 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/MissingCaseFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ public static function setUpTestFile()
// Deliberately left empty.
}

/**
* Overload the "normal" test marker QA check - this test class does not have a File object.
*
* @coversNothing
* @doesNotPerformAssertions
*
* @return void
*/
public function testTestMarkersAreUnique()
{
// Deliberately left empty.
}

/**
* Test that the setUpTestFile() fails a test when the test case file is missing.
*
Expand Down
19 changes: 19 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/ResetTestFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ final class ResetTestFileTest extends PolyfilledTestCase
/**
* Overload the "normal" set up as it needs to be run from within the actual test(s) to ensure we have a valid test.
*
* Note: We don't rely on this method being called "before class" as the tests in this class reset the statics on
* the UtilityMethodTestCase, so we need to be sure the static $caseFile property is set (again) before parsing
* a file and do that by calling this method directly from within each of the tests.
*
* @beforeClass
*
* @return void
Expand All @@ -38,6 +42,20 @@ public static function setUpTestFile()
// Deliberately not running the actual setUpTestFile() method.
}

/**
* Overload the "normal" test marker QA check - this test class resets the property containing the File object,
* so there will be no valid File + the case file is already validated in the `SetUpTestFileTest` class anyway.
*
* @coversNothing
* @doesNotPerformAssertions
*
* @return void
*/
public function testTestMarkersAreUnique()
{
// Deliberately left empty.
}

/**
* Test that the static class properties in the class are correctly reset.
*
Expand All @@ -48,6 +66,7 @@ public function testTearDownCleansUpStaticTestCaseClassProperties()
// Initialize a test, which should change the values of most static properties.
self::$tabWidth = 2;
self::$selectedSniff = ['Test.Test.Test'];
self::setUpTestFile();
parent::setUpTestFile();

// Verify that (most) properties no longer have their original value.
Expand Down
16 changes: 16 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/SetUpTestFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ public static function setUpTestFile()
// Deliberately not running the actual setUpTestFile() method.
}

/**
* Overload the "normal" test marker QA check - this test class does not have a File object in the $phpcsFile property.
*
* @coversNothing
*
* @return void
*/
public function testTestMarkersAreUnique()
{
parent::setUpTestFile();

$this->assertTestMarkersAreUnique(self::$phpcsFile);

parent::resetTestFile();
}

/**
* Test that the setUpTestFile() method works correctly.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

/* testMarkerA */
echo 'hello';

/* testMarkerB */
echo 'hello';

/* testMarkerA */
echo 'hello';

/* testMarkerB */
echo 'hello';
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Tests\TestUtils\UtilityMethodTestCase;

use PHPCSUtils\Tests\PolyfilledTestCase;

/**
* Tests for the \PHPCSUtils\TestUtils\UtilityMethodTestCase class.
*
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::testTestMarkersAreUnique
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::assertTestMarkersAreUnique
*
* @since 1.1.0
*/
final class TestMarkersAreUniqueFailsTest extends PolyfilledTestCase
{

/**
* Overload the "normal" test marker QA check - this test class does not have a valid File object.
*
* @return void
*/
public function testTestMarkersAreUnique()
{
$msg = "Duplicate test markers found.\nFailed asserting that ";
$exception = 'PHPUnit\Framework\AssertionFailedError';
if (\class_exists('PHPUnit_Framework_AssertionFailedError')) {
// PHPUnit < 6.
$exception = 'PHPUnit_Framework_AssertionFailedError';
}

$this->expectException($exception);
$this->expectExceptionMessage($msg);

parent::testTestMarkersAreUnique();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php

echo 'hello';
echo 'hello';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Tests\TestUtils\UtilityMethodTestCase;

use PHPCSUtils\Tests\PolyfilledTestCase;

/**
* Tests for the \PHPCSUtils\TestUtils\UtilityMethodTestCase class.
*
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::testTestMarkersAreUnique
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::assertTestMarkersAreUnique
*
* @since 1.1.0
*/
final class TestMarkersAreUniqueNoMarkersTest extends PolyfilledTestCase
{

/**
* Overload the "normal" test marker QA check, but only to overload the `@covers` tags.
*
* @return void
*/
public function testTestMarkersAreUnique()
{
parent::testTestMarkersAreUnique();
}
}
13 changes: 13 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/TestMarkersAreUniqueTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

/* testMarkerA */
echo 'hello';

/* testMarkerB */
echo 'hello';

/* notAMarker */
echo 'hello';

/* testMarkerD */
echo 'hello';
35 changes: 35 additions & 0 deletions Tests/TestUtils/UtilityMethodTestCase/TestMarkersAreUniqueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Tests\TestUtils\UtilityMethodTestCase;

use PHPCSUtils\Tests\PolyfilledTestCase;

/**
* Tests for the \PHPCSUtils\TestUtils\UtilityMethodTestCase class.
*
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::testTestMarkersAreUnique
* @covers \PHPCSUtils\TestUtils\UtilityMethodTestCase::assertTestMarkersAreUnique
*
* @since 1.1.0
*/
final class TestMarkersAreUniqueTest extends PolyfilledTestCase
{

/**
* Overload the "normal" test marker QA check, but only to overload the `@covers` tags.
*
* @return void
*/
public function testTestMarkersAreUnique()
{
parent::testTestMarkersAreUnique();
}
}

0 comments on commit 1526642

Please sign in to comment.