1818namespace MoodleHQ \MoodleCS \moodle \Sniffs \PHPUnit ;
1919
2020use 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 ;
2421use PHP_CodeSniffer \Files \File ;
2522use PHP_CodeSniffer \Util \Tokens ;
2623use PHPCSUtils \Utils \ObjectDeclarations ;
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 )) {
0 commit comments