diff --git a/composer.json b/composer.json index 0b52046f036cf8019ae281578600dec794db09e3..1bd76c125e4bc9f138982da3fe82e2fe831ae7ac 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "jcalderonzumba/gastonjs": "^1.0.2", "jcalderonzumba/mink-phantomjs-driver": "^0.3.1", "mikey179/vfsstream": "^1.2", - "phpunit/phpunit": "^6.5", + "phpunit/phpunit": "^6.5 || ^7", "phpspec/prophecy": "^1.7", "symfony/css-selector": "^3.4.0", "symfony/phpunit-bridge": "^3.4.3", @@ -73,6 +73,8 @@ "pre-install-cmd": "Drupal\\Core\\Composer\\Composer::ensureComposerVersion", "pre-update-cmd": "Drupal\\Core\\Composer\\Composer::ensureComposerVersion", "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump", + "drupal-phpunit-upgrade-check": "Drupal\\Core\\Composer\\Composer::upgradePHPUnit", + "drupal-phpunit-upgrade": "@composer update phpunit/phpunit symfony/phpunit-bridge phpspec/prophecy symfony/yaml --with-dependencies --no-progress", "phpcs": "phpcs --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --", "phpcbf": "phpcbf --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --" }, diff --git a/composer.lock b/composer.lock index 55d81156b7d60c3600b0b116154016dae11a31d2..0b54bbfb9a544e8f95fda61d720edfd2368daf75 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "63b940ec40ef24930a101dfce6eed82a", + "content-hash": "8ba406bd7f3522d51f0e55fe33e51aff", "packages": [ { "name": "asm89/stack-cors", @@ -4726,8 +4726,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", @@ -4774,8 +4774,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sb@sebastian-bergmann.de" + "email": "sb@sebastian-bergmann.de", + "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", @@ -4865,8 +4865,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sb@sebastian-bergmann.de" + "email": "sb@sebastian-bergmann.de", + "role": "lead" } ], "description": "Utility class for timing", @@ -4996,8 +4996,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "role": "lead", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], "description": "The PHP Unit Testing framework.", diff --git a/core/lib/Drupal/Core/Composer/Composer.php b/core/lib/Drupal/Core/Composer/Composer.php index ed0303312fa81ee82e00daa19259d9171721897a..627972c21c991f0d2d04ff9d854e2e7cda983e24 100644 --- a/core/lib/Drupal/Core/Composer/Composer.php +++ b/core/lib/Drupal/Core/Composer/Composer.php @@ -286,4 +286,46 @@ protected static function deleteRecursive($path) { return rmdir($path) && $success; } + /** + * Fires the drupal-phpunit-upgrade script event if necessary. + * + * @param \Composer\Script\Event $event + */ + public static function upgradePHPUnit(Event $event) { + $repository = $event->getComposer()->getRepositoryManager()->getLocalRepository(); + // This is, essentially, a null constraint. We only care whether the package + // is present in the vendor directory yet, but findPackage() requires it. + $constraint = new Constraint('>', ''); + $phpunit_package = $repository->findPackage('phpunit/phpunit', $constraint); + if (!$phpunit_package) { + // There is nothing to do. The user is probably installing using the + // --no-dev flag. + return; + } + + // If the PHP version is 7.3 or above and PHPUnit is less than version 7 + // call the drupal-phpunit-upgrade script to upgrade PHPUnit. + if (!static::upgradePHPUnitCheck($phpunit_package->getVersion())) { + $event->getComposer() + ->getEventDispatcher() + ->dispatchScript('drupal-phpunit-upgrade'); + } + } + + /** + * Determines if PHPUnit needs to be upgraded. + * + * This method is located in this file because it is possible that it is + * called before the autoloader is available. + * + * @param string $phpunit_version + * The PHPUnit version string. + * + * @return bool + * TRUE if the PHPUnit needs to be upgraded, FALSE if not. + */ + public static function upgradePHPUnitCheck($phpunit_version) { + return !(version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '7.3') >= 0 && version_compare($phpunit_version, '7.0') < 0); + } + } diff --git a/core/lib/Drupal/Core/Test/TestDiscovery.php b/core/lib/Drupal/Core/Test/TestDiscovery.php index 552a72acf9e9fae9d0e852d34603645bed8475ad..7c6242f950aeb8aa90416ec972b7dd50ef5ae43a 100644 --- a/core/lib/Drupal/Core/Test/TestDiscovery.php +++ b/core/lib/Drupal/Core/Test/TestDiscovery.php @@ -85,6 +85,7 @@ public function registerTestNamespaces() { $this->testNamespaces['Drupal\\KernelTests\\'] = [$this->root . '/core/tests/Drupal/KernelTests']; $this->testNamespaces['Drupal\\FunctionalTests\\'] = [$this->root . '/core/tests/Drupal/FunctionalTests']; $this->testNamespaces['Drupal\\FunctionalJavascriptTests\\'] = [$this->root . '/core/tests/Drupal/FunctionalJavascriptTests']; + $this->testNamespaces['Drupal\\TestTools\\'] = [$this->root . '/core/tests/Drupal/TestTools']; $this->availableExtensions = []; foreach ($this->getExtensions() as $name => $extension) { diff --git a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php index ba47d91307ce0b6be3a67cc2455e814fcbcfb844..f9a82a4d546ba012f0521249ca277c35f11609a1 100644 --- a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php +++ b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php @@ -72,8 +72,8 @@ public function testNumberItem() { $this->assertEqual($entity->field_float[0]->value, $float); $this->assertTrue($entity->field_decimal instanceof FieldItemListInterface, 'Field implements interface.'); $this->assertTrue($entity->field_decimal[0] instanceof FieldItemInterface, 'Field item implements interface.'); - $this->assertEqual($entity->field_decimal->value, $decimal); - $this->assertEqual($entity->field_decimal[0]->value, $decimal); + $this->assertEqual($entity->field_decimal->value, (float) $decimal); + $this->assertEqual($entity->field_decimal[0]->value, (float) $decimal); // Verify changing the number value. $new_integer = rand(11, 20); @@ -84,14 +84,14 @@ public function testNumberItem() { $entity->field_float->value = $new_float; $this->assertEqual($entity->field_float->value, $new_float); $entity->field_decimal->value = $new_decimal; - $this->assertEqual($entity->field_decimal->value, $new_decimal); + $this->assertEqual($entity->field_decimal->value, (float) $new_decimal); // Read changed entity and assert changed values. $entity->save(); $entity = EntityTest::load($id); $this->assertEqual($entity->field_integer->value, $new_integer); $this->assertEqual($entity->field_float->value, $new_float); - $this->assertEqual($entity->field_decimal->value, $new_decimal); + $this->assertEqual($entity->field_decimal->value, (float) $new_decimal); // Test sample item generation. $entity = EntityTest::create(); diff --git a/core/modules/file/tests/src/Functional/FileFieldTestBase.php b/core/modules/file/tests/src/Functional/FileFieldTestBase.php index d738c9ab72a54ed9e92f5f47f21db51f209868b0..fc57f1747fdf304e98f070a52c0a7ba577bdaf2f 100644 --- a/core/modules/file/tests/src/Functional/FileFieldTestBase.php +++ b/core/modules/file/tests/src/Functional/FileFieldTestBase.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\file\Functional; use Drupal\Component\Render\FormattableMarkup; +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldConfig; use Drupal\file\FileInterface; @@ -10,6 +11,13 @@ use Drupal\file\Entity\File; use Drupal\Tests\TestFileCreationTrait; +// In order to manage different method signatures between PHPUnit versions, we +// dynamically load a compatibility trait dependent on the PHPUnit runner +// version. +if (!trait_exists(PhpunitVersionDependentFileFieldTestBaseTrait::class, FALSE)) { + class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\FileFieldTestBaseTrait", PhpunitVersionDependentFileFieldTestBaseTrait::class); +} + /** * Provides methods specifically for testing File module's field handling. */ @@ -19,6 +27,7 @@ abstract class FileFieldTestBase extends BrowserTestBase { use TestFileCreationTrait { getTestFiles as drupalGetTestFiles; } + use PhpunitVersionDependentFileFieldTestBaseTrait; /** * {@inheritdoc} @@ -200,28 +209,6 @@ public function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) $this->drupalPostForm(NULL, $edit, t('Save')); } - /** - * Asserts that a file exists physically on disk. - * - * Overrides PHPUnit\Framework\Assert::assertFileExists() to also work with - * file entities. - * - * @param \Drupal\File\FileInterface|string $file - * Either the file entity or the file URI. - * @param string $message - * (optional) A message to display with the assertion. - * - * @see https://www.drupal.org/node/3057326 - */ - public static function assertFileExists($file, $message = NULL) { - if ($file instanceof FileInterface) { - @trigger_error('Passing a File entity as $file argument to FileFieldTestBase::assertFileExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326', E_USER_DEPRECATED); - $file = $file->getFileUri(); - } - $message = isset($message) ? $message : new FormattableMarkup('File %file exists on the disk.', ['%file' => $file]); - parent::assertFileExists($file, $message); - } - /** * Asserts that a file exists in the database. */ @@ -232,28 +219,6 @@ public function assertFileEntryExists($file, $message = NULL) { $this->assertEqual($db_file->getFileUri(), $file->getFileUri(), $message); } - /** - * Asserts that a file does not exist on disk. - * - * Overrides PHPUnit\Framework\Assert::assertFileNotExists() to also work - * with file entities. - * - * @param \Drupal\File\FileInterface|string $file - * Either the file entity or the file URI. - * @param string $message - * (optional) A message to display with the assertion. - * - * @see https://www.drupal.org/node/3057326 - */ - public static function assertFileNotExists($file, $message = NULL) { - if ($file instanceof FileInterface) { - @trigger_error('Passing a File entity as $file argument to FileFieldTestBase::assertFileNotExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326', E_USER_DEPRECATED); - $file = $file->getFileUri(); - } - $message = isset($message) ? $message : new FormattableMarkup('File %file exists on the disk.', ['%file' => $file]); - parent::assertFileNotExists($file, $message); - } - /** * Asserts that a file does not exist in the database. */ diff --git a/core/modules/file/tests/src/Functional/FileFieldValidateTest.php b/core/modules/file/tests/src/Functional/FileFieldValidateTest.php index ec7b99d63df9d45f6e626c5799288509f101b38b..5c7b5cfff1a3f6f3a45a8196ef1b98ed6e4b5a7e 100644 --- a/core/modules/file/tests/src/Functional/FileFieldValidateTest.php +++ b/core/modules/file/tests/src/Functional/FileFieldValidateTest.php @@ -4,8 +4,10 @@ use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; use Drupal\field\Entity\FieldConfig; use Drupal\file\Entity\File; +use Drupal\Tests\Traits\ExpectDeprecationTrait; /** * Tests validation functions such as file type, max file size, max size per @@ -15,6 +17,8 @@ */ class FileFieldValidateTest extends FileFieldTestBase { + use ExpectDeprecationTrait; + /** * Tests the required property on file fields. */ @@ -193,10 +197,19 @@ public function testFileRemoval() { * * @group legacy * - * @expectedDeprecation Passing a File entity as $file argument to FileFieldTestBase::assertFileExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326 - * @expectedDeprecation Passing a File entity as $file argument to FileFieldTestBase::assertFileNotExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326 + * @todo the expectedDeprecation annotation does not work if tests are marked + * skipped. + * @see https://github.com/symfony/symfony/pull/25757 */ public function testAssertFileExistsDeprecation() { + if (RunnerVersion::getMajor() == 6) { + $this->expectDeprecation('Passing a File entity as $file argument to FileFieldTestBase::assertFileExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326'); + $this->expectDeprecation('Passing a File entity as $file argument to FileFieldTestBase::assertFileNotExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326'); + } + else { + $this->markTestSkipped('This test does not work in PHPUnit 7+ since assertFileExists only accepts string arguments for $file'); + } + $node_storage = $this->container->get('entity.manager')->getStorage('node'); $type_name = 'article'; $field_name = 'file_test'; diff --git a/core/modules/node/tests/src/FunctionalJavascript/ContextualLinksTest.php b/core/modules/node/tests/src/FunctionalJavascript/ContextualLinksTest.php index 98051262ec288ecaf838ee603ce06b4d95342dce..7e9dae071b8e85e0144bae8853bc788e350f0dda 100644 --- a/core/modules/node/tests/src/FunctionalJavascript/ContextualLinksTest.php +++ b/core/modules/node/tests/src/FunctionalJavascript/ContextualLinksTest.php @@ -111,7 +111,7 @@ public function testRevisionContextualLinks() { $this->toggleContextualTriggerVisibility('main'); $contextual_button = $page->find('css', 'main .contextual button'); - $this->assertEmpty(0, $contextual_button); + $this->assertEmpty(0, $contextual_button ?: ''); } } diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php index 36fd9b0b42a3c3e35593558f5127ae6ca1a66f41..6915e4dbe4f4b1dad8b40044116a02ca65e3c0e8 100644 --- a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php @@ -158,7 +158,7 @@ public function testNode() { $this->assertSame('2015-01-20T04:15:00', $node->field_date->value); $this->assertSame('2015-01-20', $node->field_date_without_time->value); $this->assertSame('2015-01-20', $node->field_datetime_without_time->value); - $this->assertEquals('1', $node->field_float->value); + $this->assertEquals(1, $node->field_float->value); $this->assertEquals('5', $node->field_integer->value); $this->assertEquals('Some more text', $node->field_text_list[0]->value); $this->assertEquals('7', $node->field_integer_list[0]->value); diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 39ae5ff69f2c656cafe82d2790edfbb49f61244d..528b39f9c612081ee3e5aea1fc275e12a3e148fe 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -8,6 +8,7 @@ use Drupal\Component\FileSystem\FileSystem; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Timer; +use Drupal\Core\Composer\Composer; use Drupal\Core\Database\Database; use Drupal\Core\File\Exception\FileException; use Drupal\Core\Test\EnvironmentCleaner; @@ -17,6 +18,7 @@ use Drupal\Core\Test\TestRunnerKernel; use Drupal\Core\Test\TestDiscovery; use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\Version; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; use Symfony\Component\HttpFoundation\Request; @@ -149,6 +151,11 @@ exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS); } +if (!Composer::upgradePHPUnitCheck(Version::id())) { + simpletest_script_print_error("PHPUnit testing framework version 7 or greater is required when running on PHP 7.3 or greater. Run the command 'composer run-script drupal-phpunit-upgrade' in order to fix this."); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); +} + $test_list = simpletest_script_get_test_list(); // Try to allocate unlimited time to run the tests. @@ -483,7 +490,24 @@ function simpletest_script_init() { exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } + // Detect if we're in the top-level process using the private 'execute-test' + // argument. Determine if being run on drupal.org's testing infrastructure + // using the presence of 'drupalci' in the sqlite argument. + // @todo https://www.drupal.org/project/drupalci_testbot/issues/2860941 Use + // better environment variable to detect DrupalCI. + if (!$args['execute-test'] && preg_match('/drupalci/', $args['sqlite'])) { + // Update PHPUnit if needed and possible. There is a later check once the + // autoloader is in place to ensure we're on the correct version. We need to + // do this before the autoloader is in place to ensure that it is correct. + $composer = ($composer = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar`)) + ? $php . ' ' . escapeshellarg($composer) + : 'composer'; + passthru("$composer run-script drupal-phpunit-upgrade-check"); + } + $autoloader = require_once __DIR__ . '/../../autoload.php'; + // The PHPUnit compatibility layer needs to be available to autoload tests. + $autoloader->add('Drupal\\TestTools', __DIR__ . '/../tests'); // Get URL from arguments. if (!empty($args['url'])) { diff --git a/core/tests/Drupal/KernelTests/AssertLegacyTrait.php b/core/tests/Drupal/KernelTests/AssertLegacyTrait.php index 8a0fd574b2cfc8df349d5441e2ef6097a462df71..1360798e46187b739d583d5bca819dc4dcb68a90 100644 --- a/core/tests/Drupal/KernelTests/AssertLegacyTrait.php +++ b/core/tests/Drupal/KernelTests/AssertLegacyTrait.php @@ -27,30 +27,6 @@ protected function assert($actual, $message = '') { parent::assertTrue((bool) $actual, $message); } - /** - * @see \Drupal\simpletest\TestBase::assertTrue() - */ - public static function assertTrue($actual, $message = '') { - if (is_bool($actual)) { - parent::assertTrue($actual, $message); - } - else { - parent::assertNotEmpty($actual, $message); - } - } - - /** - * @see \Drupal\simpletest\TestBase::assertFalse() - */ - public static function assertFalse($actual, $message = '') { - if (is_bool($actual)) { - parent::assertFalse($actual, $message); - } - else { - parent::assertEmpty($actual, $message); - } - } - /** * @see \Drupal\simpletest\TestBase::assertEqual() * @@ -58,7 +34,7 @@ public static function assertFalse($actual, $message = '') { * instead. */ protected function assertEqual($actual, $expected, $message = '') { - $this->assertEquals($expected, $actual, $message); + $this->assertEquals($expected, $actual, (string) $message); } /** @@ -68,7 +44,7 @@ protected function assertEqual($actual, $expected, $message = '') { * self::assertNotEquals() instead. */ protected function assertNotEqual($actual, $expected, $message = '') { - $this->assertNotEquals($expected, $actual, $message); + $this->assertNotEquals($expected, $actual, (string) $message); } /** @@ -78,7 +54,7 @@ protected function assertNotEqual($actual, $expected, $message = '') { * instead. */ protected function assertIdentical($actual, $expected, $message = '') { - $this->assertSame($expected, $actual, $message); + $this->assertSame($expected, $actual, (string) $message); } /** @@ -88,7 +64,7 @@ protected function assertIdentical($actual, $expected, $message = '') { * self::assertNotSame() instead. */ protected function assertNotIdentical($actual, $expected, $message = '') { - $this->assertNotSame($expected, $actual, $message); + $this->assertNotSame($expected, $actual, (string) $message); } /** @@ -101,7 +77,7 @@ protected function assertIdenticalObject($actual, $expected, $message = '') { // Note: ::assertSame checks whether its the same object. ::assertEquals // though compares - $this->assertEquals($expected, $actual, $message); + $this->assertEquals($expected, $actual, (string) $message); } /** diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php index 8d51d254375a1ecbcbf2b20b39b3ef40d5de4396..058669b590a93fa598ea3e166e4a9da063c409f1 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php @@ -64,7 +64,7 @@ public function providerTestThemeRenderAndAutoescape() { 'empty string unchanged' => ['', ''], 'simple string unchanged' => ['ab', 'ab'], 'int (scalar) cast to string' => [111, '111'], - 'float (scalar) cast to string' => [2.10, '2.10'], + 'float (scalar) cast to string' => [2.10, '2.1'], '> is escaped' => ['>', '>'], 'Markup EM tag is unchanged' => [Markup::create('<em>hi</em>'), '<em>hi</em>'], 'Markup SCRIPT tag is unchanged' => [Markup::create('<script>alert("hi");</script>'), '<script>alert("hi");</script>'], diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index ad46657cf646d1f35100c8d18a4c44b9c6fc2f85..dfda4a9fc060f47ddc139e96227e0603bdc249ce 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -1096,16 +1096,4 @@ public function __sleep() { return []; } - /** - * {@inheritdoc} - */ - public static function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) { - // Cast objects implementing MarkupInterface to string instead of - // relying on PHP casting them to string depending on what they are being - // comparing with. - $expected = static::castSafeStrings($expected); - $actual = static::castSafeStrings($actual); - parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); - } - } diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/AfterSymfonyListener.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/AfterSymfonyListener.php new file mode 100644 index 0000000000000000000000000000000000000000..92732557f18e6ae6679614dd5c1c9a6a68246e01 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/AfterSymfonyListener.php @@ -0,0 +1,24 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; + +/** + * Listens to PHPUnit test runs. + * + * @internal + */ +class AfterSymfonyListener implements TestListener { + use TestListenerDefaultImplementation; + + /** + * {@inheritdoc} + */ + public function endTest(Test $test, $time) { + restore_error_handler(); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/DrupalListener.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/DrupalListener.php new file mode 100644 index 0000000000000000000000000000000000000000..4cb66a1797eee4cebf14e6a09d13a2cf0d1041c8 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/DrupalListener.php @@ -0,0 +1,40 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +use Drupal\Tests\Listeners\DeprecationListenerTrait; +use Drupal\Tests\Listeners\DrupalComponentTestListenerTrait; +use Drupal\Tests\Listeners\DrupalStandardsListenerTrait; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; +use PHPUnit\Framework\Test; + +/** + * Listens to PHPUnit test runs. + * + * @internal + */ +class DrupalListener implements TestListener { + + use TestListenerDefaultImplementation; + use DeprecationListenerTrait; + use DrupalComponentTestListenerTrait; + use DrupalStandardsListenerTrait; + + /** + * {@inheritdoc} + */ + public function startTest(Test $test) { + $this->deprecationStartTest($test); + } + + /** + * {@inheritdoc} + */ + public function endTest(Test $test, $time) { + $this->deprecationEndTest($test, $time); + $this->componentEndTest($test, $time); + $this->standardsEndTest($test, $time); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/FileFieldTestBaseTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/FileFieldTestBaseTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..212485f90ca2e21249dacfaa5f28b64ccebdc093 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/FileFieldTestBaseTrait.php @@ -0,0 +1,57 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +use Drupal\Component\Render\FormattableMarkup; +use Drupal\file\FileInterface; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait FileFieldTestBaseTrait { + + /** + * Asserts that a file exists physically on disk. + * + * Overrides PHPUnit\Framework\Assert::assertFileExists() to also work with + * file entities. + * + * @param \Drupal\File\FileInterface|string $file + * Either the file entity or the file URI. + * @param string $message + * (optional) A message to display with the assertion. + * + * @see https://www.drupal.org/node/3057326 + */ + public static function assertFileExists($file, $message = NULL) { + if ($file instanceof FileInterface) { + @trigger_error('Passing a File entity as $file argument to FileFieldTestBase::assertFileExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326', E_USER_DEPRECATED); + $file = $file->getFileUri(); + } + $message = isset($message) ? $message : new FormattableMarkup('File %file exists on the disk.', ['%file' => $file]); + parent::assertFileExists($file, $message); + } + + /** + * Asserts that a file does not exist on disk. + * + * Overrides PHPUnit\Framework\Assert::assertFileNotExists() to also work + * with file entities. + * + * @param \Drupal\File\FileInterface|string $file + * Either the file entity or the file URI. + * @param string $message + * (optional) A message to display with the assertion. + * + * @see https://www.drupal.org/node/3057326 + */ + public static function assertFileNotExists($file, $message = NULL) { + if ($file instanceof FileInterface) { + @trigger_error('Passing a File entity as $file argument to FileFieldTestBase::assertFileNotExists is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Instead, pass the File entity URI via File::getFileUri(). See https://www.drupal.org/node/3057326', E_USER_DEPRECATED); + $file = $file->getFileUri(); + } + $message = isset($message) ? $message : new FormattableMarkup('File %file exists on the disk.', ['%file' => $file]); + parent::assertFileNotExists($file, $message); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/HtmlOutputPrinter.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/HtmlOutputPrinter.php new file mode 100644 index 0000000000000000000000000000000000000000..c3a6a1821f1b9a0ce8912974835c68f7931db8ed --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/HtmlOutputPrinter.php @@ -0,0 +1,27 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +use Drupal\Tests\Listeners\HtmlOutputPrinterTrait; +use PHPUnit\Framework\TestResult; +use PHPUnit\TextUI\ResultPrinter; + +/** + * Defines a class for providing html output results for functional tests. + * + * @internal + */ +class HtmlOutputPrinter extends ResultPrinter { + + use HtmlOutputPrinterTrait; + + /** + * {@inheritdoc} + */ + public function printResult(TestResult $result) { + parent::printResult($result); + + $this->printHtmlOutput(); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/SimpletestUiPrinter.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/SimpletestUiPrinter.php new file mode 100644 index 0000000000000000000000000000000000000000..5ea80f48e4c5da7ff393b08d2653c00c910a6a93 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/SimpletestUiPrinter.php @@ -0,0 +1,17 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +/** + * Defines a class for providing html output links in the Simpletest UI. + */ +class SimpletestUiPrinter extends HtmlOutputPrinter { + + /** + * {@inheritdoc} + */ + public function write($buffer) { + $this->simpletestUiWrite($buffer); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/StubTestSuiteBaseTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/StubTestSuiteBaseTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..80ae1901c2ab8912a4cb7a30c7a3bb9f00fc951a --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/StubTestSuiteBaseTrait.php @@ -0,0 +1,22 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait StubTestSuiteBaseTrait { + + /** + * {@inheritdoc} + */ + public function addTestFiles($filenames) { + // We stub addTestFiles() because the parent implementation can't deal with + // vfsStream-based filesystems due to an error in + // stream_resolve_include_path(). See + // https://github.com/mikey179/vfsStream/issues/5 Here we just store the + // test file being added in $this->testFiles. + $this->testFiles = array_merge($this->testFiles, $filenames); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/TestCompatibilityTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/TestCompatibilityTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..0785472243c3cc2fc5c8ebacc617e86fe08941a3 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit6/TestCompatibilityTrait.php @@ -0,0 +1,54 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit6; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait TestCompatibilityTrait { + + /** + * @todo deprecate this method override in + * https://www.drupal.org/project/drupal/issues/2742585 + * + * @see \Drupal\simpletest\TestBase::assertTrue() + */ + public static function assertTrue($actual, $message = '') { + if (is_bool($actual)) { + parent::assertTrue($actual, $message); + } + else { + parent::assertNotEmpty($actual, $message); + } + } + + /** + * @todo deprecate this method override in + * https://www.drupal.org/project/drupal/issues/2742585 + * + * @see \Drupal\simpletest\TestBase::assertFalse() + */ + public static function assertFalse($actual, $message = '') { + if (is_bool($actual)) { + parent::assertFalse($actual, $message); + } + else { + parent::assertEmpty($actual, $message); + } + } + + /** + * {@inheritdoc} + */ + public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) { + // Cast objects implementing MarkupInterface to string instead of + // relying on PHP casting them to string depending on what they are being + // comparing with. + if (method_exists(self::class, 'castSafeStrings')) { + $expected = self::castSafeStrings($expected); + $actual = self::castSafeStrings($actual); + } + parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/AfterSymfonyListener.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/AfterSymfonyListener.php new file mode 100644 index 0000000000000000000000000000000000000000..a7f15e5b0fa0986be5b22b6dfa03074d07317fba --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/AfterSymfonyListener.php @@ -0,0 +1,24 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; + +/** + * Listens to PHPUnit test runs. + * + * @internal + */ +class AfterSymfonyListener implements TestListener { + use TestListenerDefaultImplementation; + + /** + * {@inheritdoc} + */ + public function endTest(Test $test, float $time): void { + restore_error_handler(); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/DrupalListener.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/DrupalListener.php new file mode 100644 index 0000000000000000000000000000000000000000..e189bff93743c17c67eb839324eaf1fb1b5887a0 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/DrupalListener.php @@ -0,0 +1,40 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +use Drupal\Tests\Listeners\DeprecationListenerTrait; +use Drupal\Tests\Listeners\DrupalComponentTestListenerTrait; +use Drupal\Tests\Listeners\DrupalStandardsListenerTrait; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; +use PHPUnit\Framework\Test; + +/** + * Listens to PHPUnit test runs. + * + * @internal + */ +class DrupalListener implements TestListener { + + use TestListenerDefaultImplementation; + use DeprecationListenerTrait; + use DrupalComponentTestListenerTrait; + use DrupalStandardsListenerTrait; + + /** + * {@inheritdoc} + */ + public function startTest(Test $test): void { + $this->deprecationStartTest($test); + } + + /** + * {@inheritdoc} + */ + public function endTest(Test $test, float $time): void { + $this->deprecationEndTest($test, $time); + $this->componentEndTest($test, $time); + $this->standardsEndTest($test, $time); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/FileFieldTestBaseTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/FileFieldTestBaseTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..7d055db63fe6ab19e640cbf73b8b3f21df227d14 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/FileFieldTestBaseTrait.php @@ -0,0 +1,12 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait FileFieldTestBaseTrait { + + // @todo remove in Drupal 9. + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/HtmlOutputPrinter.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/HtmlOutputPrinter.php new file mode 100644 index 0000000000000000000000000000000000000000..64226fa96db04fbbdcbe3d97f711447fc7666d57 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/HtmlOutputPrinter.php @@ -0,0 +1,27 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +use Drupal\Tests\Listeners\HtmlOutputPrinterTrait; +use PHPUnit\Framework\TestResult; +use PHPUnit\TextUI\ResultPrinter; + +/** + * Defines a class for providing html output results for functional tests. + * + * @internal + */ +class HtmlOutputPrinter extends ResultPrinter { + + use HtmlOutputPrinterTrait; + + /** + * {@inheritdoc} + */ + public function printResult(TestResult $result): void { + parent::printResult($result); + + $this->printHtmlOutput(); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/SimpletestUiPrinter.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/SimpletestUiPrinter.php new file mode 100644 index 0000000000000000000000000000000000000000..0df6f003e1a9e3d669acbae0b5208aa2de2257e7 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/SimpletestUiPrinter.php @@ -0,0 +1,17 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +/** + * Defines a class for providing html output links in the Simpletest UI. + */ +class SimpletestUiPrinter extends HtmlOutputPrinter { + + /** + * {@inheritdoc} + */ + public function write(string $buffer): void { + $this->simpletestUiWrite($buffer); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/StubTestSuiteBaseTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/StubTestSuiteBaseTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..6c00ed955c1361eddf4d135b7ac4a9312d75326a --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/StubTestSuiteBaseTrait.php @@ -0,0 +1,22 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait StubTestSuiteBaseTrait { + + /** + * {@inheritdoc} + */ + public function addTestFiles($filenames): void { + // We stub addTestFiles() because the parent implementation can't deal with + // vfsStream-based filesystems due to an error in + // stream_resolve_include_path(). See + // https://github.com/mikey179/vfsStream/issues/5 Here we just store the + // test file being added in $this->testFiles. + $this->testFiles = array_merge($this->testFiles, $filenames); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/TestCompatibilityTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/TestCompatibilityTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..a80dfbbf79090b2e95fcb80daf9fe08acabf9d19 --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit7/TestCompatibilityTrait.php @@ -0,0 +1,54 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit7; + +/** + * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. + */ +trait TestCompatibilityTrait { + + /** + * @todo deprecate this method override in + * https://www.drupal.org/project/drupal/issues/2742585 + * + * @see \Drupal\simpletest\TestBase::assertTrue() + */ + public static function assertTrue($actual, string $message = ''): void { + if (is_bool($actual)) { + parent::assertTrue($actual, $message); + } + else { + parent::assertNotEmpty($actual, $message); + } + } + + /** + * @todo deprecate this method override in + * https://www.drupal.org/project/drupal/issues/2742585 + * + * @see \Drupal\simpletest\TestBase::assertFalse() + */ + public static function assertFalse($actual, string $message = ''): void { + if (is_bool($actual)) { + parent::assertFalse($actual, $message); + } + else { + parent::assertEmpty($actual, $message); + } + } + + /** + * {@inheritdoc} + */ + public static function assertEquals($expected, $actual, string $message = '', float $delta = 0, int $maxDepth = 10, bool $canonicalize = FALSE, bool $ignoreCase = FALSE): void { + // Cast objects implementing MarkupInterface to string instead of + // relying on PHP casting them to string depending on what they are being + // comparing with. + if (method_exists(self::class, 'castSafeStrings')) { + $expected = self::castSafeStrings($expected); + $actual = self::castSafeStrings($actual); + } + parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/RunnerVersion.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/RunnerVersion.php new file mode 100644 index 0000000000000000000000000000000000000000..b929b02d30e71f9ae97a2954bf1ba1f61c10f57f --- /dev/null +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/RunnerVersion.php @@ -0,0 +1,30 @@ +<?php + +namespace Drupal\TestTools\PhpUnitCompatibility; + +use PHPUnit\Runner\Version; + +/** + * Helper class to determine information about running PHPUnit version. + * + * This class contains static methods only and is not meant to be instantiated. + */ +final class RunnerVersion { + + /** + * This class should not be instantiated. + */ + private function __construct() { + } + + /** + * Returns the major version of the PHPUnit runner being used. + * + * @return int + * The major version of the PHPUnit runner being used. + */ + public static function getMajor() { + return (int) explode('.', Version::id())[0]; + } + +} diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 759b6033eabb22a3a1ad6ba504844a570bde67c5..96657a93e7932d59ec5bb79a0738365cccee35a4 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -688,18 +688,6 @@ protected function getDrupalSettings() { return []; } - /** - * {@inheritdoc} - */ - public static function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) { - // Cast objects implementing MarkupInterface to string instead of - // relying on PHP casting them to string depending on what they are being - // comparing with. - $expected = static::castSafeStrings($expected); - $actual = static::castSafeStrings($actual); - parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); - } - /** * Retrieves the current calling line in the class under test. * @@ -710,9 +698,14 @@ protected function getTestMethodCaller() { $backtrace = debug_backtrace(); // Find the test class that has the test method. while ($caller = Error::getLastCaller($backtrace)) { - if (isset($caller['class']) && $caller['class'] === get_class($this)) { + // If we match PHPUnit's TestCase::runTest, then the previously processed + // caller entry is where our test method sits. + if (isset($last_caller) && isset($caller['function']) && $caller['function'] === 'PHPUnit\Framework\TestCase->runTest()') { + // Return the last caller since that has to be the test class. + $caller = $last_caller; break; } + // If the test method is implemented by a test class's parent then the // class name of $this will not be part of the backtrace. // In that case we process the backtrace until the caller is not a @@ -722,6 +715,11 @@ protected function getTestMethodCaller() { $caller = $last_caller; break; } + + if (isset($caller['class']) && $caller['class'] === get_class($this)) { + break; + } + // Otherwise we have not reached our test class yet: save the last caller // and remove an element from to backtrace to process the next call. $last_caller = $caller; diff --git a/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php b/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php index 853a788f5feec316008868f78995556938b0c98d..d593cd29b820e39c6d8d7e0f09a5e0c5c4c2c89e 100644 --- a/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php +++ b/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\Core\Test; +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; use Drupal\Tests\TestSuites\TestSuiteBase; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; @@ -10,6 +11,13 @@ // manually. require_once __DIR__ . '/../../../../TestSuites/TestSuiteBase.php'; +// In order to manage different method signatures between PHPUnit versions, we +// dynamically load a compatibility trait dependent on the PHPUnit runner +// version. +if (!trait_exists(PhpunitVersionDependentStubTestSuiteBaseTrait::class, FALSE)) { + class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\StubTestSuiteBaseTrait", PhpunitVersionDependentStubTestSuiteBaseTrait::class); +} + /** * @coversDefaultClass \Drupal\Tests\TestSuites\TestSuiteBase * @@ -120,6 +128,8 @@ public function testLocalTimeZone() { */ class StubTestSuiteBase extends TestSuiteBase { + use PhpunitVersionDependentStubTestSuiteBaseTrait; + /** * Test files discovered by addTestsBySuiteNamespace(). * @@ -139,16 +149,4 @@ protected function findExtensionDirectories($root) { return []; } - /** - * {@inheritdoc} - */ - public function addTestFiles($filenames) { - // We stub addTestFiles() because the parent implementation can't deal with - // vfsStream-based filesystems due to an error in - // stream_resolve_include_path(). See - // https://github.com/mikey179/vfsStream/issues/5 Here we just store the - // test file being added in $this->testFiles. - $this->testFiles = array_merge($this->testFiles, $filenames); - } - } diff --git a/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php b/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php index 8806decd1cd0ae7b081a98c2e92ab95d4687ad68..d9cb992f239a920573abe9cbd8ae2b5f83c14e7c 100644 --- a/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php +++ b/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php @@ -1,24 +1,15 @@ <?php -namespace Drupal\Tests\Listeners; - -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestListenerDefaultImplementation; - /** + * @file * Listens to PHPUnit test runs. * - * @internal + * In order to manage different method signatures between PHPUnit versions, we + * dynamically load a class dependent on the PHPUnit runner version. */ -class AfterSymfonyListener implements TestListener { - use TestListenerDefaultImplementation; - /** - * {@inheritdoc} - */ - public function endTest(Test $test, $time) { - restore_error_handler(); - } +namespace Drupal\Tests\Listeners; + +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; -} +class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\AfterSymfonyListener", AfterSymfonyListener::class); diff --git a/core/tests/Drupal/Tests/Listeners/DrupalListener.php b/core/tests/Drupal/Tests/Listeners/DrupalListener.php index 8b7966a26d658bb74a0ba4af53fd0c021cce8d3c..ba5afbb57b125de7790d90e93012a9b864df4d27 100644 --- a/core/tests/Drupal/Tests/Listeners/DrupalListener.php +++ b/core/tests/Drupal/Tests/Listeners/DrupalListener.php @@ -1,36 +1,15 @@ <?php -namespace Drupal\Tests\Listeners; - -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestListenerDefaultImplementation; - /** + * @file * Listens to PHPUnit test runs. * - * @internal + * In order to manage different method signatures between PHPUnit versions, we + * dynamically load a class dependent on the PHPUnit runner version. */ -class DrupalListener implements TestListener { - use TestListenerDefaultImplementation; - use DeprecationListenerTrait; - use DrupalComponentTestListenerTrait; - use DrupalStandardsListenerTrait; - /** - * {@inheritdoc} - */ - public function startTest(Test $test) { - $this->deprecationStartTest($test); - } +namespace Drupal\Tests\Listeners; - /** - * {@inheritdoc} - */ - public function endTest(Test $test, $time) { - $this->deprecationEndTest($test, $time); - $this->componentEndTest($test, $time); - $this->standardsEndTest($test, $time); - } +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; -} +class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\DrupalListener", DrupalListener::class); diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php index b29b11d19df3ae9d5902807e9d48d54f7c0b59a6..908c48a5ea550b26b19911438b06995894803516 100644 --- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php +++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php @@ -1,34 +1,15 @@ <?php -namespace Drupal\Tests\Listeners; - -use PHPUnit\Framework\TestResult; -use PHPUnit\TextUI\ResultPrinter; - /** + * @file * Defines a class for providing html output results for functional tests. * - * @internal + * In order to manage different method signatures between PHPUnit versions, we + * dynamically load a class dependent on the PHPUnit runner version. */ -class HtmlOutputPrinter extends ResultPrinter { - use HtmlOutputPrinterTrait; - /** - * {@inheritdoc} - */ - public function __construct($out = NULL, $verbose = FALSE, $colors = self::COLOR_DEFAULT, $debug = FALSE, $numberOfColumns = 80, $reverse = FALSE) { - parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); - - $this->setUpHtmlOutput(); - } - - /** - * {@inheritdoc} - */ - public function printResult(TestResult $result) { - parent::printResult($result); +namespace Drupal\Tests\Listeners; - $this->printHtmlOutput(); - } +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; -} +class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\HtmlOutputPrinter", HtmlOutputPrinter::class); diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php index 1dd67eb9e9a3e5367594d3049c3865ad2d1e3353..6cdd1e80029c1c60b8ecdb1c985c21296fe90dab 100644 --- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php +++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\Listeners; +use Drupal\Component\Utility\Html; + /** * Defines a class for providing html output results for functional tests. * @@ -16,6 +18,15 @@ trait HtmlOutputPrinterTrait { */ protected $browserOutputFile; + /** + * {@inheritdoc} + */ + public function __construct($out = NULL, $verbose = FALSE, $colors = self::COLOR_DEFAULT, $debug = FALSE, $numberOfColumns = 80, $reverse = FALSE) { + parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); + + $this->setUpHtmlOutput(); + } + /** * Creates the file to list the HTML output created during the test. * @@ -69,4 +80,18 @@ protected function printHtmlOutput() { } } + /** + * Prints HTML output links for the Simpletest UI. + */ + public function simpletestUiWrite($buffer) { + $buffer = Html::escape($buffer); + // Turn HTML output URLs into clickable link <a> tags. + $url_pattern = '@https?://[^\s]+@'; + $buffer = preg_replace($url_pattern, '<a href="$0" target="_blank" title="$0">$0</a>', $buffer); + // Make the output readable in HTML by breaking up lines properly. + $buffer = nl2br($buffer); + + print $buffer; + } + } diff --git a/core/tests/Drupal/Tests/Listeners/SimpletestUiPrinter.php b/core/tests/Drupal/Tests/Listeners/SimpletestUiPrinter.php index bfb91d7b9fc01580308e7716894b2f8622d8eb0e..18151dd906613fdc2f4d28b06aebf440f4e81dee 100644 --- a/core/tests/Drupal/Tests/Listeners/SimpletestUiPrinter.php +++ b/core/tests/Drupal/Tests/Listeners/SimpletestUiPrinter.php @@ -1,26 +1,15 @@ <?php -namespace Drupal\Tests\Listeners; - -use Drupal\Component\Utility\Html; - /** + * @file * Defines a class for providing html output links in the Simpletest UI. + * + * In order to manage different method signatures between PHPUnit versions, we + * dynamically load a class dependent on the PHPUnit runner version. */ -class SimpletestUiPrinter extends HtmlOutputPrinter { - /** - * {@inheritdoc} - */ - public function write($buffer) { - $buffer = Html::escape($buffer); - // Turn HTML output URLs into clickable link <a> tags. - $url_pattern = '@https?://[^\s]+@'; - $buffer = preg_replace($url_pattern, '<a href="$0" target="_blank" title="$0">$0</a>', $buffer); - // Make the output readable in HTML by breaking up lines properly. - $buffer = nl2br($buffer); +namespace Drupal\Tests\Listeners; - print $buffer; - } +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; -} +class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\SimpletestUiPrinter", SimpletestUiPrinter::class); diff --git a/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php index f6460764b4d63de01f0fe236a6e854697fdc70eb..8b52d8707e155790ea42bec4b559099232b447ba 100644 --- a/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php +++ b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php @@ -2,11 +2,22 @@ namespace Drupal\Tests; +use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; + +// In order to manage different method signatures between PHPUnit versions, we +// dynamically load a compatibility trait dependent on the PHPUnit runner +// version. +if (!trait_exists(PhpunitVersionDependentTestCompatibilityTrait::class, FALSE)) { + class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::getMajor() . "\TestCompatibilityTrait", PhpunitVersionDependentTestCompatibilityTrait::class); +} + /** * Makes Drupal's test API forward compatible with multiple versions of PHPUnit. */ trait PhpunitCompatibilityTrait { + use PhpunitVersionDependentTestCompatibilityTrait; + /** * Returns a mock object for the specified class using the available method. * diff --git a/core/tests/Drupal/Tests/TestRequirementsTrait.php b/core/tests/Drupal/Tests/TestRequirementsTrait.php index 2c320dfaaf61017094d7543bf41edec12aa829ea..f43c7a222aca64b12071dd81c545dded1ef0aa62 100644 --- a/core/tests/Drupal/Tests/TestRequirementsTrait.php +++ b/core/tests/Drupal/Tests/TestRequirementsTrait.php @@ -3,6 +3,7 @@ namespace Drupal\Tests; use Drupal\Core\Extension\ExtensionDiscovery; +use PHPUnit\Util\Test; use PHPUnit\Framework\SkippedTestError; /** @@ -34,7 +35,18 @@ protected static function getDrupalRoot() { * skipped. Callers should not catch this exception. */ protected function checkRequirements() { - parent::checkRequirements(); + if (!$this->getName(FALSE) || !method_exists($this, $this->getName(FALSE))) { + return; + } + + $missingRequirements = Test::getMissingRequirements( + get_class($this), + $this->getName(FALSE) + ); + + if (!empty($missingRequirements)) { + $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); + } $root = static::getDrupalRoot(); diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php index d8cfbec16309122cd062fabdbe2f634ca86c92f4..4a2c012260be2e983f13eb94c3d5c9a49081ad9a 100644 --- a/core/tests/Drupal/Tests/UnitTestCase.php +++ b/core/tests/Drupal/Tests/UnitTestCase.php @@ -90,7 +90,7 @@ protected function getRandomGenerator() { protected function assertArrayEquals(array $expected, array $actual, $message = NULL) { ksort($expected); ksort($actual); - $this->assertEquals($expected, $actual, $message); + $this->assertEquals($expected, $actual, !empty($message) ? $message : ''); } /** diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php index 6d30a8b7ea8735052b9be97e6e0befdb0c291173..467e6af6e603900cbcfe87e3ed54c19c63b1855a 100644 --- a/core/tests/bootstrap.php +++ b/core/tests/bootstrap.php @@ -143,6 +143,7 @@ function drupal_phpunit_populate_class_loader() { $loader->add('Drupal\\KernelTests', __DIR__); $loader->add('Drupal\\FunctionalTests', __DIR__); $loader->add('Drupal\\FunctionalJavascriptTests', __DIR__); + $loader->add('Drupal\\TestTools', __DIR__); if (!isset($GLOBALS['namespaces'])) { // Scan for arbitrary extension namespaces from core and contrib.