Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PHPUnitTestTrait
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 4
240
0.00% covered (danger)
0.00%
0 / 1
 isTestFile
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 isTestClass
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 isTestFunction
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getClassToken
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace MediaWiki\Sniffs\PHPUnit;
4
5use PHP_CodeSniffer\Files\DummyFile;
6use PHP_CodeSniffer\Files\File;
7use PHP_CodeSniffer\Util\Tokens;
8use PHPUnit\Framework\TestCase;
9
10/**
11 * Check if a class is a test class
12 *
13 * @license GPL-2.0-or-later
14 */
15trait PHPUnitTestTrait {
16
17    /**
18     * Set of PHPUnit base classes, without leading backslash
19     * @var string[]
20     */
21    private static $PHPUNIT_CLASSES = [
22        // @phan-suppress-previous-line PhanReadOnlyPrivateProperty Traits cannot have constants
23        'MediaWikiTestCase' => 'MediaWikiTestCase',
24        'MediaWikiUnitTestCase' => 'MediaWikiUnitTestCase',
25        'MediaWikiIntegrationTestCase' => 'MediaWikiIntegrationTestCase',
26        'PHPUnit_Framework_TestCase' => 'PHPUnit_Framework_TestCase',
27        // This class may be 'used', but checking for that would be complicated
28        TestCase::class => TestCase::class,
29    ];
30
31    /**
32     * @var bool[]
33     */
34    private static $isTestFile = [];
35
36    /**
37     * @param File $phpcsFile
38     * @param int|false $stackPtr
39     *
40     * @return bool
41     */
42    private function isTestFile( File $phpcsFile, $stackPtr = false ): bool {
43        $fileName = $phpcsFile->getFilename();
44
45        if ( !isset( self::$isTestFile[$fileName] ) ) {
46            $classToken = $this->getClassToken( $phpcsFile, $stackPtr ) ?:
47                $phpcsFile->findNext( Tokens::$ooScopeTokens, 0 );
48            $isTestFile = $this->isTestClass( $phpcsFile, $classToken );
49
50            // There is no file but STDIN when Helper::runPhpCs() is used
51            if ( $phpcsFile instanceof DummyFile ) {
52                return $isTestFile;
53            }
54
55            self::$isTestFile[$fileName] = $isTestFile;
56        }
57
58        return self::$isTestFile[$fileName];
59    }
60
61    /**
62     * @param File $phpcsFile
63     * @param int|false $classToken Must point at a T_CLASS token
64     *
65     * @return bool
66     */
67    private function isTestClass( File $phpcsFile, $classToken ): bool {
68        $tokens = $phpcsFile->getTokens();
69        if ( !$classToken || $tokens[$classToken]['code'] !== T_CLASS ) {
70            return false;
71        }
72
73        $extendedClass = ltrim( $phpcsFile->findExtendedClassName( $classToken ), '\\' );
74        return array_key_exists( $extendedClass, self::$PHPUNIT_CLASSES ) ||
75            (bool)preg_match(
76                '/(?:Test(?:Case)?(?:Base)?|Suite)$/',
77                $phpcsFile->getDeclarationName( $classToken )
78            );
79    }
80
81    /**
82     * @param File $phpcsFile
83     * @param int $functionToken Token position of the function declaration
84     * @return bool
85     */
86    private function isTestFunction( File $phpcsFile, $functionToken ): bool {
87        return $this->isTestClass( $phpcsFile, $this->getClassToken( $phpcsFile, $functionToken ) )
88            && preg_match( '/^(?:test|provide)|Provider$/', $phpcsFile->getDeclarationName( $functionToken ) );
89    }
90
91    /**
92     * @param File $phpcsFile
93     * @param int|false $stackPtr Should point at the T_CLASS token or a token in the class
94     *
95     * @return int|false
96     */
97    private function getClassToken( File $phpcsFile, $stackPtr ) {
98        if ( !$stackPtr ) {
99            return false;
100        }
101
102        $tokens = $phpcsFile->getTokens();
103        if ( $tokens[$stackPtr]['code'] === T_CLASS ) {
104            return $stackPtr;
105        }
106
107        foreach ( $tokens[$stackPtr]['conditions'] as $ptr => $type ) {
108            if ( $type === T_CLASS ) {
109                return $ptr;
110            }
111        }
112
113        return false;
114    }
115
116}