Page MenuHomeWickedGov Phorge

MissingCoversSniff.php
No OneTemporary

Size
3 KB
Referenced Files
None
Subscribers
None

MissingCoversSniff.php

<?php
/**
* Copyright (C) 2015 WordPoints
* Copyright (C) 2018 Kunal Mehta <legoktm@member.fsf.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
namespace MediaWiki\Sniffs\Commenting;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Identify Test classes that do not have
* any @covers tags
*/
class MissingCoversSniff implements Sniff {
/**
* @inheritDoc
*/
public function register(): array {
return [ T_CLASS ];
}
/**
* @param File $phpcsFile
* @param int $stackPtr Position of T_CLASS
* @return void
*/
public function process( File $phpcsFile, $stackPtr ) {
$name = $phpcsFile->getDeclarationName( $stackPtr );
if ( !str_ends_with( $name, 'Test' ) ) {
// Only want to validate classes that end in test
return;
}
$props = $phpcsFile->getClassProperties( $stackPtr );
if ( $props['is_abstract'] ) {
// No point in requiring @covers from an abstract class
return;
}
$classCovers = $this->hasCoversTags( $phpcsFile, $stackPtr );
if ( $classCovers ) {
// The class has a @covers tag, awesome.
return;
}
// Check each individual test function
$tokens = $phpcsFile->getTokens();
$classCloser = $tokens[$stackPtr]['scope_closer'];
$funcPtr = $stackPtr;
while ( true ) {
$funcPtr = $phpcsFile->findNext( [ T_FUNCTION ], $funcPtr + 1, $classCloser );
if ( !$funcPtr ) {
// No more
break;
}
$name = $phpcsFile->getDeclarationName( $funcPtr );
if ( !str_starts_with( $name, 'test' ) ) {
// If it doesn't start with "test", skip
continue;
}
$hasCovers = $this->hasCoversTags( $phpcsFile, $funcPtr );
if ( !$hasCovers ) {
$phpcsFile->addWarning(
'The %s test method has no @covers tags',
$funcPtr, 'MissingCovers', [ $name ]
);
}
}
}
/**
* Whether the statement has @covers tags
*
* @param File $phpcsFile
* @param int $stackPtr Position of T_CLASS/T_FUNCTION
*
* @return bool
*/
protected function hasCoversTags( File $phpcsFile, int $stackPtr ): bool {
$exclude = array_merge(
Tokens::$methodPrefixes,
[ T_WHITESPACE ]
);
$closer = $phpcsFile->findPrevious( $exclude, $stackPtr - 1, 0, true );
if ( $closer === false ) {
return false;
}
$tokens = $phpcsFile->getTokens();
$token = $tokens[$closer];
if ( $token['code'] !== T_DOC_COMMENT_CLOSE_TAG ) {
// No doc comment
return false;
}
$opener = $tokens[$closer]['comment_opener'];
$tags = $tokens[$opener]['comment_tags'];
foreach ( $tags as $tag ) {
$name = $tokens[$tag]['content'];
if ( $name === '@covers' || $name === '@coversNothing' ) {
return true;
}
}
return false;
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, May 16, 20:13 (1 d, 49 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
53/a0/d8e9d8eaccfaf7c08a08c4275770
Default Alt Text
MissingCoversSniff.php (3 KB)

Event Timeline