Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
50.00% covered (danger)
50.00%
2 / 4
CRAP
89.29% covered (warning)
89.29%
25 / 28
PHPUnitTestTrait
0.00% covered (danger)
0.00%
0 / 1
50.00% covered (danger)
50.00%
2 / 4
15.28
89.29% covered (warning)
89.29%
25 / 28
 isTestFile
0.00% covered (danger)
0.00%
0 / 1
4.18
77.78% covered (warning)
77.78%
7 / 9
 isTestClass
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
8 / 8
 isTestFunction
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 getClassToken
0.00% covered (danger)
0.00%
0 / 1
5.03
88.89% covered (warning)
88.89%
8 / 9
<?php
namespace MediaWiki\Sniffs\PHPUnit;
use PHP_CodeSniffer\Files\DummyFile;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
/**
 * Check if a class is a test class
 *
 * @license GPL-2.0-or-later
 */
trait PHPUnitTestTrait {
    /**
     * Set of PHPUnit base classes, without leading backslash
     * @var string[]
     */
    private static $PHPUNIT_CLASSES = [
        // @phan-suppress-previous-line PhanReadOnlyPrivateProperty Traits cannot have constants
        'MediaWikiTestCase' => 'MediaWikiTestCase',
        'MediaWikiUnitTestCase' => 'MediaWikiUnitTestCase',
        'MediaWikiIntegrationTestCase' => 'MediaWikiIntegrationTestCase',
        'PHPUnit_Framework_TestCase' => 'PHPUnit_Framework_TestCase',
        // This class may be 'use'd, but checking for that would be complicated
        'PHPUnit\\Framework\\TestCase' => 'PHPUnit\\Framework\\TestCase',
    ];
    /**
     * @var bool[]
     */
    private static $isTestFile = [];
    /**
     * @param File $phpcsFile
     * @param int|false $stackPtr
     *
     * @return bool
     */
    private function isTestFile( File $phpcsFile, $stackPtr = false ) : bool {
        $fileName = $phpcsFile->getFilename();
        if ( !isset( self::$isTestFile[$fileName] ) ) {
            $classToken = $this->getClassToken( $phpcsFile, $stackPtr ) ?:
                $phpcsFile->findNext( Tokens::$ooScopeTokens, 0 );
            $isTestFile = $this->isTestClass( $phpcsFile, $classToken );
            // There is no file but STDIN when Helper::runPhpCs() is used
            if ( $phpcsFile instanceof DummyFile ) {
                return $isTestFile;
            }
            self::$isTestFile[$fileName] = $isTestFile;
        }
        return self::$isTestFile[$fileName];
    }
    /**
     * @param File $phpcsFile
     * @param int|false $classToken Must point at a T_CLASS token
     *
     * @return bool
     */
    private function isTestClass( File $phpcsFile, $classToken ) : bool {
        $tokens = $phpcsFile->getTokens();
        if ( !$classToken || $tokens[$classToken]['code'] !== T_CLASS ) {
            return false;
        }
        $extendedClass = ltrim( $phpcsFile->findExtendedClassName( $classToken ), '\\' );
        return array_key_exists( $extendedClass, self::$PHPUNIT_CLASSES ) ||
            (bool)preg_match(
                '/(?:Test(?:Case)?(?:Base)?|Suite)$/',
                $phpcsFile->getDeclarationName( $classToken )
            );
    }
    /**
     * @param File $phpcsFile
     * @param int $functionToken Token position of the function declaration
     * @return bool
     */
    private function isTestFunction( File $phpcsFile, $functionToken ) : bool {
        return $this->isTestClass( $phpcsFile, $this->getClassToken( $phpcsFile, $functionToken ) )
            && preg_match( '/^(?:test|provide)|Provider$/', $phpcsFile->getDeclarationName( $functionToken ) );
    }
    /**
     * @param File $phpcsFile
     * @param int|false $stackPtr Should point at the T_CLASS token or a token in the class
     *
     * @return int|false
     */
    private function getClassToken( File $phpcsFile, $stackPtr ) {
        if ( !$stackPtr ) {
            return false;
        }
        $tokens = $phpcsFile->getTokens();
        if ( $tokens[$stackPtr]['code'] === T_CLASS ) {
            return $stackPtr;
        }
        foreach ( $tokens[$stackPtr]['conditions'] as $ptr => $type ) {
            if ( $type === T_CLASS ) {
                return $ptr;
            }
        }
        return false;
    }
}