Skip to content

Commit 40a9ebf

Browse files
committed
Refactor TestCaseCovers Sniff to new abstract TestCase sniff
1 parent a5c2563 commit 40a9ebf

File tree

3 files changed

+28
-47
lines changed

3 files changed

+28
-47
lines changed

moodle/Sniffs/PHPUnit/TestCaseCoversSniff.php

Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
namespace MoodleHQ\MoodleCS\moodle\Sniffs\PHPUnit;
1919

2020
use MoodleHQ\MoodleCS\moodle\Util\Attributes;
21-
use MoodleHQ\MoodleCS\moodle\Util\ClassnameUtil;
22-
use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil;
23-
use PHP_CodeSniffer\Sniffs\Sniff;
2421
use PHP_CodeSniffer\Files\File;
2522
use PHP_CodeSniffer\Util\Tokens;
2623
use PHPCSUtils\Utils\ObjectDeclarations;
@@ -31,7 +28,7 @@
3128
* @copyright 2022 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
3229
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3330
*/
34-
class TestCaseCoversSniff implements Sniff
31+
class TestCaseCoversSniff extends AbstractTestCaseSniff
3532
{
3633
/**
3734
* Register for open tag (only process once per file).
@@ -47,23 +44,12 @@ public function register() {
4744
* @param int $pointer The position in the stack.
4845
*/
4946
public function process(File $file, $pointer) {
50-
// Before starting any check, let's look for various things.
51-
52-
// If we aren't checking Moodle 4.0dev (400) and up, nothing to check.
53-
// Make and exception for codechecker phpunit tests, so they are run always.
54-
if (!MoodleUtil::meetsMinimumMoodleVersion($file, 400) && !MoodleUtil::isUnitTestRunning()) {
55-
return; // @codeCoverageIgnore
56-
}
57-
58-
// If the file is not a unit test file, nothing to check.
59-
if (!MoodleUtil::isUnitTest($file) && !MoodleUtil::isUnitTestRunning()) {
47+
if (!$this->shouldCheckFile($file)) {
48+
// Nothing to check.
6049
return; // @codeCoverageIgnore
6150
}
6251

63-
$supportsAttributes = true;
64-
if (MoodleUtil::meetsMinimumMoodleVersion($file, 500) === false) {
65-
$supportsAttributes = false;
66-
}
52+
$supportsAttributes = $this->shouldCheckTestCaseAttributes($file);
6753

6854
// We have all we need from core, let's start processing the file.
6955

@@ -80,31 +66,20 @@ public function process(File $file, $pointer) {
8066
return; // @codeCoverageIgnore
8167
}
8268

83-
// Iterate over all the classes (hopefully only one, but that's not this sniff problem).
84-
$cStart = $pointer;
85-
while ($cStart = $file->findNext(T_CLASS, $cStart + 1)) {
69+
foreach ($this->getTestCasesInFile($file, $pointer) as $cStart => $className) {
8670
$classInfo = ObjectDeclarations::getClassProperties($file, $cStart);
8771
if ($classInfo['is_abstract']) {
8872
// Abstract classes are not tested.
8973
// Coverage information belongs to the concrete classes that extend them.
9074
continue;
9175
}
76+
77+
9278
$class = $file->getDeclarationName($cStart);
9379
$classCovers = false; // To control when the class has a @covers tag.
9480
$classCoversNothing = false; // To control when the class has a @coversNothing tag.
9581
$classCoversDefaultClass = []; // To annotate all the existing @coversDefaultClass tags.
9682

97-
// Only if the class is extending something.
98-
// TODO: We could add a list of valid classes once we have a class-map available.
99-
if (!$file->findNext(T_EXTENDS, $cStart + 1, $tokens[$cStart]['scope_opener'])) {
100-
continue;
101-
}
102-
103-
// Ignore non ended "_test|_testcase" classes.
104-
if (substr($class, -5) !== '_test' && substr($class, -9) != '_testcase') {
105-
continue;
106-
}
107-
10883
if ($supportsAttributes) {
10984
// From PHPUnit 10 onwards, the class may be annotated with attributes.
11085
// The following attributes exist:
@@ -196,31 +171,24 @@ public function process(File $file, $pointer) {
196171
}
197172

198173
// Iterate over all the methods in the class.
199-
$mStart = $cStart;
200-
while ($mStart = $file->findNext(T_FUNCTION, $mStart + 1, $tokens[$cStart]['scope_closer'])) {
174+
// From PHPUnit 10 onwards, the class may be annotated with attributes.
175+
// The following method-level attributes exist:
176+
// - #[\PHPUnit\Framework\Attributes\CoversNothing]
177+
$validAttributes = [
178+
\PHPUnit\Framework\Attributes\CoversNothing::class,
179+
];
180+
foreach ($this->getTestMethodsInClass($file, $cStart) as $method => $mStart) {
201181
if ($supportsAttributes) {
202-
// From PHPUnit 10 onwards, the class may be annotated with attributes.
203-
// The following method-level attributes exist:
204-
// - #[\PHPUnit\Framework\Attributes\CoversNothing]
205-
206182
// Check if any valid Covers attributes are defined for this class.
207-
$validAttributes = [
208-
\PHPUnit\Framework\Attributes\CoversNothing::class,
209-
];
210183
if ($this->containsCoversAttribute($file, $mStart, $validAttributes)) {
211184
// If the class has any of the valid attributes, we can skip the rest of the checks.
212185
continue;
213186
}
214187
}
215188

216-
$method = $file->getDeclarationName($mStart);
217189
$methodCovers = false; // To control when the method has a @covers tag.
218190
$methodCoversNothing = false; // To control when the method has a @coversNothing tag.
219191

220-
// Ignore non test_xxxx() methods.
221-
if (strpos($method, 'test_') !== 0) {
222-
continue;
223-
}
224192

225193
// Let's see if the method has any phpdoc block (first non skip token must be end of phpdoc comment).
226194
$docPointer = $file->findPrevious($skipTokens, $mStart - 1, null, true);
@@ -414,7 +382,8 @@ protected function containsCoversAttribute(
414382
foreach ($attributes as $attributePtr) {
415383
$attribute = Attributes::getAttributeProperties($file, $attributePtr);
416384
if ($attribute === null) {
417-
continue; // No attribute found, skip.
385+
// No attribute found, skip.
386+
continue; // @codeCoverageIgnore
418387
}
419388

420389
if (in_array($attribute['qualified_name'], $validAttributes)) {

moodle/Tests/Sniffs/PHPUnit/TestCaseCoversTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2727
*
2828
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\PHPUnit\TestCaseCoversSniff
29+
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\PHPUnit\AbstractTestCaseSniff
2930
*/
3031
class TestCaseCoversTest extends MoodleCSBaseTestCase
3132
{
@@ -133,6 +134,11 @@ public function phpunitTestCaseCoversProvider() {
133134
11 => 'has @coversNothing, but class also has it, redundant',
134135
],
135136
],
137+
'Abstract classes are not tested' => [
138+
'fixture' => 'testcasecovers_abstract',
139+
'errors' => [],
140+
'warnings' => [],
141+
],
136142
];
137143
}
138144

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
defined('MOODLE_INTERNAL') || die(); // Make this always the 1st line in all CS fixtures.
3+
4+
abstract class correct_test extends base_test {
5+
6+
}

0 commit comments

Comments
 (0)