Skip to content

Commit

Permalink
Unit test classes should be declared as either final or abstract
Browse files Browse the repository at this point in the history
Fixes #102
  • Loading branch information
andrewnicols committed Feb 14, 2024
1 parent fc409da commit ec33ecb
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
85 changes: 85 additions & 0 deletions moodle/Sniffs/PHPUnit/TestClassesFinalSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace MoodleHQ\MoodleCS\moodle\Sniffs\PHPUnit;

// phpcs:disable moodle.NamingConventions

use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHPCSUtils\Utils\ObjectDeclarations;

/**
* Checks that test classes are declared either abstract or final.
*
* @package local_codechecker
* @copyright 2024 Andrew Lyons <adrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class TestClassesFinalSniff implements Sniff {
public function register() {
return [
T_OPEN_TAG,
];
}

public function process(File $file, $pointer) {
// If the file is not a unit test file, nothing to check.
if (!MoodleUtil::isUnitTest($file) && !MoodleUtil::isUnitTestRunning()) {
return; // @codeCoverageIgnore
}

// Get the file tokens, for ease of use.
$tokens = $file->getTokens();

// We only want to do this once per file.
$prevopentag = $file->findPrevious(T_OPEN_TAG, $pointer - 1);
if ($prevopentag !== false) {
return; // @codeCoverageIgnore
}

$cStart = $pointer;
while ($cStart = $file->findNext(T_CLASS, $cStart + 1)) {
if (MoodleUtil::isUnitTestCaseClass($file, $cStart) === false) {
// This class does not relate to a unit test.
continue;

Check warning on line 59 in moodle/Sniffs/PHPUnit/TestClassesFinalSniff.php

View check run for this annotation

Codecov / codecov/patch

moodle/Sniffs/PHPUnit/TestClassesFinalSniff.php#L59

Added line #L59 was not covered by tests
}
$className = ObjectDeclarations::getName($file, $cStart);

if (substr($className, -5) !== '_test') {
continue;
}

$classInfo = ObjectDeclarations::getClassProperties($file, $cStart);

if (!$classInfo['is_abstract'] && $classInfo['is_final'] === false) {
$fix = $file->addFixableWarning(
'Unit test %s should be declared as final.',
$cStart,
'UnitTestClassesFinal',
[$className],
);

if ($fix) {
$file->fixer->beginChangeset();
$file->fixer->addContentBefore($cStart, 'final ');
$file->fixer->endChangeset();
}
}
}
}
}
67 changes: 67 additions & 0 deletions moodle/Tests/Sniffs/PHPUnit/TestClassesFinalSniffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\PHPUnit;

use MoodleHQ\MoodleCS\moodle\Tests\MoodleCSBaseTestCase;

// phpcs:disable moodle.NamingConventions

/**
* Test the TestClassesFinalSniff sniff.
*
* @package local_codechecker
* @category test
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @covers \MoodleHQ\MoodleCS\moodle\Sniffs\PHPUnit\TestClassesFinalSniff
*/
class TestclassesFinalSniffTest extends MoodleCSBaseTestCase {

/**
* Data provider for self::provider_phpunit_data_returntypes
*/
public static function phpunit_classes_final_provider(): array {
return [
'Standard fixes' => [
'fixture' => 'testclassesfinal',
'errors' => [
],
'warnings' => [
16 => 'Unit test example_standard_test should be declared as final.',
],
],
];
}

/**
* @dataProvider phpunit_classes_final_provider
*/
public function test_phpunit_classes_final(
string $fixture,
array $errors,
array $warnings
): void {
$this->set_standard('moodle');
$this->set_sniff('moodle.PHPUnit.TestClassesFinal');
$this->set_fixture(sprintf("%s/fixtures/%s.php", __DIR__, $fixture));
$this->set_warnings($warnings);
$this->set_errors($errors);

$this->verify_cs_results();
}
}
21 changes: 21 additions & 0 deletions moodle/Tests/Sniffs/PHPUnit/fixtures/testclassesfinal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\PHPUnit;

defined('MOODLE_INTERNAL') || die(); // Make this always the 1st line in all CS fixtures.

// Testcases are not included
class example_testcase extends \advanced_testcase {
}

// An abstract test cannot be final.
abstract class example_abstract_test extends \advanced_testcase {
}

// A regular test should be final.
class example_standard_test extends \advanced_testcase {
}

// A final test is already final.
final class example_final_test extends \advanced_testcase {
}
21 changes: 21 additions & 0 deletions moodle/Tests/Sniffs/PHPUnit/fixtures/testclassesfinal.php.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace MoodleHQ\MoodleCS\moodle\Tests\Sniffs\PHPUnit;

defined('MOODLE_INTERNAL') || die(); // Make this always the 1st line in all CS fixtures.

// Testcases are not included
class example_testcase extends \advanced_testcase {
}

// An abstract test cannot be final.
abstract class example_abstract_test extends \advanced_testcase {
}

// A regular test should be final.
final class example_standard_test extends \advanced_testcase {
}

// A final test is already final.
final class example_final_test extends \advanced_testcase {
}

0 comments on commit ec33ecb

Please sign in to comment.