Commit 5d457e37 authored by catch's avatar catch

Issue #3063887 by mondrake, alexpott, longwave, catch, xjm: Support PHPUnit 8...

Issue #3063887 by mondrake, alexpott, longwave, catch, xjm: Support PHPUnit 8 in Drupal 9, drop support for PHPUnit 7
parent da21d14a
This diff is collapsed.
......@@ -16,7 +16,7 @@
"justinrainbow/json-schema": "^5.2",
"mikey179/vfsstream": "^1.6.8",
"phpspec/prophecy": "^1.7",
"phpunit/phpunit": "^7",
"phpunit/phpunit": "^8.4.1",
"symfony/browser-kit": "^4.4",
"symfony/css-selector": "^4.4",
"symfony/debug": "^4.4",
......
......@@ -30,22 +30,23 @@
"phpdocumentor/reflection-docblock": "4.3.4",
"phpdocumentor/type-resolver": "1.0.1",
"phpspec/prophecy": "v1.10.2",
"phpunit/php-code-coverage": "6.1.4",
"phpunit/php-code-coverage": "7.0.10",
"phpunit/php-file-iterator": "2.0.2",
"phpunit/php-text-template": "1.2.1",
"phpunit/php-timer": "2.1.2",
"phpunit/php-token-stream": "3.1.1",
"phpunit/phpunit": "7.5.20",
"phpunit/phpunit": "8.5.2",
"sebastian/code-unit-reverse-lookup": "1.0.1",
"sebastian/comparator": "3.0.2",
"sebastian/diff": "3.0.2",
"sebastian/environment": "4.2.3",
"sebastian/exporter": "3.1.2",
"sebastian/global-state": "2.0.0",
"sebastian/global-state": "3.0.0",
"sebastian/object-enumerator": "3.0.3",
"sebastian/object-reflector": "1.1.1",
"sebastian/recursion-context": "3.0.0",
"sebastian/resource-operations": "2.0.1",
"sebastian/type": "1.1.3",
"sebastian/version": "2.0.1",
"seld/jsonlint": "1.7.2",
"seld/phar-utils": "1.0.2",
......
......@@ -180,7 +180,7 @@ public function __destruct() {
$count = $this->query('SELECT COUNT(*) FROM ' . $prefix . '.sqlite_master WHERE type = :type AND name NOT LIKE :pattern', [':type' => 'table', ':pattern' => 'sqlite_%'])->fetchField();
// We can prune the database file if it doesn't have any tables.
if ($count == 0) {
if ($count == 0 && file_exists($this->connectionOptions['database'] . '-' . $prefix)) {
// Detaching the database fails at this point, but no other queries
// are executed after the connection is destructed so we can simply
// remove the database file.
......
......@@ -7,6 +7,7 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Test\Exception\MissingGroupException;
use Drupal\TestTools\PhpUnitCompatibility\PhpUnit8\ClassWriter;
use PHPUnit\Util\Test;
/**
......@@ -116,6 +117,10 @@ public function registerTestNamespaces() {
$this->classLoader->addPsr4($prefix, $paths);
}
$loader = require __DIR__ . '/../../../../../autoload.php';
// Ensure we have a valid TestCase class.
ClassWriter::mutateTestBase($loader);
return $this->testNamespaces;
}
......
......@@ -9,7 +9,8 @@
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true"
printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter">
printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter"
cacheResult="false">
<php>
<!-- Set error reporting to E_ALL. -->
<ini name="error_reporting" value="32767"/>
......
......@@ -18,6 +18,7 @@
use Drupal\Core\Test\TestDatabase;
use Drupal\Core\Test\TestRunnerKernel;
use Drupal\Core\Test\TestDiscovery;
use Drupal\TestTools\PhpUnitCompatibility\PhpUnit8\ClassWriter;
use PHPUnit\Framework\TestCase;
use PHPUnit\Runner\Version;
use Symfony\Component\Console\Output\ConsoleOutput;
......@@ -504,6 +505,7 @@ function simpletest_script_init() {
$autoloader = require_once __DIR__ . '/../../autoload.php';
// The PHPUnit compatibility layer needs to be available to autoload tests.
$autoloader->add('Drupal\\TestTools', __DIR__ . '/../tests');
ClassWriter::mutateTestBase($autoloader);
// Get URL from arguments.
if (!empty($args['url'])) {
......
<?php
namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit8;
/**
* Helper class to rewrite PHPUnit's TestCase class.
*
* This class contains static methods only and is not meant to be instantiated.
*
* @internal
* This should only be called by test running code. Drupal 9 will provide best
* effort to maintain this class for the Drupal 9 cycle. However if changes to
* PHP or PHPUnit make this impossible then support will be removed.
*/
final class ClassWriter {
/**
* This class should not be instantiated.
*/
private function __construct() {
}
/**
* Mutates the TestCase class from PHPUnit to make it compatible with Drupal.
*
* @param object $autoloader
* The autoloader.
*
* @throws \ReflectionException
*/
public static function mutateTestBase($autoloader) {
// If the class exists already there is nothing we can do. Hopefully this
// is happening because this has been called already. The call from
// \Drupal\Core\Test\TestDiscovery::registerTestNamespaces() necessitates
// this protection.
if (class_exists('PHPUnit\Framework\TestCase', FALSE)) {
return;
}
// Inspired by Symfony's simple-phpunit remove typehints from TestCase.
$reflector = new \ReflectionClass($autoloader);
$vendor_dir = dirname(dirname($reflector->getFileName()));
// Mutate TestCase code to make it compatible with Drupal 8 and 9 tests.
$alteredCode = file_get_contents($alteredFile = $vendor_dir . '/phpunit/phpunit/src/Framework/TestCase.php');
$alteredCode = preg_replace('/^ ((?:protected|public)(?: static)? function \w+\(\)): void/m', ' $1', $alteredCode);
$alteredCode = str_replace("__DIR__ . '/../Util/", "'$vendor_dir/phpunit/phpunit/src/Util/", $alteredCode);
$filename = __DIR__ . '/../../../../../../sites/simpletest/TestCase.php';
// Only write when necessary.
if (!file_exists($filename) || md5_file($filename) !== md5($alteredCode)) {
file_put_contents($filename, $alteredCode);
}
include $filename;
}
}
......@@ -479,7 +479,7 @@ public function testAccess() {
*/
public function testLabel() {
$this->expectDeprecation('Entity type ' . $this->entityTypeId . ' defines a label callback. Support for that is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Override the EntityInterface::label() method instead. See https://www.drupal.org/node/3050794');
$this->addExpectedDeprecationMessage('Entity type ' . $this->entityTypeId . ' defines a label callback. Support for that is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Override the EntityInterface::label() method instead. See https://www.drupal.org/node/3050794');
// Make a mock with one method that we use as the entity's label callback.
// We check that it is called, and that the entity's label is the callback's
......
......@@ -173,7 +173,7 @@ public function testBundle() {
*/
public function testLabel() {
$this->expectDeprecation('Entity type ' . $this->entityTypeId . ' defines a label callback. Support for that is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Override the EntityInterface::label() method instead. See https://www.drupal.org/node/3050794');
$this->addExpectedDeprecationMessage('Entity type ' . $this->entityTypeId . ' defines a label callback. Support for that is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Override the EntityInterface::label() method instead. See https://www.drupal.org/node/3050794');
// Make a mock with one method that we use as the entity's uri_callback. We
// check that it is called, and that the entity's label is the callback's
......
......@@ -5,7 +5,7 @@
use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
use PHPUnit\Framework\MockObject\Matcher\InvokedRecorder;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
/**
* @coversDefaultClass \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
......@@ -16,7 +16,7 @@ class DefaultSingleLazyPluginCollectionTest extends LazyPluginCollectionTestBase
/**
* {@inheritdoc}
*/
protected function setupPluginCollection(InvokedRecorder $create_count = NULL) {
protected function setupPluginCollection(InvocationOrder $create_count = NULL) {
$definitions = $this->getPluginDefinitions();
$this->pluginInstances['apple'] = new ConfigurablePlugin(['id' => 'apple', 'key' => 'value'], 'apple', $definitions['apple']);
$this->pluginInstances['banana'] = new ConfigurablePlugin(['id' => 'banana', 'key' => 'other_value'], 'banana', $definitions['banana']);
......
......@@ -4,7 +4,7 @@
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\MockObject\Matcher\InvokedRecorder;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
/**
* Provides a base class for plugin collection tests.
......@@ -54,12 +54,12 @@ protected function setUp() {
/**
* Sets up the default plugin collection.
*
* @param \PHPUnit\Framework\MockObject\Matcher\InvokedRecorder|null $create_count
* @param \PHPUnit\Framework\MockObject\Rule\InvocationOrder|null $create_count
* (optional) The number of times that createInstance() is expected to be
* called. For example, $this->any(), $this->once(), $this->exactly(6).
* Defaults to $this->never().
*/
protected function setupPluginCollection(InvokedRecorder $create_count = NULL) {
protected function setupPluginCollection(InvocationOrder $create_count = NULL) {
$this->pluginInstances = [];
$map = [];
foreach ($this->getPluginDefinitions() as $plugin_id => $definition) {
......
......@@ -14,21 +14,21 @@ class ExpectDeprecationTest extends UnitTestCase {
use ExpectDeprecationTrait;
/**
* @covers ::expectDeprecation
* @covers ::addExpectedDeprecationMessage
*/
public function testExpectDeprecation() {
$this->expectDeprecation('Test deprecation');
$this->addExpectedDeprecationMessage('Test deprecation');
@trigger_error('Test deprecation', E_USER_DEPRECATED);
}
/**
* @covers ::expectDeprecation
* @covers ::addExpectedDeprecationMessage
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testExpectDeprecationInIsolation() {
$this->expectDeprecation('Test isolated deprecation');
$this->expectDeprecation('Test isolated deprecation2');
$this->addExpectedDeprecationMessage('Test isolated deprecation');
$this->addExpectedDeprecationMessage('Test isolated deprecation2');
@trigger_error('Test isolated deprecation', E_USER_DEPRECATED);
@trigger_error('Test isolated deprecation2', E_USER_DEPRECATED);
}
......
......@@ -185,6 +185,15 @@ public static function getSkippedDeprecations() {
// testing using \Symfony\Component\ErrorHandler\DebugClassLoader.
'The "Twig\Environment::getTemplateClass()" method is considered internal. It may change without further notice. You should not extend it from "Drupal\Core\Template\TwigEnvironment".',
'"Symfony\Component\DomCrawler\Crawler::text()" will normalize whitespaces by default in Symfony 5.0, set the second "$normalizeWhitespace" argument to false to retrieve the non-normalized version of the text.',
// PHPUnit 8.
"The \"Drupal\Tests\Listeners\AfterSymfonyListener\" class implements \"PHPUnit\Framework\TestListener\" that is deprecated Use the `TestHook` interfaces instead.",
"The \"Drupal\Tests\Listeners\AfterSymfonyListener\" class uses \"PHPUnit\Framework\TestListenerDefaultImplementation\" that is deprecated The `TestListener` interface is deprecated.",
"The \"PHPUnit\TextUI\ResultPrinter\" class is considered internal This class is not covered by the backward compatibility promise for PHPUnit. It may change without further notice. You should not use it from \"Drupal\Tests\Listeners\HtmlOutputPrinter\".",
"The \"Drupal\Tests\Listeners\DrupalListener\" class implements \"PHPUnit\Framework\TestListener\" that is deprecated Use the `TestHook` interfaces instead.",
"The \"Drupal\Tests\Listeners\DrupalListener\" class uses \"PHPUnit\Framework\TestListenerDefaultImplementation\" that is deprecated The `TestListener` interface is deprecated.",
"The \"PHPUnit\Framework\TestSuite\" class is considered internal This class is not covered by the backward compatibility promise for PHPUnit. It may change without further notice. You should not use it from \"Drupal\Tests\TestSuites\TestSuiteBase\".",
"The \"PHPUnit\Framework\TestCase::__construct()\" method is considered internal This method is not covered by the backward compatibility promise for PHPUnit. It may change without further notice. You should not extend it from \"Drupal\Tests\BrowserTestBase\".",
"The \"PHPUnit\Framework\TestCase::__construct()\" method is considered internal This method is not covered by the backward compatibility promise for PHPUnit. It may change without further notice. You should not extend it from \"Drupal\FunctionalTests\Update\UpdatePathTestBase\".",
];
}
......
......@@ -25,7 +25,7 @@ trait ExpectDeprecationTrait {
* @param string $message
* The expected deprecation message.
*/
protected function expectDeprecation($message) {
protected function addExpectedDeprecationMessage($message) {
$this->expectedDeprecations([$message]);
}
......
......@@ -8,6 +8,7 @@
*/
use Drupal\Component\Assertion\Handle;
use Drupal\TestTools\PhpUnitCompatibility\PhpUnit8\ClassWriter;
/**
* Finds all valid extension directories recursively within a given directory.
......@@ -150,7 +151,9 @@ function drupal_phpunit_populate_class_loader() {
}
// Do class loader population.
drupal_phpunit_populate_class_loader();
$loader = drupal_phpunit_populate_class_loader();
ClassWriter::mutateTestBase($loader);
// Set sane locale settings, to ensure consistent string, dates, times and
// numbers handling.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment