Skip to content
Snippets Groups Projects
Commit 58dd736c authored by catch's avatar catch
Browse files

Issue #3400366 by mondrake, alexpott, mglaman, longwave, Spokje: Remove...

Issue #3400366 by mondrake, alexpott, mglaman, longwave, Spokje: Remove DrupalComponentTestListenerTrait and replace with a PHPStan rule
parent f757b3d3
No related branches found
No related tags found
No related merge requests found
Showing
with 337 additions and 40 deletions
# cspell:ignore drupaltestbot drupaltestbotpw
# cspell:ignore cobertura drupaltestbot drupaltestbotpw
stages:
- 🗜️ Test
......@@ -173,6 +173,35 @@ variables:
TESTSUITE: PHPUnit-Unit
KUBERNETES_CPU_REQUEST: "16"
'✅️ PHPStan Tests':
<<: [ *default-job-settings ]
variables:
KUBERNETES_CPU_REQUEST: "2"
# Run if PHPStan files have changed, or manually.
rules:
- if: $CI_PIPELINE_SOURCE == "parent_pipeline" && $PERFORMANCE_TEST != "1"
changes:
- core/tests/PHPStan/*
- composer/Metapackage/PinnedDevDependencies/composer.json
- when: manual
allow_failure: true
# Default job settings runs a script that expects vendor to exist.
before_script: []
script:
- docker-php-ext-enable pcov
- cd core/tests/PHPStan
- composer install
- vendor/bin/phpunit tests --coverage-text --colors=never --coverage-cobertura=coverage.cobertura.xml --log-junit junit.xml
# Default job settings runs a script that junit files in a specific location..
after_script: []
artifacts:
when: always
reports:
junit: core/tests/PHPStan/junit.xml
coverage_report:
coverage_format: cobertura
path: core/tests/PHPStan/coverage.cobertura.xml
'🦉️️️ Nightwatch':
<<: [ *with-composer-and-yarn, *default-job-settings ]
variables:
......
......@@ -105,6 +105,11 @@
"Drupal\\Composer\\": "composer"
}
},
"autoload-dev": {
"psr-4": {
"Drupal\\PHPStan\\Rules\\": "core/tests/PHPStan/Rules"
}
},
"scripts": {
"pre-install-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
"pre-update-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
......
......@@ -26,6 +26,7 @@
"profiles/demo_umami/modules/demo_umami_content/default_content/languages/es/**/*",
"tests/fixtures/files/*",
"tests/Drupal/Tests/Component/Annotation/Doctrine/**",
"tests/PHPStan/vendor/**",
"themes/olivero/fonts/**",
"COPYRIGHT.txt",
"MAINTAINERS.txt",
......
......@@ -26,6 +26,8 @@ parameters:
- ../*/node_modules/*
- */tests/fixtures/*.php
- */tests/fixtures/*.php.gz
# Skip Drupal's own PHPStan rules test fixtures.
- tests/PHPStan/fixtures/*
# Skip Drupal 6 & 7 code.
- scripts/dump-database-d?.sh
- scripts/generate-d?-content.sh
......@@ -44,3 +46,6 @@ parameters:
- "#Drupal calls should be avoided in classes, use dependency injection instead#"
- "#^Plugin definitions cannot be altered.#"
- "#^Class .* extends @internal class#"
rules:
- Drupal\PHPStan\Rules\ComponentTestDoesNotExtendCoreTest
<?php
declare(strict_types=1);
namespace Drupal\Tests\Listeners;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\AssertionFailedError;
/**
* Ensures that no component tests are extending a core test base class.
*
* @internal
*/
trait DrupalComponentTestListenerTrait {
/**
* Reacts to the end of a test.
*
* @param \PHPUnit\Framework\Test $test
* The test object that has ended its test run.
* @param float $time
* The time the test took.
*/
protected function componentEndTest($test, $time) {
/** @var \PHPUnit\Framework\Test $test */
if (str_starts_with($test->toString(), 'Drupal\Tests\Component')) {
if ($test instanceof BrowserTestBase || $test instanceof KernelTestBase || $test instanceof UnitTestCase) {
$error = new AssertionFailedError('Component tests should not extend a core test base class.');
$test->getTestResultObject()->addFailure($test, $error, $time);
}
}
}
}
......@@ -18,7 +18,6 @@
class DrupalListener implements TestListener {
use TestListenerDefaultImplementation;
use DrupalComponentTestListenerTrait;
/**
* The wrapped Symfony test listener.
......@@ -60,7 +59,6 @@ public function startTest(Test $test): void {
*/
public function endTest(Test $test, float $time): void {
$this->symfonyListener->endTest($test, $time);
$this->componentEndTest($test, $time);
}
}
composer.lock
coverage.cobertura.xml
junit.xml
vendor/
# Drupal custom PHPStan rules
This directory contains PHPStan rules specifically developed for Drupal.
## Subdirectories
* _Rules_: contains the actual rules.
* _tests_: contains PHPUnit tests for the rules.
* _fixtures_: contains fixture files for the PHPUnit tests of the rules.
## Enabling rules
Rules are executed when they are added to the the phpstan.neon(.dist)
configuration file of a PHPStan scan run. You need to add them under the
`rules` entry in the file, specifying the fully qualified class name of the
rule. For example:
```
rules:
- Drupal\PHPStan\Rules\ComponentTestDoesNotExtendCoreTest
```
## Testing rules
PHPStan rules must be tested in the context of the PHPStan testing framework,
that differs in terms of dependencies from Drupal's one.
Note that for this reason, these tests are run _separately_ from Drupal core
tests.
A _composer.json_ file is present in this directory, indicating the required
packages for the execution of the tests. Installing via composer
```
$ composer install
```
builds a _vendor_ subdirectory that includes all the packages required. Note
this packages' codebase is totally independent from Drupal core's one.
In the context of this directory, you can then execute the rule tests like
```
$ vendor/bin/phpunit tests
```
<?php
declare(strict_types=1);
// cspell:ignore analyse
namespace Drupal\PHPStan\Rules;
use Drupal\BuildTests\Framework\BuildTestBase;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\UnitTestCase;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
/**
* Ensures that no component tests are extending a core test base class.
*
* @implements Rule<\PHPStan\Node\InClassNode>
*
* @internal
*/
final class ComponentTestDoesNotExtendCoreTest implements Rule {
/**
* {@inheritdoc}
*/
public function getNodeType(): string {
return InClassNode::class;
}
/**
* {@inheritdoc}
*/
public function processNode(Node $node, Scope $scope): array {
$class = $node->getClassReflection();
if (!str_starts_with($class->getName(), 'Drupal\Tests\Component')) {
return [];
}
$invalidParents = [
UnitTestCase::class,
BuildTestBase::class,
KernelTestBase::class,
BrowserTestBase::class,
];
foreach ($invalidParents as $invalidParent) {
if ($class->isSubclassOf($invalidParent)) {
return [
RuleErrorBuilder::message("Component tests should not extend {$invalidParent}.")
->line($node->getStartLine())
->build(),
];
}
}
return [];
}
}
{
"name": "drupal/phpstan-testing",
"description": "Tests Drupal core's PHPStan rules",
"require-dev": {
"phpunit/phpunit": "^9",
"phpstan/phpstan": "1.10.66"
},
"license": "GPL-2.0-or-later",
"autoload": {
"psr-4": {
"Drupal\\PHPStan\\Rules\\": "Rules/",
"Drupal\\BuildTests\\": "../Drupal/BuildTests/",
"Drupal\\FunctionalJavascriptTests\\": "../Drupal/FunctionalJavascriptTests",
"Drupal\\FunctionalTests\\": "../Drupal/FunctionalTests",
"Drupal\\KernelTests\\": "../Drupal/KernelTests/",
"Drupal\\Tests\\": "../Drupal/Tests/"
}
},
"require": {}
}
<?php
// phpcs:ignoreFile
declare(strict_types=1);
namespace Drupal\Tests\Component\Foo {
use Drupal\BuildTests\Framework\BuildTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\TestCase;
final class FooTest extends TestCase {
}
final class UnitTest extends UnitTestCase {
}
final class BuildTest extends BuildTestBase {
}
final class KernelTest extends KernelTestBase {
}
final class FunctionalTest extends BrowserTestBase {
}
final class FunctionalJavascriptTest extends WebDriverTestBase {
}
}
namespace Drupal\Tests\Core\Foo {
use Drupal\BuildTests\Framework\BuildTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\TestCase;
final class FooTest extends TestCase {
}
final class UnitTest extends UnitTestCase {
}
final class BuildTest extends BuildTestBase {
}
final class KernelTest extends KernelTestBase {
}
final class FunctionalTest extends BrowserTestBase {
}
final class FunctionalJavascriptTest extends WebDriverTestBase {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
colors="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true"
failOnWarning="true"
cacheResult="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<php>
<!-- Set error reporting to E_ALL. -->
<ini name="error_reporting" value="32767"/>
<!-- Do not limit the amount of memory tests take to run. -->
<ini name="memory_limit" value="-1"/>
</php>
<testsuites>
<testsuite name="PHPStan tests">
<directory>tests</directory>
</testsuite>
</testsuites>
<!-- Settings for coverage reports. -->
<coverage>
<include>
<directory>Rules</directory>
</include>
</coverage>
</phpunit>
<?php
declare(strict_types=1);
// cspell:ignore analyse
namespace Drupal\PHPStan\Tests;
use Drupal\PHPStan\Rules\ComponentTestDoesNotExtendCoreTest;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
/**
* Tests ComponentTestDoesNotExtendCoreTest rule.
*/
class ComponentTestDoesNotExtendCoreTestTest extends RuleTestCase {
/**
* {@inheritdoc}
*/
protected function getRule(): Rule {
return new ComponentTestDoesNotExtendCoreTest();
}
/**
* {@inheritdoc}
*/
public function testRule(): void {
$this->analyse(
[__DIR__ . '/../fixtures/component-tests.php'],
[
[
'Component tests should not extend Drupal\Tests\UnitTestCase.',
19,
],
[
'Component tests should not extend Drupal\BuildTests\Framework\BuildTestBase.',
22,
],
[
'Component tests should not extend Drupal\KernelTests\KernelTestBase.',
25,
],
[
'Component tests should not extend Drupal\Tests\BrowserTestBase.',
28,
],
[
'Component tests should not extend Drupal\Tests\BrowserTestBase.',
31,
],
]
);
}
}
<?php
declare(strict_types=1);
namespace Drupal\PHPStan\Tests;
use PHPUnit\Framework\TestCase;
/**
* Tests that PHPStan versions match.
*/
class EnsurePHPStanVersionsMatchTest extends TestCase {
public function testVersions(): void {
$test_composer = json_decode(file_get_contents(__DIR__ . '/../composer.json'), TRUE);
$drupal_composer = json_decode(file_get_contents(__DIR__ . '/../../../../composer/Metapackage/PinnedDevDependencies/composer.json'), TRUE);
$this->assertSame($test_composer['require-dev']['phpstan/phpstan'], $drupal_composer['require']['phpstan/phpstan']);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment