From d4fa83453d6a1a040f151943e5e63fbc9745578f Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Tue, 7 May 2024 11:52:49 +0100 Subject: [PATCH] Issue #3417066 by mondrake, longwave, larowlan, alexpott, catch: Upgrade PHPUnit to 10, drop Symfony PHPUnit-bridge dependency --- composer.json | 3 +- composer.lock | 598 +++++++----------- .../Metapackage/CoreRecommended/composer.json | 2 +- .../Metapackage/DevDependencies/composer.json | 3 +- .../PinnedDevDependencies/composer.json | 42 +- composer/Plugin/VendorHardening/Config.php | 1 - core/.deprecation-ignore.txt | 16 +- core/.phpstan-baseline.php | 176 ++++-- core/includes/bootstrap.inc | 7 +- core/lib/Drupal/Core/Database/Database.php | 7 + .../TestHttpClientMiddleware.php | 6 +- .../Drupal/Core/Test/PhpUnitTestRunner.php | 57 +- core/lib/Drupal/Core/Test/TestDiscovery.php | 5 - .../lib/Drupal/Core/Test/TestRunnerKernel.php | 6 +- core/lib/Drupal/Core/Test/TestSetupTrait.php | 1 + core/lib/Drupal/Core/Utility/Error.php | 12 + .../src/Unit/process/LanguageDomainsTest.php | 5 - .../SecurityAdvisoryTest.php | 2 +- core/phpunit.xml.dist | 73 ++- core/scripts/run-tests.sh | 9 +- .../BuildTests/Framework/BuildTestBase.php | 2 - .../Framework/Tests/BuildTestTest.php | 2 + .../WebDriverTestBase.php | 6 - .../Core/Test/PhpUnitBridgeTest.php | 2 +- .../Installer/SuperUserAccessInstallTest.php | 6 +- .../FunctionalTestDebugHtmlOutputTest.php | 33 +- .../DrupalKernel/DrupalKernelSiteTest.php | 1 + .../Core/DrupalKernel/DrupalKernelTest.php | 12 + .../Drupal/KernelTests/KernelTestBase.php | 37 +- .../Drupal/KernelTests/KernelTestBaseTest.php | 5 - .../Comparator/MarkupInterfaceComparator.php | 2 +- .../ErrorHandler/BootstrapErrorHandler.php | 84 +++ .../ErrorHandler/TestErrorHandler.php | 75 +++ .../DeprecationBridge/DeprecationHandler.php | 249 ++++++++ .../ExpectDeprecationTrait.php | 137 ++++ .../HtmlLogging/HtmlOutputLogger.php | 143 +++++ .../Extension/HtmlLogging/SubscriberBase.php | 23 + .../TestRunnerFinishedSubscriber.php | 21 + .../TestRunnerStartedSubscriber.php | 21 + .../PhpUnitCompatibility/ClassWriter.php | 118 ---- .../TestCompatibilityTrait.php | 20 +- .../Drupal/Tests/BrowserHtmlDebugTrait.php | 21 +- core/tests/Drupal/Tests/BrowserTestBase.php | 30 +- .../Component/Assertion/InspectorTest.php | 2 +- .../DependencyInjection/ContainerTest.php | 2 +- .../Dumper/OptimizedPhpArrayDumperTest.php | 2 +- .../PhpStorage/FileStorageReadOnlyTest.php | 2 +- .../Component/PhpStorage/FileStorageTest.php | 5 +- .../Render/FormattableMarkupTest.php | 2 +- .../Tests/Component/Utility/BytesTest.php | 2 +- .../Tests/Component/Utility/UnicodeTest.php | 2 +- .../Plugin/Scaffold/AssertUtilsTrait.php | 3 - .../Functional/ManageGitIgnoreTest.php | 2 - .../Scaffold/Functional/ScaffoldTest.php | 2 - .../Functional/ScaffoldUpgradeTest.php | 2 - .../Scaffold/Integration/AppendOpTest.php | 2 - .../Scaffold/Integration/ReplaceOpTest.php | 2 - .../Scaffold/Integration/SkipOpTest.php | 2 - .../Plugin/VendorHardening/ConfigTest.php | 3 - .../VendorHardeningPluginTest.php | 2 - .../Tests/Core/Asset/CssOptimizerUnitTest.php | 5 - .../Tests/Core/Form/FormBuilderTest.php | 2 +- .../Drupal/Tests/Core/Form/FormCacheTest.php | 10 - .../Core/Form/FormStateDecoratorBaseTest.php | 2 +- .../Core/Security/RequestSanitizerTest.php | 8 + .../TranslatableMarkupTest.php | 2 +- .../Core/Test/PhpUnitBridgeRequiresTest.php | 44 -- .../Tests/Core/Test/PhpUnitBridgeTest.php | 14 - .../Drupal/Tests/Core/Test/PhpUnitCliTest.php | 6 - .../Tests/Core/Test/PhpUnitTestRunnerTest.php | 4 +- .../Tests/Core/Test/TestSuiteBaseTest.php | 151 +---- .../Core/Utility/CallableResolverTest.php | 49 +- .../Drupal/Tests/ExpectDeprecationTest.php | 2 +- .../Drupal/Tests/Listeners/DrupalListener.php | 64 -- .../Tests/Listeners/HtmlOutputPrinter.php | 28 - .../Listeners/HtmlOutputPrinterTrait.php | 83 --- .../Tests/PhpUnitCompatibilityTrait.php | 2 +- .../Drupal/Tests/PhpUnitWarningsTest.php | 21 - .../Drupal/Tests/Traits/PhpUnitWarnings.php | 68 -- core/tests/Drupal/Tests/UnitTestCase.php | 4 +- core/tests/TestSuites/BuildTestSuite.php | 34 - .../FunctionalJavascriptTestSuite.php | 34 - core/tests/TestSuites/FunctionalTestSuite.php | 34 - core/tests/TestSuites/KernelTestSuite.php | 34 - core/tests/TestSuites/TestSuiteBase.php | 75 --- core/tests/TestSuites/UnitTestSuite.php | 34 - core/tests/bootstrap.php | 33 +- 87 files changed, 1428 insertions(+), 1539 deletions(-) create mode 100644 core/tests/Drupal/TestTools/ErrorHandler/BootstrapErrorHandler.php create mode 100644 core/tests/Drupal/TestTools/ErrorHandler/TestErrorHandler.php create mode 100644 core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php create mode 100644 core/tests/Drupal/TestTools/Extension/DeprecationBridge/ExpectDeprecationTrait.php create mode 100644 core/tests/Drupal/TestTools/Extension/HtmlLogging/HtmlOutputLogger.php create mode 100644 core/tests/Drupal/TestTools/Extension/HtmlLogging/SubscriberBase.php create mode 100644 core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerFinishedSubscriber.php create mode 100644 core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerStartedSubscriber.php delete mode 100644 core/tests/Drupal/TestTools/PhpUnitCompatibility/ClassWriter.php rename core/tests/Drupal/TestTools/PhpUnitCompatibility/{PhpUnit9 => PhpUnit10}/TestCompatibilityTrait.php (52%) delete mode 100644 core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeRequiresTest.php delete mode 100644 core/tests/Drupal/Tests/Listeners/DrupalListener.php delete mode 100644 core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php delete mode 100644 core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php delete mode 100644 core/tests/Drupal/Tests/PhpUnitWarningsTest.php delete mode 100644 core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php delete mode 100644 core/tests/TestSuites/BuildTestSuite.php delete mode 100644 core/tests/TestSuites/FunctionalJavascriptTestSuite.php delete mode 100644 core/tests/TestSuites/FunctionalTestSuite.php delete mode 100644 core/tests/TestSuites/KernelTestSuite.php delete mode 100644 core/tests/TestSuites/TestSuiteBase.php delete mode 100644 core/tests/TestSuites/UnitTestSuite.php diff --git a/composer.json b/composer.json index 9fef43ac90fa..de31724f3e53 100644 --- a/composer.json +++ b/composer.json @@ -33,13 +33,12 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.10.62", "phpstan/phpstan-phpunit": "^1.3.16", - "phpunit/phpunit": "^9.6.13", + "phpunit/phpunit": "^10.5.19", "symfony/browser-kit": "^7.0", "symfony/css-selector": "^7.0", "symfony/dom-crawler": "^7.0", "symfony/error-handler": "^7.0", "symfony/lock": "^7.0", - "symfony/phpunit-bridge": "^7.0", "symfony/var-dumper": "^7.0" }, "replace": { diff --git a/composer.lock b/composer.lock index 59cc6b18cd44..a8ab3cc28454 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": "7033803d9382729f982fd06b4b8603de", + "content-hash": "0fb1643cd4e3341596fa89fcbdf1b7e4", "packages": [ { "name": "asm89/stack-cors", @@ -1861,29 +1861,29 @@ }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -1915,7 +1915,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" }, "funding": [ { @@ -1923,7 +1924,7 @@ "type": "github" } ], - "time": "2024-03-02T06:30:58+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { "name": "symfony/console", @@ -7160,16 +7161,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "10.1.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", "shasum": "" }, "require": { @@ -7177,18 +7178,18 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -7197,7 +7198,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "10.1-dev" } }, "autoload": { @@ -7226,7 +7227,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" }, "funding": [ { @@ -7234,32 +7235,32 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-03-12T15:33:41+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -7286,7 +7287,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -7294,28 +7296,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-pcntl": "*" @@ -7323,7 +7325,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -7349,7 +7351,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, "funding": [ { @@ -7357,32 +7359,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2023-02-03T06:56:09+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -7408,7 +7410,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -7416,32 +7419,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -7467,7 +7470,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, "funding": [ { @@ -7475,24 +7478,23 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "10.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3", + "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -7502,27 +7504,26 @@ "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.5", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" }, "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "ext-soap": "To be able to generate mocks based on WSDL files" }, "bin": [ "phpunit" @@ -7530,7 +7531,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -7562,7 +7563,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20" }, "funding": [ { @@ -7578,7 +7579,7 @@ "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2024-04-24T06:32:35+00:00" }, { "name": "react/promise", @@ -7655,28 +7656,28 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -7699,7 +7700,8 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" }, "funding": [ { @@ -7707,32 +7709,32 @@ "type": "github" } ], - "time": "2024-03-02T06:27:43+00:00" + "time": "2024-03-02T07:12:49+00:00" }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -7755,7 +7757,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -7763,32 +7765,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -7810,7 +7812,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -7818,34 +7820,36 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -7884,7 +7888,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -7892,33 +7897,33 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -7941,7 +7946,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -7949,27 +7955,27 @@ "type": "github" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "6.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-posix": "*" @@ -7977,7 +7983,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -7996,7 +8002,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -8004,7 +8010,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -8012,34 +8019,34 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "5.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -8081,7 +8088,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" }, "funding": [ { @@ -8089,38 +8097,35 @@ "type": "github" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2024-03-02T07:17:12+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -8139,13 +8144,14 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" }, "funding": [ { @@ -8153,33 +8159,33 @@ "type": "github" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2024-03-02T07:19:19+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -8202,7 +8208,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -8210,34 +8217,34 @@ "type": "github" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -8259,7 +8266,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -8267,32 +8274,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -8314,7 +8321,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -8322,32 +8329,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -8377,7 +8384,7 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { @@ -8385,86 +8392,32 @@ "type": "github" } ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -8487,7 +8440,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -8495,29 +8448,29 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -8540,7 +8493,7 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -8548,7 +8501,7 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { "name": "seld/jsonlint", @@ -9204,87 +9157,6 @@ ], "time": "2024-03-19T09:26:35+00:00" }, - { - "name": "symfony/phpunit-bridge", - "version": "v7.0.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "a014167aa1f66cb9990675840da65609d3e61612" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/a014167aa1f66cb9990675840da65609d3e61612", - "reference": "a014167aa1f66cb9990675840da65609d3e61612", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" - }, - "require-dev": { - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/error-handler": "^5.4|^6.4|^7.0", - "symfony/polyfill-php81": "^1.27" - }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-03-19T11:57:22+00:00" - }, { "name": "theseer/tokenizer", "version": "1.2.3", diff --git a/composer/Metapackage/CoreRecommended/composer.json b/composer/Metapackage/CoreRecommended/composer.json index 79ba852d5cd5..9095f5a62eaf 100644 --- a/composer/Metapackage/CoreRecommended/composer.json +++ b/composer/Metapackage/CoreRecommended/composer.json @@ -30,7 +30,7 @@ "psr/http-factory": "~1.0.2", "psr/log": "~3.0.0", "ralouphie/getallheaders": "~3.0.3", - "sebastian/diff": "~4.0.6", + "sebastian/diff": "~5.1.1", "symfony/console": "~v7.0.6", "symfony/dependency-injection": "~v7.0.6", "symfony/deprecation-contracts": "~v3.4.0", diff --git a/composer/Metapackage/DevDependencies/composer.json b/composer/Metapackage/DevDependencies/composer.json index 396fbfad68e5..259f200cec6b 100644 --- a/composer/Metapackage/DevDependencies/composer.json +++ b/composer/Metapackage/DevDependencies/composer.json @@ -25,13 +25,12 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.10.62", "phpstan/phpstan-phpunit": "^1.3.16", - "phpunit/phpunit": "^9.6.13", + "phpunit/phpunit": "^10.5.19", "symfony/browser-kit": "^7.0", "symfony/css-selector": "^7.0", "symfony/dom-crawler": "^7.0", "symfony/error-handler": "^7.0", "symfony/lock": "^7.0", - "symfony/phpunit-bridge": "^7.0", "symfony/var-dumper": "^7.0" } } diff --git a/composer/Metapackage/PinnedDevDependencies/composer.json b/composer/Metapackage/PinnedDevDependencies/composer.json index c430ac3a3dcf..e8bc1a5953d2 100644 --- a/composer/Metapackage/PinnedDevDependencies/composer.json +++ b/composer/Metapackage/PinnedDevDependencies/composer.json @@ -52,28 +52,27 @@ "phpstan/phpstan": "1.10.66", "phpstan/phpstan-deprecation-rules": "1.1.4", "phpstan/phpstan-phpunit": "1.3.16", - "phpunit/php-code-coverage": "9.2.31", - "phpunit/php-file-iterator": "3.0.6", - "phpunit/php-invoker": "3.1.1", - "phpunit/php-text-template": "2.0.4", - "phpunit/php-timer": "5.0.3", - "phpunit/phpunit": "9.6.19", + "phpunit/php-code-coverage": "10.1.14", + "phpunit/php-file-iterator": "4.1.0", + "phpunit/php-invoker": "4.0.0", + "phpunit/php-text-template": "3.0.1", + "phpunit/php-timer": "6.0.0", + "phpunit/phpunit": "10.5.20", "react/promise": "v3.1.0", - "sebastian/cli-parser": "1.0.2", - "sebastian/code-unit": "1.0.8", - "sebastian/code-unit-reverse-lookup": "2.0.3", - "sebastian/comparator": "4.0.8", - "sebastian/complexity": "2.0.3", - "sebastian/environment": "5.1.5", - "sebastian/exporter": "4.0.6", - "sebastian/global-state": "5.0.7", - "sebastian/lines-of-code": "1.0.4", - "sebastian/object-enumerator": "4.0.4", - "sebastian/object-reflector": "2.0.4", - "sebastian/recursion-context": "4.0.5", - "sebastian/resource-operations": "3.0.4", - "sebastian/type": "3.2.1", - "sebastian/version": "3.0.2", + "sebastian/cli-parser": "2.0.1", + "sebastian/code-unit": "2.0.0", + "sebastian/code-unit-reverse-lookup": "3.0.0", + "sebastian/comparator": "5.0.1", + "sebastian/complexity": "3.2.0", + "sebastian/environment": "6.1.0", + "sebastian/exporter": "5.1.2", + "sebastian/global-state": "6.0.2", + "sebastian/lines-of-code": "2.0.2", + "sebastian/object-enumerator": "5.0.0", + "sebastian/object-reflector": "3.0.0", + "sebastian/recursion-context": "5.0.0", + "sebastian/type": "4.0.0", + "sebastian/version": "4.0.1", "seld/jsonlint": "1.10.2", "seld/phar-utils": "1.2.1", "seld/signal-handler": "2.0.2", @@ -84,7 +83,6 @@ "symfony/css-selector": "v7.0.3", "symfony/dom-crawler": "v7.0.4", "symfony/lock": "v7.0.6", - "symfony/phpunit-bridge": "v7.0.6", "theseer/tokenizer": "1.2.3", "webflo/drupal-finder": "1.2.2", "webmozart/assert": "1.11.0" diff --git a/composer/Plugin/VendorHardening/Config.php b/composer/Plugin/VendorHardening/Config.php index 3db5c3372a3b..411425b14f53 100644 --- a/composer/Plugin/VendorHardening/Config.php +++ b/composer/Plugin/VendorHardening/Config.php @@ -70,7 +70,6 @@ class Config { 'symfony/event-dispatcher' => ['Tests'], 'symfony/http-foundation' => ['Tests'], 'symfony/http-kernel' => ['Tests'], - 'symfony/phpunit-bridge' => ['Tests'], 'symfony/process' => ['Tests'], 'symfony/psr-http-message-bridge' => ['Tests'], 'symfony/routing' => ['Tests'], diff --git a/core/.deprecation-ignore.txt b/core/.deprecation-ignore.txt index 92d3da6177c9..ec42944605b3 100644 --- a/core/.deprecation-ignore.txt +++ b/core/.deprecation-ignore.txt @@ -3,7 +3,6 @@ # See https://www.drupal.org/node/3285162 for more details. %The "Symfony\\Component\\Validator\\Context\\ExecutionContextInterface::.*\(\)" method is considered internal Used by the validator engine\. (Should not be called by user\W+code\. )?It may change without further notice\. You should not extend it from "[^"]+"\.% -%The "PHPUnit\\Framework\\TestCase::addWarning\(\)" method is considered internal% # Skip some dependencies' DebugClassLoader forward compatibility warnings. %Method "Behat\\[^"]+" might add "[^"]+" as a native return type declaration in the future. Do the same in (child class|implementation) "[^"]+" now to avoid errors or add an explicit @return annotation to suppress this message% @@ -17,16 +16,13 @@ %Method "Twig\\TokenParser\\TokenParserInterface::[^"]+" might add "[^"]+" as a native return type declaration in the future. Do the same in (child class|implementation) "[^"]+" now to avoid errors or add an explicit @return annotation to suppress this message% %Method "WebDriver\\Service\\CurlServiceInterface::[^"]+" might add "[^"]+" as a native return type declaration in the future. Do the same in implementation "[^"]+" now to avoid errors or add an explicit @return annotation to suppress this message% +# Indirect deprecations. These are not in Drupal's remit to fix, but it is +# worth keeping track of dependencies' issues. +%Method "[^"]+" might add "[^"]+" as a native return type declaration in the future. Do the same in implementation "org\\bovigo\\vfs\\[^"]+" now to avoid errors or add an explicit @return annotation to suppress this message% + # The following deprecation is listed for Twig 2 compatibility when unit # 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"\.% -# PHPUnit 9. -%"PHPUnit\\Framework\\TestListener".*is deprecated% -%"PHPUnit\\Framework\\TestListenerDefaultImplementation".*is deprecated% -%"PHPUnit\\Framework\\TestSuite".*is considered internal% -%"PHPUnit\\TextUI\\DefaultResultPrinter".*is considered internal% - -# PHPUnit 9.6. -%Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10% -%Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10% +# PHPUnit 10. +%The "PHPUnit\\Framework\\TestCase::__construct\(\)" method is considered internal.*You should not extend it from "Drupal\\[^"]+"% diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php index ab6639d657d2..609ee106deb5 100644 --- a/core/.phpstan-baseline.php +++ b/core/.phpstan-baseline.php @@ -841,6 +841,11 @@ 'count' => 1, 'path' => __DIR__ . '/modules/filter/src/Plugin/Filter/FilterHtml.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/help/tests/src/Unit/HelpTopicTwigTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\history\\\\Plugin\\\\views\\\\field\\\\HistoryUserTimestamp\\:\\:render\\(\\) should return Drupal\\\\Component\\\\Render\\\\MarkupInterface\\|string but return statement is missing\\.$#', 'count' => 1, @@ -1106,6 +1111,11 @@ 'count' => 1, 'path' => __DIR__ . '/modules/migrate/tests/src/Kernel/MigrateTestBase.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/migrate/tests/src/Unit/MigrateExecutableTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Variable \\$sub_process_plugins might not be defined\\.$#', 'count' => 2, @@ -1356,6 +1366,11 @@ 'count' => 1, 'path' => __DIR__ . '/modules/serialization/src/Normalizer/EntityNormalizer.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/serialization/tests/src/Unit/Normalizer/NormalizerBaseTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Method Drupal\\\\shortcut\\\\Form\\\\SetCustomize\\:\\:save\\(\\) should return int but return statement is missing\\.$#', 'count' => 1, @@ -1838,6 +1853,36 @@ 'count' => 2, 'path' => __DIR__ . '/modules/views/tests/src/Kernel/Plugin/StyleTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 5, + 'path' => __DIR__ . '/modules/views/tests/src/Unit/Plugin/Derivative/ViewsLocalTaskTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/modules/views/tests/src/Unit/Plugin/argument_validator/EntityTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/views/tests/src/Unit/Plugin/display/PathPluginBaseTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/views/tests/src/Unit/Plugin/pager/PagerPluginBaseTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/views/tests/src/Unit/Plugin/pager/SqlBaseTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/modules/views/tests/src/Unit/Plugin/views/field/EntityOperationsUnitTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Variable \\$relationship_handler in empty\\(\\) always exists and is not falsy\\.$#', 'count' => 1, @@ -1963,6 +2008,11 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Variable \\$found might not be defined\\.$#', 'count' => 1, @@ -1993,6 +2043,16 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/tests/Drupal/KernelTests/Core/Installer/InstallerRedirectTraitTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/KernelTests/Core/Installer/InstallerRedirectTraitTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Variable \\$value in isset\\(\\) always exists and is not nullable\\.$#', 'count' => 1, @@ -2074,6 +2134,16 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/Tests/Component/Plugin/Factory/ReflectionFactoryTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 4, + 'path' => __DIR__ . '/tests/Drupal/Tests/Component/Plugin/PluginBaseTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/tests/Drupal/Tests/Component/Plugin/PluginManagerBaseTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Missing cache backend declaration for performance\\.$#', 'count' => 1, @@ -2084,6 +2154,51 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/Tests/Composer/ComposerTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Controller/ControllerBaseTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityFormTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityStorageBaseTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Trying to mock an undefined method getRevisionId\\(\\) on class Drupal\\\\Tests\\\\Core\\\\Entity\\\\UrlTestEntity\\.$#', 'count' => 1, @@ -2094,12 +2209,27 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\.$#', + 'count' => 3, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Call to deprecated method getConfig\\(\\) of class GuzzleHttp\\\\Client\\: Client\\:\\:getConfig will be removed in guzzlehttp/guzzle\\:8\\.0\\.$#', 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Http/ClientFactoryTest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Call to deprecated method getMockForAbstractClass\\(\\) of class PHPUnit\\\\Framework\\\\TestCase\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/tests/Drupal/Tests/Core/Lock/LockBackendAbstractTest.php', +]; $ignoreErrors[] = [ 'message' => '#^Call to method getDefinitions\\(\\) on an unknown class Drupal\\\\Core\\\\Plugin\\\\CategorizingPluginManagerTrait\\.$#', 'count' => 1, @@ -2146,51 +2276,5 @@ 'count' => 1, 'path' => __DIR__ . '/tests/Drupal/Tests/DrupalTestBrowser.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Class Drupal\\\\Tests\\\\Listeners\\\\DrupalListener implements deprecated interface PHPUnit\\\\Framework\\\\TestListener\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/Drupal/Tests/Listeners/DrupalListener.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Usage of deprecated trait PHPUnit\\\\Framework\\\\TestListenerDefaultImplementation in class Drupal\\\\Tests\\\\Listeners\\\\DrupalListener\\: -The `TestListener` interface is deprecated$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/Drupal/Tests/Listeners/DrupalListener.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Class Drupal\\\\Tests\\\\TestSuites\\\\BuildTestSuite extends deprecated class Drupal\\\\Tests\\\\TestSuites\\\\TestSuiteBase\\: -in drupal\\:10\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. There is no - replacement and test discovery will be handled differently in PHPUnit 10\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/TestSuites/BuildTestSuite.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Class Drupal\\\\Tests\\\\TestSuites\\\\FunctionalJavascriptTestSuite extends deprecated class Drupal\\\\Tests\\\\TestSuites\\\\TestSuiteBase\\: -in drupal\\:10\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. There is no - replacement and test discovery will be handled differently in PHPUnit 10\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/TestSuites/FunctionalJavascriptTestSuite.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Class Drupal\\\\Tests\\\\TestSuites\\\\FunctionalTestSuite extends deprecated class Drupal\\\\Tests\\\\TestSuites\\\\TestSuiteBase\\: -in drupal\\:10\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. There is no - replacement and test discovery will be handled differently in PHPUnit 10\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/TestSuites/FunctionalTestSuite.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Class Drupal\\\\Tests\\\\TestSuites\\\\KernelTestSuite extends deprecated class Drupal\\\\Tests\\\\TestSuites\\\\TestSuiteBase\\: -in drupal\\:10\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. There is no - replacement and test discovery will be handled differently in PHPUnit 10\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/TestSuites/KernelTestSuite.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Class Drupal\\\\Tests\\\\TestSuites\\\\UnitTestSuite extends deprecated class Drupal\\\\Tests\\\\TestSuites\\\\TestSuiteBase\\: -in drupal\\:10\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. There is no - replacement and test discovery will be handled differently in PHPUnit 10\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/TestSuites/UnitTestSuite.php', -]; return ['parameters' => ['ignoreErrors' => $ignoreErrors]]; diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index cf632ae73374..881d695b4aee 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -218,6 +218,9 @@ function drupal_valid_test_ua($new_prefix = NULL) { /** * Generates a user agent string with a HMAC and timestamp for tests. + * + * @return string|null + * The user agent, or NULL on failure. */ function drupal_generate_test_ua($prefix) { static $key, $last_prefix; @@ -242,7 +245,9 @@ function drupal_generate_test_ua($prefix) { // Generate and save a new hash salt for a test run. // Consumed by drupal_valid_test_ua() before settings.php is loaded. $private_key = Crypt::randomBytesBase64(55); - file_put_contents($key_file, $private_key); + if (!@file_put_contents($key_file, $private_key)) { + return NULL; + } } // The file properties add more entropy not easily accessible to others. $key = $private_key . filectime(__FILE__) . fileinode(__FILE__); diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php index 4e87201fba88..aceb5fd12827 100644 --- a/core/lib/Drupal/Core/Database/Database.php +++ b/core/lib/Drupal/Core/Database/Database.php @@ -457,6 +457,13 @@ public static function closeConnection($target = NULL, $key = NULL) { } unset(self::$connections[$key]); } + + // When last connection for $key is closed, we also stop any active + // logging. + if (empty(self::$connections[$key])) { + unset(self::$logs[$key]); + } + // Force garbage collection to run. This ensures that client connection // objects and results in the connection being closed are destroyed. gc_collect_cycles(); diff --git a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php index 4fbfff01cdd0..b0e950868f7c 100644 --- a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php +++ b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php @@ -23,8 +23,8 @@ public function __invoke() { // database prefix were stored statically in a file or database variable. return function ($handler) { return function (RequestInterface $request, array $options) use ($handler) { - if ($test_prefix = drupal_valid_test_ua()) { - $request = $request->withHeader('User-Agent', drupal_generate_test_ua($test_prefix)); + if ($user_agent = drupal_generate_test_ua(drupal_valid_test_ua())) { + $request = $request->withHeader('User-Agent', $user_agent); } return $handler($request, $options) ->then(function (ResponseInterface $response) { @@ -47,7 +47,7 @@ public function __invoke() { if ($parameters[1] === 'User deprecated function') { // Fire the same deprecation message to allow it to be // collected by - // \Symfony\Bridge\PhpUnit\DeprecationErrorHandler::collectDeprecations(). + // \Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler::collectActualDeprecation(). // phpcs:ignore Drupal.Semantics.FunctionTriggerError @trigger_error((string) $parameters[0], E_USER_DEPRECATED); } diff --git a/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php b/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php index f238cf4d3456..e9103ceb9f47 100644 --- a/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php +++ b/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php @@ -4,6 +4,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\Process; @@ -18,7 +19,7 @@ * * @code * $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); - * $results = $runner->execute($test_run, $test_list['phpunit']); + * $results = $runner->execute($test_run, $test_class_name); * @endcode * * @internal @@ -95,10 +96,9 @@ public function phpUnitCommand(): string { /** * Executes the PHPUnit command. * - * @param string[] $unescaped_test_classnames - * An array of test class names, including full namespaces, to be passed as - * a regular expression to PHPUnit's --filter option. - * @param string $phpunit_file + * @param string $test_class_name + * A fully qualified test class name. + * @param string $log_junit_file_path * A filepath to use for PHPUnit's --log-junit option. * @param int $status * (optional) The exit status code of the PHPUnit process will be assigned @@ -109,7 +109,7 @@ public function phpUnitCommand(): string { * * @internal */ - public function runCommand(array $unescaped_test_classnames, string $phpunit_file, int &$status = NULL, array &$output = NULL): void { + protected function runCommand(string $test_class_name, string $log_junit_file_path, int &$status = NULL, array &$output = NULL): void { global $base_url; // Setup an environment variable containing the database connection so that // functional tests can connect to the database. @@ -127,30 +127,24 @@ public function runCommand(array $unescaped_test_classnames, string $phpunit_fil } $phpunit_bin = $this->phpUnitCommand(); + // Build the command line for the PHPUnit CLI invocation. $command = [ $phpunit_bin, '--log-junit', - $phpunit_file, + $log_junit_file_path, ]; - // Optimized for running a single test. - if (count($unescaped_test_classnames) == 1) { - $class = new \ReflectionClass($unescaped_test_classnames[0]); - $command[] = $class->getFileName(); + // If the deprecation handler bridge is active, we need to fail when there + // are deprecations that get reported (i.e. not ignored or expected). + if (DeprecationHandler::getConfiguration() !== FALSE) { + $command[] = '--fail-on-deprecation'; } - else { - // Double escape namespaces so they'll work in a regexp. - $escaped_test_classnames = array_map(function ($class) { - return addslashes($class); - }, $unescaped_test_classnames); - $filter_string = implode("|", $escaped_test_classnames); - $command = array_merge($command, [ - '--filter', - $filter_string, - ]); - } + // Add to the command the file containing the test class to be run. + $reflectedClass = new \ReflectionClass($test_class_name); + $command[] = $reflectedClass->getFileName(); + // Invoke PHPUnit CLI with the built command line. $process = new Process($command, \Drupal::root() . "/core", $process_environment_variables); $process->setTimeout(NULL); $process->run(); @@ -163,9 +157,8 @@ public function runCommand(array $unescaped_test_classnames, string $phpunit_fil * * @param \Drupal\Core\Test\TestRun $test_run * The test run object. - * @param string[] $unescaped_test_classnames - * An array of test class names, including full namespaces, to be passed as - * a regular expression to PHPUnit's --filter option. + * @param string $test_class_name + * A fully qualified test class name. * @param int $status * (optional) The exit status code of the PHPUnit process will be assigned * to this variable. @@ -176,25 +169,25 @@ public function runCommand(array $unescaped_test_classnames, string $phpunit_fil * * @internal */ - public function execute(TestRun $test_run, array $unescaped_test_classnames, int &$status = NULL): array { - $phpunit_file = $this->xmlLogFilePath($test_run->id()); + public function execute(TestRun $test_run, string $test_class_name, int &$status = NULL): array { + $log_junit_file_path = $this->xmlLogFilePath($test_run->id()); // Store output from our test run. $output = []; - $this->runCommand($unescaped_test_classnames, $phpunit_file, $status, $output); + $this->runCommand($test_class_name, $log_junit_file_path, $status, $output); if ($status == TestStatus::PASS) { - return JUnitConverter::xmlToRows($test_run->id(), $phpunit_file); + return JUnitConverter::xmlToRows($test_run->id(), $log_junit_file_path); } return [ [ 'test_id' => $test_run->id(), - 'test_class' => implode(",", $unescaped_test_classnames), + 'test_class' => $test_class_name, 'status' => TestStatus::label($status), 'message' => 'PHPUnit Test failed to complete; Error: ' . implode("\n", $output), 'message_group' => 'Other', - 'function' => implode(",", $unescaped_test_classnames), + 'function' => $test_class_name, 'line' => '0', - 'file' => $phpunit_file, + 'file' => $log_junit_file_path, ], ]; } diff --git a/core/lib/Drupal/Core/Test/TestDiscovery.php b/core/lib/Drupal/Core/Test/TestDiscovery.php index 9112d6e7abb1..f63510a0d1a1 100644 --- a/core/lib/Drupal/Core/Test/TestDiscovery.php +++ b/core/lib/Drupal/Core/Test/TestDiscovery.php @@ -7,7 +7,6 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Test\Exception\MissingGroupException; -use Drupal\TestTools\PhpUnitCompatibility\ClassWriter; /** * Discovers available tests. @@ -108,10 +107,6 @@ 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; } diff --git a/core/lib/Drupal/Core/Test/TestRunnerKernel.php b/core/lib/Drupal/Core/Test/TestRunnerKernel.php index e1729f825010..e5ece5c07a2b 100644 --- a/core/lib/Drupal/Core/Test/TestRunnerKernel.php +++ b/core/lib/Drupal/Core/Test/TestRunnerKernel.php @@ -5,6 +5,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Extension\Extension; use Drupal\Core\Site\Settings; +use Drupal\Core\Utility\Error; use Symfony\Component\HttpFoundation\Request; /** @@ -60,7 +61,10 @@ public function boot() { // Remove Drupal's error/exception handlers; they are designed for HTML // and there is no storage nor a (watchdog) logger here. - restore_error_handler(); + $currentErrorHandler = Error::currentErrorHandler(); + if (is_string($currentErrorHandler) && $currentErrorHandler === '_drupal_error_handler') { + restore_error_handler(); + } restore_exception_handler(); // In addition, ensure that PHP errors are not hidden away in logs. diff --git a/core/lib/Drupal/Core/Test/TestSetupTrait.php b/core/lib/Drupal/Core/Test/TestSetupTrait.php index e1ff6eecd783..e3bb02f10bcd 100644 --- a/core/lib/Drupal/Core/Test/TestSetupTrait.php +++ b/core/lib/Drupal/Core/Test/TestSetupTrait.php @@ -170,6 +170,7 @@ protected function changeDatabasePrefix() { // prefixes of the test runner leak into the test. $connection_info[$target]['prefix'] = $value['prefix'] . $this->databasePrefix; } + Database::removeConnection('default'); Database::addConnectionInfo('default', 'default', $connection_info['default']); } } diff --git a/core/lib/Drupal/Core/Utility/Error.php b/core/lib/Drupal/Core/Utility/Error.php index f8a54e20a281..459af44d8c51 100644 --- a/core/lib/Drupal/Core/Utility/Error.php +++ b/core/lib/Drupal/Core/Utility/Error.php @@ -206,4 +206,16 @@ public static function formatBacktrace(array $backtrace) { return $return; } + /** + * Returns the current PHP error handler as a callable. + * + * @return callable|null + * The current error handler as a callable, or NULL if none is set. + */ + public static function currentErrorHandler(): ?callable { + $currentHandler = set_error_handler('var_dump'); + restore_error_handler(); + return $currentHandler; + } + } diff --git a/core/modules/language/tests/src/Unit/process/LanguageDomainsTest.php b/core/modules/language/tests/src/Unit/process/LanguageDomainsTest.php index 8f6c3105ec59..87f69db62352 100644 --- a/core/modules/language/tests/src/Unit/process/LanguageDomainsTest.php +++ b/core/modules/language/tests/src/Unit/process/LanguageDomainsTest.php @@ -13,11 +13,6 @@ */ class LanguageDomainsTest extends MigrateProcessTestCase { - /** - * {@inheritdoc} - */ - protected $backupGlobalsBlacklist = ['base_url']; - /** * {@inheritdoc} */ diff --git a/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php b/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php index 1233fd245426..248e57c74a30 100644 --- a/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php +++ b/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php @@ -118,7 +118,7 @@ protected function writeSettings(array $settings): void { public function testPsa(): void { $assert = $this->assertSession(); // Setup test PSA endpoint. - AdvisoryTestClientMiddleware::setTestEndpoint($this->workingEndpointMixed); + AdvisoryTestClientMiddleware::setTestEndpoint($this->workingEndpointMixed, TRUE); $mixed_advisory_links = [ 'Critical Release - SA-2019-02-19', 'Critical Release - PSA-Really Old', diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist index c616c8c179f6..4bfeb0086f62 100644 --- a/core/phpunit.xml.dist +++ b/core/phpunit.xml.dist @@ -1,5 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> - <!-- For how to customize PHPUnit configuration, see core/tests/README.md. --> <!-- TODO set checkForUnintentionallyCoveredCode="true" once https://www.drupal.org/node/2626832 is resolved. --> <!-- PHPUnit expects functional tests to be run with either a privileged user @@ -7,14 +6,18 @@ https://www.drupal.org/node/2116263 for details. --> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - bootstrap="tests/bootstrap.php" colors="true" + bootstrap="tests/bootstrap.php" + colors="true" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutOutputDuringTests="true" beStrictAboutChangesToGlobalState="true" failOnWarning="true" - printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter" + displayDetailsOnTestsThatTriggerErrors="true" + displayDetailsOnTestsThatTriggerWarnings="true" + displayDetailsOnTestsThatTriggerDeprecations="true" cacheResult="false" - xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" + cacheDirectory=".phpunit.cache"> <php> <!-- Set error reporting to E_ALL. --> <ini name="error_reporting" value="32767"/> @@ -26,6 +29,13 @@ <env name="SIMPLETEST_DB" value=""/> <!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output --> <env name="BROWSERTEST_OUTPUT_DIRECTORY" value=""/> + <!-- To avoid overcrowding the output in CI environments, browser tests + will not print the individual links in the test run report by default. + The output in Drupal testing environment is saved as an artifact that + can be browsed or downloaded from Gitlab. However, if you need to + print the individual links locally you can set the + BROWSERTEST_OUTPUT_VERBOSE environment variable to "true". --> + <!-- <env name="BROWSERTEST_OUTPUT_VERBOSE" value="true"/> --> <!-- By default, browser tests will output links that use the base URL set in SIMPLETEST_BASE_URL. However, if your SIMPLETEST_BASE_URL is an internal path (such as may be the case in a virtual or Docker-based environment), @@ -33,11 +43,8 @@ reachable from your host machine here. This will allow you to follow them directly and view the output. --> <env name="BROWSERTEST_OUTPUT_BASE_URL" value=""/> - - <!-- Deprecation testing is managed through Symfony's PHPUnit Bridge. - The environment variable SYMFONY_DEPRECATIONS_HELPER is used to configure + <!-- The environment variable SYMFONY_DEPRECATIONS_HELPER is used to configure the behavior of the deprecation tests. - See https://symfony.com/doc/current/components/phpunit_bridge.html#configuration Drupal core's testing framework is setting this variable to its defaults. Projects with their own requirements need to manage this variable explicitly. @@ -46,43 +53,61 @@ <!-- <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> --> <!-- Deprecation errors can be selectively ignored by specifying a file of regular expression patterns for exclusion. - See https://symfony.com/doc/current/components/phpunit_bridge.html#ignoring-deprecations Uncomment the line below to specify a custom deprecations ignore file. NOTE: it may be required to specify the full path to the file to run tests correctly. --> <!-- <env name="SYMFONY_DEPRECATIONS_HELPER" value="ignoreFile=.deprecation-ignore.txt"/> --> - <!-- Example for changing the driver class for mink tests MINK_DRIVER_CLASS value: 'Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver' --> - <env name="MINK_DRIVER_CLASS" value=''/> + <env name="MINK_DRIVER_CLASS" value=""/> <!-- Example for changing the driver args to mink tests MINK_DRIVER_ARGS value: '["http://127.0.0.1:8510"]' --> - <env name="MINK_DRIVER_ARGS" value=''/> + <env name="MINK_DRIVER_ARGS" value=""/> <!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '["chrome", { "goog:chromeOptions": { "w3c": false } }, "http://localhost:4444/wd/hub"]' For using the Firefox browser, replace "chrome" with "firefox" --> - <env name="MINK_DRIVER_ARGS_WEBDRIVER" value=''/> + <env name="MINK_DRIVER_ARGS_WEBDRIVER" value=""/> </php> <testsuites> <testsuite name="unit"> - <file>./tests/TestSuites/UnitTestSuite.php</file> + <directory>tests/Drupal/Tests</directory> + <directory>modules/**/tests/src/Unit</directory> + <directory>profiles/**/tests/src/Unit</directory> + <directory>themes/**/tests/src/Unit</directory> + <directory>../modules/**/tests/src/Unit</directory> + <directory>../profiles/**/tests/src/Unit</directory> + <directory>../themes/**/tests/src/Unit</directory> </testsuite> <testsuite name="kernel"> - <file>./tests/TestSuites/KernelTestSuite.php</file> + <directory>tests/Drupal/KernelTests</directory> + <directory>modules/**/tests/src/Kernel</directory> + <directory>profiles/**/tests/src/Kernel</directory> + <directory>themes/**/tests/src/Kernel</directory> + <directory>../modules/**/tests/src/Kernel</directory> + <directory>../profiles/**/tests/src/Kernel</directory> + <directory>../themes/**/tests/src/Kernel</directory> </testsuite> <testsuite name="functional"> - <file>./tests/TestSuites/FunctionalTestSuite.php</file> + <directory>tests/Drupal/FunctionalTests</directory> + <directory>modules/**/tests/src/Functional</directory> + <directory>profiles/**/tests/src/Functional</directory> + <directory>themes/**/tests/src/Functional</directory> + <directory>../modules/**/tests/src/Functional</directory> + <directory>../profiles/**/tests/src/Functional</directory> + <directory>../themes/**/tests/src/Functional</directory> </testsuite> <testsuite name="functional-javascript"> - <file>./tests/TestSuites/FunctionalJavascriptTestSuite.php</file> + <directory>tests/Drupal/FunctionalJavascriptTests</directory> + <directory>modules/**/tests/src/FunctionalJavascript</directory> + <directory>profiles/**/tests/src/FunctionalJavascript</directory> + <directory>themes/**/tests/src/FunctionalJavascript</directory> + <directory>../modules/**/tests/src/FunctionalJavascript</directory> + <directory>../profiles/**/tests/src/FunctionalJavascript</directory> + <directory>../themes/**/tests/src/FunctionalJavascript</directory> </testsuite> <testsuite name="build"> - <file>./tests/TestSuites/BuildTestSuite.php</file> + <directory>tests/Drupal/BuildTests</directory> </testsuite> </testsuites> - <listeners> - <listener class="\Drupal\Tests\Listeners\DrupalListener"> - </listener> - </listeners> <!-- Settings for coverage reports. --> - <coverage> + <source ignoreSuppressionOfDeprecations="true"> <include> <directory>./includes</directory> <directory>./lib</directory> @@ -101,5 +126,5 @@ <directory suffix=".api.php">./modules/**</directory> <directory suffix=".api.php">../modules/**</directory> </exclude> - </coverage> + </source> </phpunit> diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 56ad3e553f4d..0672c44d85f7 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -25,7 +25,6 @@ use Drupal\Core\Test\TestRunnerKernel; use Drupal\Core\Test\TestRunResultsStorageInterface; use Drupal\Core\Test\TestDiscovery; -use Drupal\TestTools\PhpUnitCompatibility\ClassWriter; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\Version; use Symfony\Component\Console\Output\ConsoleOutput; @@ -511,7 +510,6 @@ 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'])) { @@ -817,13 +815,8 @@ function simpletest_script_execute_batch(TestRunResultsStorageInterface $test_ru * Run a PHPUnit-based test. */ function simpletest_script_run_phpunit(TestRun $test_run, $class) { - $reflection = new \ReflectionClass($class); - if ($reflection->hasProperty('runLimit')) { - set_time_limit($reflection->getStaticPropertyValue('runLimit')); - } - $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); - $results = $runner->execute($test_run, [$class], $status); + $results = $runner->execute($test_run, $class, $status); $runner->processPhpUnitResults($test_run, $results); $summaries = $runner->summarizeResults($results); diff --git a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php index f0cb552acf36..afdd7fcbcb55 100644 --- a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php +++ b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php @@ -11,7 +11,6 @@ use Drupal\Component\FileSystem\FileSystem as DrupalFilesystem; use Drupal\Tests\DrupalTestBrowser; use Drupal\Tests\PhpUnitCompatibilityTrait; -use Drupal\Tests\Traits\PhpUnitWarnings; use Drupal\TestTools\Extension\RequiresComposerTrait; use PHPUnit\Framework\TestCase; use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; @@ -55,7 +54,6 @@ abstract class BuildTestBase extends TestCase { use RequiresComposerTrait; - use PhpUnitWarnings; use PhpUnitCompatibilityTrait; /** diff --git a/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php b/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php index c55ae1580ebd..4803397db446 100644 --- a/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php +++ b/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php @@ -92,6 +92,7 @@ public function testCopyCodebaseExclude() { /** @var \PHPUnit\Framework\MockObject\MockBuilder|\Drupal\BuildTests\Framework\BuildTestBase $base */ $base = $this->getMockBuilder(BuildTestBase::class) ->onlyMethods(['getDrupalRoot', 'getComposerRoot']) + ->setConstructorArgs(['test']) ->getMockForAbstractClass(); $base->expects($this->exactly(1)) ->method('getDrupalRoot') @@ -170,6 +171,7 @@ public function testCopyCodebaseDocRoot() { /** @var \PHPUnit\Framework\MockObject\MockBuilder|\Drupal\BuildTests\Framework\BuildTestBase $base */ $base = $this->getMockBuilder(BuildTestBase::class) ->onlyMethods(['getDrupalRoot', 'getComposerRoot']) + ->setConstructorArgs(['test']) ->getMockForAbstractClass(); $base->expects($this->exactly(3)) ->method('getDrupalRoot') diff --git a/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php index 9ec27626137c..d8f0b3d2de65 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php @@ -7,7 +7,6 @@ use Behat\Mink\Exception\DriverException; use Drupal\Component\Utility\UrlHelper; use Drupal\Tests\BrowserTestBase; -use PHPUnit\Runner\BaseTestRunner; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -96,11 +95,6 @@ protected function initFrontPage() { */ protected function tearDown(): void { if ($this->mink) { - $status = $this->getStatus(); - if ($status === BaseTestRunner::STATUS_ERROR || $status === BaseTestRunner::STATUS_WARNING || $status === BaseTestRunner::STATUS_FAILURE) { - // Ensure we capture the output at point of failure. - @$this->htmlOutput(); - } // Wait for all requests to finish. It is possible that an AJAX request is // still on-going. $result = $this->getSession()->wait(5000, 'window.drupalActiveXhrCount === 0 || typeof window.drupalActiveXhrCount === "undefined"'); diff --git a/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php b/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php index f978361cf4c2..a674fa7a8a5f 100644 --- a/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php +++ b/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php @@ -8,7 +8,7 @@ use Drupal\Tests\BrowserTestBase; /** - * Tests Drupal's integration with Symfony PHPUnit Bridge. + * Tests Drupal's extension to manage code deprecation. * * @group Test * @group legacy diff --git a/core/tests/Drupal/FunctionalTests/Installer/SuperUserAccessInstallTest.php b/core/tests/Drupal/FunctionalTests/Installer/SuperUserAccessInstallTest.php index a327866f16a6..dd99caf01fd8 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/SuperUserAccessInstallTest.php +++ b/core/tests/Drupal/FunctionalTests/Installer/SuperUserAccessInstallTest.php @@ -46,10 +46,10 @@ protected function prepareEnvironment() { mkdir($path, 0777, TRUE); file_put_contents("$path/superuser.info.yml", Yaml::encode($info)); - file_put_contents("$path/superuser.install", $this->getProvidedData()['install_code']); + file_put_contents("$path/superuser.install", $this->providedData()['install_code']); $services = Yaml::decode(file_get_contents(DRUPAL_ROOT . '/sites/default/default.services.yml')); - $services['parameters']['security.enable_super_user'] = $this->getProvidedData()['super_user_policy']; + $services['parameters']['security.enable_super_user'] = $this->providedData()['super_user_policy']; file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/services.yml', Yaml::encode($services)); } @@ -57,7 +57,7 @@ protected function prepareEnvironment() { * {@inheritdoc} */ protected function setUpSite() { - if ($this->getProvidedData()['super_user_policy'] === FALSE && empty($this->getProvidedData()['expected_roles'])) { + if ($this->providedData()['super_user_policy'] === FALSE && empty($this->providedData()['expected_roles'])) { $this->assertSession()->pageTextContains('Site account'); $this->assertSession()->pageTextNotContains('Site maintenance account'); } diff --git a/core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputTest.php b/core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputTest.php index ae88182aa68f..53c3d130b64b 100644 --- a/core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputTest.php +++ b/core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputTest.php @@ -26,8 +26,15 @@ class FunctionalTestDebugHtmlOutputTest extends BrowserTestBase { * running a functional test are met. */ public function testFunctionalTestDebugHtmlOutput(): void { - // Test with the specified output directory. - $process = Process::fromShellCommandline('vendor/bin/phpunit --configuration core --verbose core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputHelperTest.php'); + $command = [ + 'vendor/bin/phpunit', + '--configuration', + 'core', + 'core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputHelperTest.php', + ]; + + // Test with the default output directory, specified by BROWSERTEST_OUTPUT_DIRECTORY. + $process = new Process($command); $process->setWorkingDirectory($this->root) ->setTimeout(300) ->setIdleTimeout(300); @@ -36,15 +43,31 @@ public function testFunctionalTestDebugHtmlOutput(): void { 'COMMAND: ' . $process->getCommandLine() . "\n" . 'OUTPUT: ' . $process->getOutput() . "\n" . 'ERROR: ' . $process->getErrorOutput() . "\n"); - $this->assertStringContainsString('HTML output was generated', $process->getOutput()); + $this->assertMatchesRegularExpression('/HTML output was generated, \d+ page\(s\)\./m', $process->getOutput()); + + // Test with verbose output. + $process = new Process($command); + $process->setWorkingDirectory($this->root) + ->setTimeout(300) + ->setIdleTimeout(300); + $process->run(NULL, [ + 'BROWSERTEST_OUTPUT_VERBOSE' => '1', + ]); + $this->assertEquals(0, $process->getExitCode(), + 'COMMAND: ' . $process->getCommandLine() . "\n" . + 'OUTPUT: ' . $process->getOutput() . "\n" . + 'ERROR: ' . $process->getErrorOutput() . "\n"); + $this->assertStringContainsString('HTML output was generated.', $process->getOutput()); $this->assertStringContainsString('Drupal_FunctionalTests_Test_FunctionalTestDebugHtmlOutputHelperTest', $process->getOutput()); // Test with a wrong output directory. - $process = Process::fromShellCommandline('vendor/bin/phpunit --configuration core --verbose core/tests/Drupal/FunctionalTests/Test/FunctionalTestDebugHtmlOutputHelperTest.php'); + $process = new Process($command); $process->setWorkingDirectory($this->root) ->setTimeout(300) ->setIdleTimeout(300); - $process->run(NULL, ['BROWSERTEST_OUTPUT_DIRECTORY' => 'can_we_assume_that_a_subdirectory_with_this_name_does_not_exist']); + $process->run(NULL, [ + 'BROWSERTEST_OUTPUT_DIRECTORY' => 'can_we_assume_that_a_subdirectory_with_this_name_does_not_exist', + ]); $this->assertEquals(0, $process->getExitCode(), 'COMMAND: ' . $process->getCommandLine() . "\n" . 'OUTPUT: ' . $process->getOutput() . "\n" . diff --git a/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelSiteTest.php b/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelSiteTest.php index be7bf3c28fe9..c58461ea29ee 100644 --- a/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelSiteTest.php +++ b/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelSiteTest.php @@ -38,6 +38,7 @@ class: Drupal\Component\Datetime\Time # Add a new service. site.service.yml: class: $class + arguments: ['test'] # Swap out a core service. cache.backend.database: class: Drupal\Core\Cache\MemoryBackendFactory diff --git a/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelTest.php b/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelTest.php index ec94d2fc52ef..e3d3b2f5d2ee 100644 --- a/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelTest.php +++ b/core/tests/Drupal/KernelTests/Core/DrupalKernel/DrupalKernelTest.php @@ -7,6 +7,7 @@ use Composer\Autoload\ClassLoader; use Drupal\Core\DrupalKernel; use Drupal\Core\DrupalKernelInterface; +use Drupal\Core\Utility\Error; use Drupal\KernelTests\KernelTestBase; use org\bovigo\vfs\vfsStream; use Prophecy\Argument; @@ -22,6 +23,17 @@ */ class DrupalKernelTest extends KernelTestBase { + /** + * {@inheritdoc} + */ + protected function tearDown(): void { + $currentErrorHandler = Error::currentErrorHandler(); + if (is_string($currentErrorHandler) && $currentErrorHandler === '_drupal_error_handler') { + restore_error_handler(); + } + parent::tearDown(); + } + /** * {@inheritdoc} */ diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index 5f24dc24e575..0ccee09bbe52 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -23,8 +23,8 @@ use Drupal\Tests\RandomGeneratorTrait; use Drupal\Tests\PhpUnitCompatibilityTrait; use Drupal\Tests\TestRequirementsTrait; -use Drupal\Tests\Traits\PhpUnitWarnings; use Drupal\TestTools\Comparator\MarkupInterfaceComparator; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use Drupal\TestTools\Extension\SchemaInspector; use Drupal\TestTools\TestVarDumper; use PHPUnit\Framework\Exception; @@ -38,7 +38,6 @@ use Drupal\Core\Routing\RouteObjectInterface; use Symfony\Component\Routing\Route; use Symfony\Component\VarDumper\VarDumper; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** * Base class for functional integration tests. @@ -91,7 +90,6 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa use ConfigTestTrait; use ExtensionListTestTrait; use TestRequirementsTrait; - use PhpUnitWarnings; use PhpUnitCompatibilityTrait; use ProphecyTrait; use ExpectDeprecationTrait; @@ -99,30 +97,19 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa /** * {@inheritdoc} * - * Back up and restore any global variables that may be changed by tests. + * Back up and restore static class properties that may be changed by tests. * * @see self::runTestInSeparateProcess */ - protected $backupGlobals = TRUE; - - /** - * {@inheritdoc} - * - * Kernel tests are run in separate processes because they allow autoloading - * of code from extensions. Running the test in a separate process isolates - * this behavior from other tests. Subclasses should not override this - * property. - */ - protected $runTestInSeparateProcess = TRUE; + protected $backupStaticAttributes = TRUE; /** * {@inheritdoc} - * - * Back up and restore static class properties that may be changed by tests. - * - * @see self::runTestInSeparateProcess */ - protected $backupStaticAttributes = TRUE; + public function __construct(string $name) { + parent::__construct($name); + $this->setRunTestInSeparateProcess(TRUE); + } /** * {@inheritdoc} @@ -141,16 +128,6 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa 'Drupal\Core\Site\Settings' => ['instance'], ]; - /** - * {@inheritdoc} - * - * Do not forward any global state from the parent process to the processes - * that run the actual tests. - * - * @see self::runTestInSeparateProcess - */ - protected $preserveGlobalState = FALSE; - /** * @var \Composer\Autoload\Classloader */ diff --git a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php index fe71a5b2d1d5..55c27fa4c0ca 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php +++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php @@ -75,9 +75,6 @@ public function testSetUp() { $this->assertEquals($this, $GLOBALS['conf']['container_service_providers']['test']); - $GLOBALS['destroy-me'] = TRUE; - $this->assertArrayHasKey('destroy-me', $GLOBALS); - $database = $this->container->get('database'); $database->schema()->createTable('foo', [ 'fields' => [ @@ -98,8 +95,6 @@ public function testSetUp() { * @depends testSetUp */ public function testSetUpDoesNotLeak() { - $this->assertArrayNotHasKey('destroy-me', $GLOBALS); - // Ensure that we have a different database prefix. $schema = $this->container->get('database')->schema(); $this->assertFalse($schema->tableExists('foo')); diff --git a/core/tests/Drupal/TestTools/Comparator/MarkupInterfaceComparator.php b/core/tests/Drupal/TestTools/Comparator/MarkupInterfaceComparator.php index 174f2447ad5a..50e60f5dca98 100644 --- a/core/tests/Drupal/TestTools/Comparator/MarkupInterfaceComparator.php +++ b/core/tests/Drupal/TestTools/Comparator/MarkupInterfaceComparator.php @@ -42,7 +42,7 @@ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = F throw new \RuntimeException("Cannot compare markup between MarkupInterface objects and plain strings"); } } - $comparator = $this->factory->getComparatorFor($expected_safe_stripped, $actual_safe_stripped); + $comparator = $this->factory()->getComparatorFor($expected_safe_stripped, $actual_safe_stripped); $comparator->assertEquals($expected_safe_stripped, $actual_safe_stripped, $delta, $canonicalize, $ignoreCase); } diff --git a/core/tests/Drupal/TestTools/ErrorHandler/BootstrapErrorHandler.php b/core/tests/Drupal/TestTools/ErrorHandler/BootstrapErrorHandler.php new file mode 100644 index 000000000000..ab3d0c8a1c3c --- /dev/null +++ b/core/tests/Drupal/TestTools/ErrorHandler/BootstrapErrorHandler.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\ErrorHandler; + +use Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler; +use PHPUnit\Event\Code\NoTestCaseObjectOnCallStackException; +use PHPUnit\Runner\ErrorHandler as PhpUnitErrorHandler; + +/** + * Drupal's PHPUnit base error handler. + * + * This code works in coordination with DeprecationHandler. + * + * This error handler is registered during PHPUnit's runner bootstrap, and is + * essentially used to capture deprecations occurring before tests are run (for + * example, deprecations triggered by the DebugClassloader). When test runs + * are prepared, a test specific TestErrorHandler is activated instead. + * + * @see \Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler + * + * @internal + */ +final class BootstrapErrorHandler { + + /** + * @param \PHPUnit\Runner\ErrorHandler $phpUnitErrorHandler + * An instance of PHPUnit's runner own error handler. Any error not + * managed here will fall back to it. + */ + public function __construct( + private readonly PhpUnitErrorHandler $phpUnitErrorHandler, + ) { + } + + /** + * Executes when the object is called as a function. + * + * @param int $errorNumber + * The level of the error raised. + * @param string $errorString + * The error message. + * @param string $errorFile + * The filename that the error was raised in. + * @param int $errorLine + * The line number the error was raised at. + * + * @return bool + * TRUE to stop error handling, FALSE to let the normal error handler + * continue. + */ + public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool { + if (!DeprecationHandler::isEnabled()) { + throw new \RuntimeException(__METHOD__ . '() must not be called if the deprecation handler is not enabled.'); + } + + // We collect a deprecation no matter what. + if (E_USER_DEPRECATED === $errorNumber || E_DEPRECATED === $errorNumber) { + $prefix = (error_reporting() & $errorNumber) ? 'Unsilenced deprecation: ' : ''; + DeprecationHandler::collectActualDeprecation($prefix . $errorString); + } + + // If the deprecation handled is one of those in the ignore list, we keep + // running. + if ((E_USER_DEPRECATED === $errorNumber || E_DEPRECATED === $errorNumber) && DeprecationHandler::isIgnoredDeprecation($errorString)) { + return TRUE; + } + + // In all other cases (errors, warnings, deprecations to be reported), we + // fall back to PHPUnit's error handler, an instance of which was created + // when this error handler was created. + try { + call_user_func($this->phpUnitErrorHandler, $errorNumber, $errorString, $errorFile, $errorLine); + } + catch (NoTestCaseObjectOnCallStackException $e) { + // If we end up here, it's likely because a test's processing has + // finished already and we are processing an error that occurred while + // dealing with STDOUT rewinding or truncating. Do nothing. + } + return TRUE; + } + +} diff --git a/core/tests/Drupal/TestTools/ErrorHandler/TestErrorHandler.php b/core/tests/Drupal/TestTools/ErrorHandler/TestErrorHandler.php new file mode 100644 index 000000000000..8ad1de3f634e --- /dev/null +++ b/core/tests/Drupal/TestTools/ErrorHandler/TestErrorHandler.php @@ -0,0 +1,75 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\ErrorHandler; + +use Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler; +use PHPUnit\Framework\TestCase; + +/** + * Drupal's PHPUnit test level error handler. + * + * This code works in coordination with DeprecationHandler. + * + * This error handler is registered during the preparation of a PHPUnit's test, + * and is essentially used to capture deprecations occurring during test + * executions. When test runs are torn down, the more generic + * BootstrapErrorHandler is restored. + * + * @see \Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler + * + * @internal + */ +final class TestErrorHandler { + + /** + * @param \Drupal\TestTools\ErrorHandler\BootstrapErrorHandler $parentHandler + * The parent error handler. + * @param \PHPUnit\Framework\TestCase $testCase + * The test case being executed. + */ + public function __construct( + private readonly BootstrapErrorHandler $parentHandler, + private readonly TestCase $testCase, + ) { + } + + /** + * Executes when the object is called as a function. + * + * @param int $errorNumber + * The level of the error raised. + * @param string $errorString + * The error message. + * @param string $errorFile + * The filename that the error was raised in. + * @param int $errorLine + * The line number the error was raised at. + * + * @return bool + * TRUE to stop error handling, FALSE to let the normal error handler + * continue. + */ + public function __invoke(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool { + if (!DeprecationHandler::isEnabled()) { + throw new \RuntimeException(__METHOD__ . '() must not be called if the deprecation handler is not enabled.'); + } + + // We are within a test execution. If we have a deprecation and the test is + // a deprecation test, than we just collect the deprecation and return to + // execution, since deprecations are expected. + if ((E_USER_DEPRECATED === $errorNumber || E_DEPRECATED === $errorNumber) && DeprecationHandler::isDeprecationTest($this->testCase)) { + $prefix = (error_reporting() & $errorNumber) ? 'Unsilenced deprecation: ' : ''; + DeprecationHandler::collectActualDeprecation($prefix . $errorString); + return TRUE; + } + + // In all other cases (errors, warnings, deprecations in normal tests), we + // fall back to the parent error handler, which is the one that was + // registered in the test runner bootstrap (BootstrapErrorHandler). + call_user_func($this->parentHandler, $errorNumber, $errorString, $errorFile, $errorLine); + return TRUE; + } + +} diff --git a/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php b/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php new file mode 100644 index 000000000000..c176980bae27 --- /dev/null +++ b/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php @@ -0,0 +1,249 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\Extension\DeprecationBridge; + +use PHPUnit\Framework\TestCase; + +/** + * Drupal's PHPUnit extension to manage code deprecation. + * + * This class is a replacement for symfony/phpunit-bridge that does not + * support PHPUnit 10. In the future this extension might be dropped if + * PHPUnit adds support for all deprecation management needs. + * + * @internal + */ +final class DeprecationHandler { + + /** + * Indicates if the extension is enabled. + */ + private static bool $enabled = FALSE; + + /** + * A list of deprecation messages that should be ignored if detected. + * + * @var list<string> + */ + private static array $deprecationIgnorePatterns = []; + + /** + * A list of expected deprecation messages. + * + * @var list<string> + */ + private static array $expectedDeprecations = []; + + /** + * A list of deprecation messages collected during test run. + * + * @var list<string> + */ + private static array $collectedDeprecations = []; + + /** + * This class should not be instantiated. + */ + private function __construct() { + throw new \LogicException(__CLASS__ . ' should not be instantiated'); + } + + /** + * Returns the extension configuration. + * + * For historical reasons, the configuration is stored in the + * SYMFONY_DEPRECATIONS_HELPER environment variable. + * + * @return array|false + * An array of configuration variables, of FALSE if the extension is + * disabled. + */ + public static function getConfiguration(): array|FALSE { + $environmentVariable = getenv('SYMFONY_DEPRECATIONS_HELPER'); + if ($environmentVariable === 'disabled') { + return FALSE; + } + if ($environmentVariable === FALSE) { + // Ensure ignored deprecation patterns listed in .deprecation-ignore.txt + // are considered in testing. + $relativeFilePath = __DIR__ . "/../../../../../.deprecation-ignore.txt"; + $deprecationIgnoreFilename = realpath($relativeFilePath); + if (empty($deprecationIgnoreFilename)) { + throw new \InvalidArgumentException(sprintf('The ignoreFile "%s" does not exist.', $relativeFilePath)); + } + $environmentVariable = "ignoreFile=$deprecationIgnoreFilename"; + } + parse_str($environmentVariable, $configuration); + return $configuration; + } + + /** + * Determines if the extension is enabled. + * + * @return bool + * TRUE if enabled, FALSE if disabled. + */ + public static function isEnabled(): bool { + return self::$enabled; + } + + /** + * Initializes the extension. + * + * @param string|null $ignoreFile + * The path to a file containing ignore patterns for deprecations. + */ + public static function init(?string $ignoreFile = NULL): void { + if (self::isEnabled()) { + throw new \LogicException(__CLASS__ . ' is already initialized'); + } + + // Load the deprecation ignore patterns from the specified file. + if ($ignoreFile && !self::$deprecationIgnorePatterns) { + if (!is_file($ignoreFile)) { + throw new \InvalidArgumentException(sprintf('The ignoreFile "%s" does not exist.', $ignoreFile)); + } + set_error_handler(static function ($t, $m) use ($ignoreFile, &$line) { + throw new \RuntimeException(sprintf('Invalid pattern found in "%s" on line "%d"', $ignoreFile, 1 + $line) . substr($m, 12)); + }); + try { + foreach (file($ignoreFile) as $line => $pattern) { + if ((trim($pattern)[0] ?? '#') !== '#') { + preg_match($pattern, ''); + self::$deprecationIgnorePatterns[] = $pattern; + } + } + } + finally { + restore_error_handler(); + } + } + + // Mark the extension as enabled. + self::$enabled = TRUE; + } + + /** + * Resets the extension. + * + * The extension should be reset at the beginning of each test run to ensure + * matching of expected and actual deprecations. + */ + public static function reset(): void { + if (!self::isEnabled()) { + return; + } + self::$expectedDeprecations = []; + self::$collectedDeprecations = []; + } + + /** + * Adds an expected deprecation. + * + * Tests will expect deprecations during the test execution; at the end of + * each test run, collected deprecations are checked against the expected + * ones. + * + * @param string $message + * The expected deprecation message. + */ + public static function expectDeprecation(string $message): void { + if (!self::isEnabled()) { + return; + } + self::$expectedDeprecations[] = $message; + } + + /** + * Returns all expected deprecations. + * + * @return list<string> + * The expected deprecation messages. + */ + public static function getExpectedDeprecations(): array { + if (!self::isEnabled()) { + throw new \LogicException(__CLASS__ . ' is not initialized'); + } + return self::$expectedDeprecations; + } + + /** + * Collects an actual deprecation. + * + * Tests will expect deprecations during the test execution; at the end of + * each test run, collected deprecations are checked against the expected + * ones. + * + * @param string $message + * The actual deprecation message triggered via trigger_error(). + */ + public static function collectActualDeprecation(string $message): void { + if (!self::isEnabled()) { + return; + } + self::$collectedDeprecations[] = $message; + } + + /** + * Returns all collected deprecations. + * + * @return list<string> + * The collected deprecation messages. + */ + public static function getCollectedDeprecations(): array { + if (!self::isEnabled()) { + throw new \LogicException(__CLASS__ . ' is not initialized'); + } + return self::$collectedDeprecations; + } + + /** + * Determines if an actual deprecation should be ignored. + * + * Deprecations that match the patterns included in the ignore file should + * be ignored. + * + * @param string $deprecationMessage + * The actual deprecation message triggered via trigger_error(). + */ + public static function isIgnoredDeprecation(string $deprecationMessage): bool { + if (!self::$deprecationIgnorePatterns) { + return FALSE; + } + $result = @preg_filter(self::$deprecationIgnorePatterns, '$0', $deprecationMessage); + if (preg_last_error() !== \PREG_NO_ERROR) { + throw new \RuntimeException(preg_last_error_msg()); + } + return (bool) $result; + } + + /** + * Determines if a test case is a deprecation test. + * + * Deprecation tests are those that are annotated with '@group legacy' or + * that have a '#[IgnoreDeprecations]' attribute. + * + * @param \PHPUnit\Framework\TestCase $testCase + * The test case being executed. + */ + public static function isDeprecationTest(TestCase $testCase): bool { + return $testCase->valueObjectForEvents()->metadata()->isIgnoreDeprecations()->isNotEmpty() || self::isTestInLegacyGroup($testCase); + } + + /** + * Determines if a test case is part of the 'legacy' group. + * + * @param \PHPUnit\Framework\TestCase $testCase + * The test case being executed. + */ + private static function isTestInLegacyGroup(TestCase $testCase): bool { + $groups = []; + foreach ($testCase->valueObjectForEvents()->metadata()->isGroup() as $metadata) { + $groups[] = $metadata->groupName(); + } + return in_array('legacy', $groups, TRUE); + } + +} diff --git a/core/tests/Drupal/TestTools/Extension/DeprecationBridge/ExpectDeprecationTrait.php b/core/tests/Drupal/TestTools/Extension/DeprecationBridge/ExpectDeprecationTrait.php new file mode 100644 index 000000000000..bd87d7db905e --- /dev/null +++ b/core/tests/Drupal/TestTools/Extension/DeprecationBridge/ExpectDeprecationTrait.php @@ -0,0 +1,137 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\Extension\DeprecationBridge; + +use Drupal\Core\Utility\Error; +use Drupal\TestTools\ErrorHandler\TestErrorHandler; +use PHPUnit\Framework\Attributes\After; +use PHPUnit\Framework\Attributes\Before; + +/** + * A trait to include in Drupal tests to manage expected deprecations. + * + * This code works in coordination with DeprecationHandler. + * + * This trait is a replacement for symfony/phpunit-bridge that is not + * supporting PHPUnit 10. In the future this extension might be dropped if + * PHPUnit will support all deprecation management needs. + * + * @see \Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler + * + * @internal + */ +trait ExpectDeprecationTrait { + + /** + * Sets up the test error handler. + * + * This method is run before each test's ::setUp() method, and when the + * DeprecationHandler is active, resets the extension to be able to collect + * the test's deprecations, and sets TestErrorHandler as the current error + * handler. + * + * @see \Drupal\TestTools\ErrorHandler\TestErrorHandler + */ + #[Before] + public function setUpErrorHandler(): void { + if (!DeprecationHandler::isEnabled()) { + return; + } + + DeprecationHandler::reset(); + set_error_handler(new TestErrorHandler(Error::currentErrorHandler(), $this)); + } + + /** + * Tears down the test error handler. + * + * This method is run after each test's ::tearDown() method, and checks if + * collected deprecations match the expectations; it also resets the error + * handler to the one set prior of the change made by ::setUpErrorHandler(). + */ + #[After] + public function tearDownErrorHandler(): void { + if (!DeprecationHandler::isEnabled()) { + return; + } + + // We expect that the current error handler is the one set by + // ::setUpErrorHandler() prior to the start of the test execution. If not, + // the error handler was changed during the test execution but not properly + // restored during ::tearDown(). + $handler = Error::currentErrorHandler(); + if (!$handler instanceof TestErrorHandler) { + throw new \RuntimeException(sprintf('%s registered its own error handler (%s) without restoring the previous one before or during tear down. This can cause unpredictable test results. Ensure the test cleans up after itself.', + $this->name(), + self::getCallableName($handler), + )); + } + restore_error_handler(); + + // Checks if collected deprecations match the expectations. + if (DeprecationHandler::getExpectedDeprecations()) { + $prefix = "@expectedDeprecation:\n"; + $expDep = $prefix . '%A ' . implode("\n%A ", DeprecationHandler::getExpectedDeprecations()) . "\n%A"; + $actDep = $prefix . ' ' . implode("\n ", DeprecationHandler::getCollectedDeprecations()) . "\n"; + $this->assertStringMatchesFormat($expDep, $actDep); + } + } + + /** + * Adds an expected deprecation. + * + * @param string $message + * The expected deprecation message. + */ + public function expectDeprecation(string $message): void { + if (!DeprecationHandler::isDeprecationTest($this)) { + throw new \RuntimeException('expectDeprecation() can only be called from tests marked with #[IgnoreDeprecations] or \'@group legacy\''); + } + + if (!DeprecationHandler::isEnabled()) { + return; + } + + DeprecationHandler::expectDeprecation($message); + } + + /** + * Returns a callable as a string suitable for inclusion in a message. + * + * @param callable $callable + * The callable. + * + * @return string + * The string suitable for inclusion in a message. + * + * @see https://stackoverflow.com/questions/34324576/print-name-or-definition-of-callable-in-php + */ + private static function getCallableName(callable $callable): string { + switch (TRUE) { + case is_string($callable) && strpos($callable, '::'): + return '[static] ' . $callable; + + case is_string($callable): + return '[function] ' . $callable; + + case is_array($callable) && is_object($callable[0]): + return '[method] ' . get_class($callable[0]) . '->' . $callable[1]; + + case is_array($callable): + return '[static] ' . $callable[0] . '::' . $callable[1]; + + case $callable instanceof \Closure: + return '[closure]'; + + case is_object($callable): + return '[invokable] ' . get_class($callable); + + default: + return '[unknown]'; + + } + } + +} diff --git a/core/tests/Drupal/TestTools/Extension/HtmlLogging/HtmlOutputLogger.php b/core/tests/Drupal/TestTools/Extension/HtmlLogging/HtmlOutputLogger.php new file mode 100644 index 000000000000..10a8f5a49b61 --- /dev/null +++ b/core/tests/Drupal/TestTools/Extension/HtmlLogging/HtmlOutputLogger.php @@ -0,0 +1,143 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\Extension\HtmlLogging; + +use PHPUnit\Event\Facade; +use PHPUnit\Event\TestRunner\Finished as TestRunnerFinished; +use PHPUnit\Event\TestRunner\Started as TestRunnerStarted; + +/** + * Drupal's extension for providing HTML output results for functional tests. + * + * @internal + */ +final class HtmlOutputLogger { + + /** + * The singleton instance. + */ + private static ?self $instance = NULL; + + /** + * A file with list of links to HTML pages generated. + */ + private ?string $browserOutputFile = NULL; + + /** + * @throws \PHPUnit\Event\EventFacadeIsSealedException + * @throws \PHPUnit\Util\Exception + * @throws \PHPUnit\Event\UnknownSubscriberTypeException + * @throws \RuntimeException + */ + private function __construct( + private readonly string $outputDirectory, + private readonly bool $outputVerbose, + private readonly Facade $facade, + ) { + $this->facade->registerSubscriber(new TestRunnerStartedSubscriber($this)); + $this->facade->registerSubscriber(new TestRunnerFinishedSubscriber($this)); + } + + /** + * Initializes the extension. + * + * @param string $outputDirectory + * The directory where the HTML pages should be generated. + * @param bool $outputVerbose + * If TRUE, a list of links generated will be output at the end of the test + * run; if FALSE, only a summary with the count of pages generated. + * + * @throws \PHPUnit\Event\EventFacadeIsSealedException + * @throws \PHPUnit\Util\Exception + * @throws \PHPUnit\Event\UnknownSubscriberTypeException + * @throws \RuntimeException + */ + public static function init(string $outputDirectory, bool $outputVerbose): void { + if (self::$instance === NULL) { + $realDirectory = realpath($outputDirectory); + if ($realDirectory === FALSE || !is_dir($realDirectory) || !is_writable($realDirectory)) { + print "HTML output directory {$outputDirectory} is not a writable directory.\n\n"; + return; + } + self::$instance = new self($realDirectory, $outputVerbose, Facade::instance()); + } + } + + /** + * Determines if the extension is enabled. + * + * @return bool + * TRUE if enabled, FALSE if disabled. + */ + public static function isEnabled(): bool { + return self::$instance !== NULL; + } + + /** + * Logs a link to a generated HTML page. + * + * @param string $logEntry + * A link to a generated HTML page. + * + * @throws \RuntimeException + */ + public static function log(string $logEntry): void { + if (!self::isEnabled()) { + throw new \RuntimeException("HTML output is not enabled"); + } + + $browserOutputFile = getenv('BROWSERTEST_OUTPUT_FILE'); + file_put_contents($browserOutputFile, $logEntry . "\n", FILE_APPEND); + } + + /** + * Empties the list of the HTML output created during the test run. + */ + public function testRunnerStarted(TestRunnerStarted $event): void { + if (!self::isEnabled()) { + throw new \RuntimeException("HTML output is not enabled"); + } + + // Convert to a canonicalized absolute pathname just in case the current + // working directory is changed. + $this->browserOutputFile = tempnam($this->outputDirectory, 'browser_output_'); + if ($this->browserOutputFile) { + touch($this->browserOutputFile); + putenv('BROWSERTEST_OUTPUT_FILE=' . $this->browserOutputFile); + } + else { + // Remove any environment variable. + putenv('BROWSERTEST_OUTPUT_FILE'); + throw new \RuntimeException("Unable to create a temporary file in {$this->outputDirectory}."); + } + } + + /** + * Prints the list of HTML output generated during the test. + */ + public function testRunnerFinished(TestRunnerFinished $event): void { + if (!self::isEnabled()) { + throw new \RuntimeException("HTML output is not enabled"); + } + + $contents = file_get_contents($this->browserOutputFile); + if ($contents) { + print "\n\n"; + if ($this->outputVerbose) { + print "HTML output was generated.\n"; + print $contents; + } + else { + print "HTML output was generated, " . count(explode("\n", $contents)) . " page(s).\n"; + } + } + + // No need to keep the file around any more. + unlink($this->browserOutputFile); + putenv('BROWSERTEST_OUTPUT_FILE'); + $this->browserOutputFile = NULL; + } + +} diff --git a/core/tests/Drupal/TestTools/Extension/HtmlLogging/SubscriberBase.php b/core/tests/Drupal/TestTools/Extension/HtmlLogging/SubscriberBase.php new file mode 100644 index 000000000000..9b562a79a855 --- /dev/null +++ b/core/tests/Drupal/TestTools/Extension/HtmlLogging/SubscriberBase.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\Extension\HtmlLogging; + +/** + * Base class for PHPUnit event subscribers related to HTML logging. + * + * @internal + */ +abstract class SubscriberBase { + + public function __construct( + private readonly HtmlOutputLogger $logger, + ) { + } + + protected function logger(): HtmlOutputLogger { + return $this->logger; + } + +} diff --git a/core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerFinishedSubscriber.php b/core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerFinishedSubscriber.php new file mode 100644 index 000000000000..806b24f72677 --- /dev/null +++ b/core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerFinishedSubscriber.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\Extension\HtmlLogging; + +use PHPUnit\Event\TestRunner\Finished; +use PHPUnit\Event\TestRunner\FinishedSubscriber; + +/** + * Event subscriber notifying end of test runner execution to HTML logging. + * + * @internal + */ +final class TestRunnerFinishedSubscriber extends SubscriberBase implements FinishedSubscriber { + + public function notify(Finished $event): void { + $this->logger()->testRunnerFinished($event); + } + +} diff --git a/core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerStartedSubscriber.php b/core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerStartedSubscriber.php new file mode 100644 index 000000000000..69d7056cbf33 --- /dev/null +++ b/core/tests/Drupal/TestTools/Extension/HtmlLogging/TestRunnerStartedSubscriber.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\TestTools\Extension\HtmlLogging; + +use PHPUnit\Event\TestRunner\Started; +use PHPUnit\Event\TestRunner\StartedSubscriber; + +/** + * Event subscriber notifying beginning of test runner to HTML logging. + * + * @internal + */ +final class TestRunnerStartedSubscriber extends SubscriberBase implements StartedSubscriber { + + public function notify(Started $event): void { + $this->logger()->testRunnerStarted($event); + } + +} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/ClassWriter.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/ClassWriter.php deleted file mode 100644 index 76d5deb891db..000000000000 --- a/core/tests/Drupal/TestTools/PhpUnitCompatibility/ClassWriter.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\TestTools\PhpUnitCompatibility; - -use Composer\Autoload\ClassLoader; - -/** - * 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 PHPUnit classes to make it compatible with Drupal. - * - * @param \Composer\Autoload\ClassLoader $autoloader - * The autoloader. - * - * @throws \ReflectionException - */ - public static function mutateTestBase($autoloader) { - static::alterAssert($autoloader); - static::alterTestCase($autoloader); - } - - /** - * Alters the Assert class. - * - * @param \Composer\Autoload\ClassLoader $autoloader - * The autoloader. - * - * @throws \ReflectionException - */ - private static function alterAssert(ClassLoader $autoloader): void { - // 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\Assert', FALSE)) { - return; - } - // Mutate Assert code to make it forward compatible with different PhpUnit - // versions, by adding Symfony's PHPUnit-bridge PolyfillAssertTrait. - $alteredFile = $autoloader->findFile('PHPUnit\Framework\Assert'); - $phpunit_dir = dirname($alteredFile, 3); - $alteredCode = file_get_contents($alteredFile); - $alteredCode = preg_replace('/abstract class Assert[^\{]+\{/', '$0 ' . \PHP_EOL . " use \Symfony\Bridge\PhpUnit\Legacy\PolyfillAssertTrait;" . \PHP_EOL, $alteredCode, 1); - include static::flushAlteredCodeToFile('Assert.php', $alteredCode); - } - - /** - * Alters the TestCase class. - * - * @param \Composer\Autoload\ClassLoader $autoloader - * The autoloader. - * - * @throws \ReflectionException - */ - private static function alterTestCase(ClassLoader $autoloader): void { - // 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; - } - // Mutate TestCase code to make it forward compatible with different PhpUnit - // versions, by adding Symfony's PHPUnit-bridge PolyfillTestCaseTrait. - $alteredFile = $autoloader->findFile('PHPUnit\Framework\TestCase'); - $phpunit_dir = dirname($alteredFile, 3); - $alteredCode = file_get_contents($alteredFile); - $alteredCode = preg_replace('/abstract class TestCase[^\{]+\{/', '$0 ' . \PHP_EOL . " use \Symfony\Bridge\PhpUnit\Legacy\PolyfillTestCaseTrait;" . \PHP_EOL, $alteredCode, 1); - $alteredCode = str_replace("__DIR__ . '/../Util/", "'$phpunit_dir/src/Util/", $alteredCode); - include static::flushAlteredCodeToFile('TestCase.php', $alteredCode); - } - - /** - * Flushes altered class code to file when necessary. - * - * @param string $file_name - * The file name. - * @param string $altered_code - * The altered code. - * - * @return string - * The full path of the file to be included. - */ - private static function flushAlteredCodeToFile(string $file_name, string $altered_code): string { - $directory = __DIR__ . '/../../../../../sites/simpletest'; - $full_path = $directory . '/' . $file_name; - - // Only write when necessary. - if (!file_exists($full_path) || md5_file($full_path) !== md5($altered_code)) { - // Create directory when necessary. - if (!is_dir($directory) && !@mkdir($directory, 0777, TRUE) && !is_dir($directory)) { - throw new \RuntimeException('Unable to create directory: ' . $directory); - } - file_put_contents($full_path, $altered_code); - } - - return $full_path; - } - -} diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit9/TestCompatibilityTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit10/TestCompatibilityTrait.php similarity index 52% rename from core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit9/TestCompatibilityTrait.php rename to core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit10/TestCompatibilityTrait.php index 06d874e2859b..a2dbea6ff521 100644 --- a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit9/TestCompatibilityTrait.php +++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit10/TestCompatibilityTrait.php @@ -2,22 +2,15 @@ declare(strict_types=1); -namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit9; - -use PHPUnit\Util\Test; +namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit10; /** * Drupal's forward compatibility layer with multiple versions of PHPUnit. + * + * @internal */ trait TestCompatibilityTrait { - /** - * Get test name. - */ - public function name(): string { - return $this->getName(); - } - /** * Gets @covers defined on the test class. * @@ -25,8 +18,11 @@ public function name(): string { * An array of classes listed with the @covers annotation. */ public function getTestClassCovers(): array { - $annotations = Test::parseTestMethodAnnotations(static::class, $this->name()); - return $annotations['class']['covers'] ?? []; + $ret = []; + foreach ($this->valueObjectForEvents()->metadata()->isCovers()->isClassLevel() as $metadata) { + $ret[] = $metadata->target(); + } + return $ret; } } diff --git a/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php b/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php index 0ce61082fd18..35400a5c90eb 100644 --- a/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php +++ b/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php @@ -6,6 +6,7 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Utility\Error; +use Drupal\TestTools\Extension\HtmlLogging\HtmlOutputLogger; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -49,17 +50,6 @@ trait BrowserHtmlDebugTrait { */ protected $htmlOutputEnabled = FALSE; - /** - * The file name to write the list of URLs to. - * - * This file is read by the PHPUnit result printer. - * - * @var string - * - * @see \Drupal\Tests\Listeners\HtmlOutputPrinter - */ - protected $htmlOutputFile; - /** * HTML output test ID. * @@ -128,21 +118,16 @@ protected function htmlOutput($message = NULL) { // Do not use the file_url_generator service as the module_handler service // might not be available. $uri = $this->htmlOutputBaseUrl . '/sites/simpletest/browser_output/' . $html_output_filename; - file_put_contents($this->htmlOutputFile, $uri . "\n", FILE_APPEND); + HtmlOutputLogger::log($uri . "\n"); } /** * Creates the directory to store browser output. - * - * Creates the directory to store browser output in if a file to write - * URLs to has been created by \Drupal\Tests\Listeners\HtmlOutputPrinter. */ protected function initBrowserOutputFile() { - $browser_output_file = getenv('BROWSERTEST_OUTPUT_FILE'); - $this->htmlOutputEnabled = is_string($browser_output_file) && is_file($browser_output_file); + $this->htmlOutputEnabled = HtmlOutputLogger::isEnabled(); $this->htmlOutputBaseUrl = getenv('BROWSERTEST_OUTPUT_BASE_URL') ?: $GLOBALS['base_url']; if ($this->htmlOutputEnabled) { - $this->htmlOutputFile = $browser_output_file; $this->htmlOutputClassName = str_replace("\\", "_", static::class); $this->htmlOutputDirectory = DRUPAL_ROOT . '/sites/simpletest/browser_output'; // Do not use the file_system service so this method can be called before diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index f27fa37a40bd..83a1a0eb342f 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -6,6 +6,7 @@ use Behat\Mink\Driver\BrowserKitDriver; use Behat\Mink\Element\Element; +use Behat\Mink\Exception\Exception as MinkException; use Behat\Mink\Mink; use Behat\Mink\Selector\SelectorsHandler; use Behat\Mink\Session; @@ -19,13 +20,12 @@ use Drupal\Tests\block\Traits\BlockCreationTrait; use Drupal\Tests\node\Traits\ContentTypeCreationTrait; use Drupal\Tests\node\Traits\NodeCreationTrait; -use Drupal\Tests\Traits\PhpUnitWarnings; use Drupal\Tests\user\Traits\UserCreationTrait; use Drupal\TestTools\Comparator\MarkupInterfaceComparator; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use Drupal\TestTools\TestVarDumper; use GuzzleHttp\Cookie\CookieJar; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; use Symfony\Component\VarDumper\VarDumper; @@ -72,7 +72,6 @@ abstract class BrowserTestBase extends TestCase { createUser as drupalCreateUser; } use XdebugRequestTrait; - use PhpUnitWarnings; use PhpUnitCompatibilityTrait; use ExpectDeprecationTrait; use ExtensionListTestTrait; @@ -173,19 +172,6 @@ abstract class BrowserTestBase extends TestCase { */ protected $mink; - /** - * {@inheritdoc} - * - * Browser tests are run in separate processes to prevent collisions between - * code that may be loaded by tests. - */ - protected $runTestInSeparateProcess = TRUE; - - /** - * {@inheritdoc} - */ - protected $preserveGlobalState = FALSE; - /** * The base URL. * @@ -210,6 +196,14 @@ abstract class BrowserTestBase extends TestCase { */ protected $originalContainer; + /** + * {@inheritdoc} + */ + public function __construct(string $name) { + parent::__construct($name); + $this->setRunTestInSeparateProcess(TRUE); + } + /** * {@inheritdoc} */ @@ -369,7 +363,9 @@ protected function setUp(): void { $this->prepareEnvironment(); $this->installDrupal(); - // Setup Mink. + // Setup Mink. Register Mink exceptions to cause test failures instead of + // errors. + $this->registerFailureType(MinkException::class); $this->initMink(); // Set up the browser test output file. diff --git a/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php b/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php index 1bdca0702e71..a5e1c5705163 100644 --- a/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php +++ b/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php @@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase; use Drupal\Component\Assertion\Inspector; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; /** * @coversDefaultClass \Drupal\Component\Assertion\Inspector diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php index 7f1be690a8ed..0e42a0b4e8e5 100644 --- a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php +++ b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php @@ -5,9 +5,9 @@ namespace Drupal\Tests\Component\DependencyInjection; use Drupal\Component\Utility\Crypt; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php index df31c85d8a63..9766833a0a53 100644 --- a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php +++ b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php @@ -5,10 +5,10 @@ namespace Drupal\Tests\Component\DependencyInjection\Dumper { use Drupal\Component\Utility\Crypt; + use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophet; - use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Definition; diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php index 60fa0b440409..438814702c62 100644 --- a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php +++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php @@ -7,7 +7,7 @@ use Drupal\Component\PhpStorage\FileStorage; use Drupal\Component\PhpStorage\FileReadOnlyStorage; use Drupal\Component\Utility\Random; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; /** * @coversDefaultClass \Drupal\Component\PhpStorage\FileReadOnlyStorage diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php index 19896437843d..db63b5498b64 100644 --- a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php +++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php @@ -6,9 +6,8 @@ use Drupal\Component\PhpStorage\FileStorage; use Drupal\Component\Utility\Random; -use Drupal\Tests\Traits\PhpUnitWarnings; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use org\bovigo\vfs\vfsStreamDirectory; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** * @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage @@ -17,7 +16,7 @@ */ class FileStorageTest extends PhpStorageTestBase { - use PhpUnitWarnings, ExpectDeprecationTrait; + use ExpectDeprecationTrait; /** * Standard test settings to pass to storage instances. diff --git a/core/tests/Drupal/Tests/Component/Render/FormattableMarkupTest.php b/core/tests/Drupal/Tests/Component/Render/FormattableMarkupTest.php index cb52f2f69524..9bd02126d786 100644 --- a/core/tests/Drupal/Tests/Component/Render/FormattableMarkupTest.php +++ b/core/tests/Drupal/Tests/Component/Render/FormattableMarkupTest.php @@ -5,8 +5,8 @@ namespace Drupal\Tests\Component\Render; use Drupal\Component\Render\FormattableMarkup; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** * Tests the TranslatableMarkup class. diff --git a/core/tests/Drupal/Tests/Component/Utility/BytesTest.php b/core/tests/Drupal/Tests/Component/Utility/BytesTest.php index e28b912fb5e2..04c77eb0a71b 100644 --- a/core/tests/Drupal/Tests/Component/Utility/BytesTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/BytesTest.php @@ -5,10 +5,10 @@ namespace Drupal\Tests\Component\Utility; use Drupal\Component\Utility\Bytes; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Validator\Context\ExecutionContextInterface; /** diff --git a/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php b/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php index 6dfe031dd123..291304532786 100644 --- a/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php @@ -5,8 +5,8 @@ namespace Drupal\Tests\Component\Utility; use Drupal\Component\Utility\Unicode; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** * Test unicode handling features implemented in Unicode component. diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php index a2257158b2c5..9c5779f4604f 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/AssertUtilsTrait.php @@ -4,13 +4,10 @@ namespace Drupal\Tests\Composer\Plugin\Scaffold; -use Drupal\Tests\Traits\PhpUnitWarnings; - /** * Convenience class for creating fixtures. */ trait AssertUtilsTrait { - use PhpUnitWarnings; /** * Asserts that a given file exists and is/is not a symlink. diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php index 3bea63641f30..e61a716ff8bf 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php @@ -8,7 +8,6 @@ use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; use Drupal\Tests\Composer\Plugin\Scaffold\AssertUtilsTrait; use Drupal\Tests\Composer\Plugin\Scaffold\ExecTrait; -use Drupal\Tests\PhpUnitCompatibilityTrait; use PHPUnit\Framework\TestCase; /** @@ -23,7 +22,6 @@ class ManageGitIgnoreTest extends TestCase { use ExecTrait; use AssertUtilsTrait; - use PhpUnitCompatibilityTrait; /** * The root of this project. diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php index e5127780cb70..012cc4590c44 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldTest.php @@ -8,7 +8,6 @@ use Drupal\Tests\Composer\Plugin\Scaffold\AssertUtilsTrait; use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; use Drupal\Tests\Composer\Plugin\Scaffold\ScaffoldTestResult; -use Drupal\Tests\PhpUnitCompatibilityTrait; use PHPUnit\Framework\TestCase; /** @@ -21,7 +20,6 @@ */ class ScaffoldTest extends TestCase { use AssertUtilsTrait; - use PhpUnitCompatibilityTrait; /** * The root of this project. diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php index f28ec78ca510..c9f34d95516d 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php @@ -8,7 +8,6 @@ use Drupal\Tests\Composer\Plugin\Scaffold\AssertUtilsTrait; use Drupal\Tests\Composer\Plugin\Scaffold\ExecTrait; use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; -use Drupal\Tests\PhpUnitCompatibilityTrait; use PHPUnit\Framework\TestCase; /** @@ -28,7 +27,6 @@ class ScaffoldUpgradeTest extends TestCase { use AssertUtilsTrait; use ExecTrait; - use PhpUnitCompatibilityTrait; /** * The Fixtures object. diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php index 45806562416b..3cce05475a44 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/AppendOpTest.php @@ -7,7 +7,6 @@ use Drupal\Composer\Plugin\Scaffold\Operations\AppendOp; use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions; use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; -use Drupal\Tests\Traits\PhpUnitWarnings; use PHPUnit\Framework\TestCase; /** @@ -16,7 +15,6 @@ * @group Scaffold */ class AppendOpTest extends TestCase { - use PhpUnitWarnings; /** * @covers ::process diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php index 00deb085684d..a781d5b9c66c 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/ReplaceOpTest.php @@ -7,7 +7,6 @@ use Drupal\Composer\Plugin\Scaffold\Operations\ReplaceOp; use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions; use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; -use Drupal\Tests\Traits\PhpUnitWarnings; use PHPUnit\Framework\TestCase; /** @@ -16,7 +15,6 @@ * @group Scaffold */ class ReplaceOpTest extends TestCase { - use PhpUnitWarnings; /** * @covers ::process diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php index 8fd239540bde..5a13e34918a4 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Integration/SkipOpTest.php @@ -7,7 +7,6 @@ use Drupal\Composer\Plugin\Scaffold\Operations\SkipOp; use Drupal\Composer\Plugin\Scaffold\ScaffoldOptions; use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures; -use Drupal\Tests\Traits\PhpUnitWarnings; use PHPUnit\Framework\TestCase; /** @@ -16,7 +15,6 @@ * @group Scaffold */ class SkipOpTest extends TestCase { - use PhpUnitWarnings; /** * @covers ::process diff --git a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php index 442379723d3d..66f28d2ed5e5 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/ConfigTest.php @@ -6,7 +6,6 @@ use Composer\Package\RootPackageInterface; use Drupal\Composer\Plugin\VendorHardening\Config; -use Drupal\Tests\Traits\PhpUnitWarnings; use PHPUnit\Framework\TestCase; /** @@ -15,8 +14,6 @@ */ class ConfigTest extends TestCase { - use PhpUnitWarnings; - /** * @covers ::getPathsForPackage */ diff --git a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php index 1382235f7772..29c935fe1444 100644 --- a/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php +++ b/core/tests/Drupal/Tests/Composer/Plugin/VendorHardening/VendorHardeningPluginTest.php @@ -10,7 +10,6 @@ use Composer\Package\RootPackageInterface; use Drupal\Composer\Plugin\VendorHardening\Config; use Drupal\Composer\Plugin\VendorHardening\VendorHardeningPlugin; -use Drupal\Tests\Traits\PhpUnitWarnings; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; @@ -21,7 +20,6 @@ */ class VendorHardeningPluginTest extends TestCase { - use PhpUnitWarnings; use ProphecyTrait; /** diff --git a/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php index 2b7033ac8e13..985cc785f591 100644 --- a/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php @@ -15,11 +15,6 @@ */ class CssOptimizerUnitTest extends UnitTestCase { - /** - * {@inheritdoc} - */ - protected $backupGlobals = FALSE; - /** * A CSS asset optimizer. * diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 34e16dd5bae2..7e6590b797de 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -67,7 +67,7 @@ public function testGetFormIdWithString() { * @covers ::getFormId */ public function testGetFormIdWithNonFormClass() { - $form_arg = __CLASS__; + $form_arg = \stdClass::class; $form_state = new FormState(); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage("The form argument $form_arg must be an instance of \Drupal\Core\Form\FormInterface."); diff --git a/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php b/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php index 8a532a4027f9..e83d5705c443 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php @@ -84,16 +84,6 @@ class FormCacheTest extends UnitTestCase { */ protected $requestPolicy; - /** - * {@inheritdoc} - */ - protected $runTestInSeparateProcess = TRUE; - - /** - * {@inheritdoc} - */ - protected $preserveGlobalState = FALSE; - /** * {@inheritdoc} */ diff --git a/core/tests/Drupal/Tests/Core/Form/FormStateDecoratorBaseTest.php b/core/tests/Drupal/Tests/Core/Form/FormStateDecoratorBaseTest.php index 2b00dfdfa1a6..3924855fd97d 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormStateDecoratorBaseTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormStateDecoratorBaseTest.php @@ -1451,7 +1451,7 @@ public static function providerPrepareCallback(): array { $closure = function () {}; $static_method_string = __METHOD__; $static_method_array = [__CLASS__, __FUNCTION__]; - $object_method_array = [new static(), __FUNCTION__]; + $object_method_array = [new static('test'), __FUNCTION__]; return [ // A shorthand form method is generally expanded to become a method on an diff --git a/core/tests/Drupal/Tests/Core/Security/RequestSanitizerTest.php b/core/tests/Drupal/Tests/Core/Security/RequestSanitizerTest.php index 6e508a1ea62b..070f1d12631d 100644 --- a/core/tests/Drupal/Tests/Core/Security/RequestSanitizerTest.php +++ b/core/tests/Drupal/Tests/Core/Security/RequestSanitizerTest.php @@ -34,6 +34,14 @@ protected function setUp(): void { set_error_handler([$this, "errorHandler"]); } + /** + * {@inheritdoc} + */ + protected function tearDown(): void { + restore_error_handler(); + parent::tearDown(); + } + /** * Tests RequestSanitizer class. * diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/TranslatableMarkupTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/TranslatableMarkupTest.php index a9a1b363d275..338c21a5c77a 100644 --- a/core/tests/Drupal/Tests/Core/StringTranslation/TranslatableMarkupTest.php +++ b/core/tests/Drupal/Tests/Core/StringTranslation/TranslatableMarkupTest.php @@ -80,7 +80,7 @@ public function testToString() { restore_error_handler(); $this->assertEquals(E_USER_ERROR, $this->lastErrorNumber); - $this->assertMatchesRegularExpression('/Exception thrown while calling __toString on a .*Mock_TranslatableMarkup_.* object in .*TranslatableMarkupTest.php on line [0-9]+: Yes you may./', $this->lastErrorMessage); + $this->assertMatchesRegularExpression('/Exception thrown while calling __toString on a .*MockObject_TranslatableMarkup_.* object in .*TranslatableMarkupTest.php on line [0-9]+: Yes you may./', $this->lastErrorMessage); } /** diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeRequiresTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeRequiresTest.php deleted file mode 100644 index cf0e731ae8e1..000000000000 --- a/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeRequiresTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Core\Test; - -use Drupal\Tests\UnitTestCase; -use Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass; - -/** - * Test how unit tests interact with deprecation errors. - * - * If a test requires an extension that does not exist and has a data provider - * the interaction between Drupal and Symfony's deprecation testing can cause - * errors. This test proves this is not broken. - * - * This test will be skipped and should not cause the test suite to fail. - * - * @group Test - * @requires extension will_hopefully_never_exist - * @see \Drupal\Tests\Listeners\DrupalListener - */ -class PhpUnitBridgeRequiresTest extends UnitTestCase { - - /** - * Tests the @requires annotation. - * - * @dataProvider providerTestWillNeverRun - */ - public function testWillNeverRun(): void { - $deprecated = new FixtureDeprecatedClass(); - $this->assertEquals('test', $deprecated->testFunction()); - } - - /** - * Data provider for ::testWillNeverRun(). - */ - public static function providerTestWillNeverRun(): array { - return [ - ['this_will_never_run'], - ]; - } - -} diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php index fe3d55182617..79420875f4e8 100644 --- a/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php +++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php @@ -29,18 +29,4 @@ public function testDeprecatedFunction() { $this->assertEquals('known_return_value', \deprecation_test_function()); } - /** - * Tests the @requires annotation in conjunction with DrupalListener. - * - * This test method will be skipped and should not cause the test suite to - * fail. - * - * @requires extension will_hopefully_never_exist - * @see \Drupal\Tests\Listeners\DrupalListener - */ - public function testWillNeverRun(): void { - $deprecated = new FixtureDeprecatedClass(); - $this->assertEquals('test', $deprecated->testFunction()); - } - } diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitCliTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitCliTest.php index a240af9d4cbb..926a7cdedc5c 100644 --- a/core/tests/Drupal/Tests/Core/Test/PhpUnitCliTest.php +++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitCliTest.php @@ -5,7 +5,6 @@ namespace Drupal\Tests\Core\Test; use Drupal\Tests\UnitTestCase; -use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion; use Symfony\Component\Process\Process; /** @@ -31,11 +30,6 @@ public function testPhpUnitListTests() { '--list-tests', ]; - // PHPUnit 10 dropped the --verbose command line option. - if (RunnerVersion::getMajor() < 10) { - $command[] = '--verbose'; - } - $process = new Process($command, $this->root); $process ->setTimeout(300) diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php index 52c6e7980c29..c8dc2cd27b62 100644 --- a/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php +++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php @@ -53,7 +53,7 @@ public function testRunTestsError() { $runner->expects($this->once()) ->method('runCommand') ->willReturnCallback( - function ($unescaped_test_classnames, $phpunit_file, &$status) { + function (string $test_class_name, string $log_junit_file_path, int &$status): string { $status = TestStatus::EXCEPTION; return ' '; } @@ -63,7 +63,7 @@ function ($unescaped_test_classnames, $phpunit_file, &$status) { // to some value we don't expect back. $status = -1; $test_run = TestRun::createNew($storage); - $results = $runner->execute($test_run, ['SomeTest'], $status); + $results = $runner->execute($test_run, 'SomeTest', $status); // Make sure our status code made the round trip. $this->assertEquals(TestStatus::EXCEPTION, $status); diff --git a/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php b/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php index 1f53f981775e..13fdffbb7906 100644 --- a/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php +++ b/core/tests/Drupal/Tests/Core/Test/TestSuiteBaseTest.php @@ -4,113 +4,13 @@ namespace Drupal\Tests\Core\Test; -use Drupal\Tests\TestSuites\TestSuiteBase; -use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; - -// The test suite class is not part of the autoloader, we need to include it -// manually. -require_once __DIR__ . '/../../../../TestSuites/TestSuiteBase.php'; /** - * @coversDefaultClass \Drupal\Tests\TestSuites\TestSuiteBase - * - * @group TestSuite + * @group Test */ class TestSuiteBaseTest extends TestCase { - use ExpectDeprecationTrait; - - /** - * Helper method to set up the file system. - * - * @return array[] - * A Drupal filesystem suitable for use with vfsStream. - */ - protected function getFilesystem() { - return [ - 'core' => [ - 'modules' => [], - 'profiles' => [], - 'tests' => [ - 'Drupal' => [ - 'NotUnitTests' => [ - 'CoreNotUnitTest.php' => '<?php', - ], - 'Tests' => [ - 'CoreUnitTest.php' => '<?php', - // Ensure that the following files are not found as tests. - 'Listeners' => [ - 'Listener.php' => '<?php', - 'Legacy' => [ - 'Listener.php' => '<?php', - ], - ], - ], - ], - ], - ], - ]; - } - - /** - * @return array[] - * Test data for testAddTestsBySuiteNamespaceCore(). An array of arrays: - * - A filesystem array for vfsStream. - * - The sub-namespace of the test suite. - * - The array of tests expected to be discovered. - */ - public function provideCoreTests() { - $filesystem = $this->getFilesystem(); - return [ - 'unit-tests' => [ - $filesystem, - 'Unit', - [ - 'Drupal\Tests\CoreUnitTest' => 'vfs://root/core/tests/Drupal/Tests/CoreUnitTest.php', - ], - ], - 'not-unit-tests' => [ - $filesystem, - 'NotUnit', - [ - 'Drupal\NotUnitTests\CoreNotUnitTest' => 'vfs://root/core/tests/Drupal/NotUnitTests/CoreNotUnitTest.php', - ], - ], - ]; - } - - /** - * Tests for special case behavior of unit test suite namespaces in core. - * - * @group legacy - * - * @covers ::addTestsBySuiteNamespace - * - * @dataProvider provideCoreTests - */ - public function testAddTestsBySuiteNamespaceCore($filesystem, $suite_namespace, $expected_tests) { - - $this->expectDeprecation('Drupal\\Tests\\Core\\Test\\StubTestSuiteBase is deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no replacement and test discovery will be handled differently in PHPUnit 10. See https://www.drupal.org/node/3405829'); - - // Set up the file system. - $vfs = vfsStream::setup('root'); - vfsStream::create($filesystem, $vfs); - - // Make a stub suite base to test. - $stub = new StubTestSuiteBase('test_me'); - - // Access addTestsBySuiteNamespace(). - $ref_add_tests = new \ReflectionMethod($stub, 'addTestsBySuiteNamespace'); - - // Invoke addTestsBySuiteNamespace(). - $ref_add_tests->invokeArgs($stub, [vfsStream::url('root'), $suite_namespace]); - - // Determine if we loaded the expected test files. - $this->assertEquals($expected_tests, $stub->testFiles); - } - /** * Tests the assumption that local time is in 'Australia/Sydney'. */ @@ -120,52 +20,3 @@ public function testLocalTimeZone() { } } - -/** - * Stub subclass of TestSuiteBase. - * - * We use this class to alter the behavior of TestSuiteBase so it can be - * testable. - * - * @phpstan-ignore-next-line - */ -class StubTestSuiteBase extends TestSuiteBase { - - /** - * Test files discovered by addTestsBySuiteNamespace(). - * - * @var string[] - */ - public $testFiles = []; - - public function __construct(string $name) { - @trigger_error(__CLASS__ . ' is deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no replacement and test discovery will be handled differently in PHPUnit 10. See https://www.drupal.org/node/3405829', E_USER_DEPRECATED); - // @phpstan-ignore-next-line - parent::__construct($name); - } - - /** - * {@inheritdoc} - */ - protected function findExtensionDirectories($root) { - // We have to stub findExtensionDirectories() because we can't inject a - // vfsStream filesystem into drupal_phpunit_find_extension_directories(), - // which uses \SplFileInfo->getRealPath(). getRealPath() resolves - // stream-based paths to an empty string. See - // https://github.com/mikey179/vfsStream/wiki/Known-Issues - return []; - } - - /** - * {@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/Tests/Core/Utility/CallableResolverTest.php b/core/tests/Drupal/Tests/Core/Utility/CallableResolverTest.php index 89f7957d6a06..01bb1b972724 100644 --- a/core/tests/Drupal/Tests/Core/Utility/CallableResolverTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/CallableResolverTest.php @@ -78,8 +78,8 @@ function ($suffix) { __CLASS__ . '::method', ], 'Non-static function, instantiated by class resolver' => [ - static::class . '::method', - __CLASS__ . '::method', + MethodCallable::class . '::method', + MethodCallable::class . '::method', ], 'Non-static function, instantiated by class resolver, container injection' => [ '\Drupal\Tests\Core\Utility\MockContainerInjection::getResult', @@ -94,8 +94,8 @@ function ($suffix) { __CLASS__ . '::staticMethod', ], 'Class with invoke method' => [ - static::class, - __CLASS__ . '::__invoke', + MethodCallable::class, + MethodCallable::class . '::__invoke', ], ]; @@ -131,14 +131,14 @@ public static function callableResolverExceptionHandlingTestCases() { 'The callable definition provided "[not_a_callable,not_a_callable]" is not a valid callable.', ], 'Missing method on class, array notation' => [ - [static::class, 'method_not_exists'], + [\stdClass::class, 'method_not_exists'], \InvalidArgumentException::class, - 'The callable definition provided "[Drupal\Tests\Core\Utility\CallableResolverTest,method_not_exists]" is not a valid callable.', + 'The callable definition provided "[stdClass,method_not_exists]" is not a valid callable.', ], 'Missing method on class, static notation' => [ - static::class . '::method_not_exists', + \stdClass::class . '::method_not_exists', \InvalidArgumentException::class, - 'The callable definition provided was invalid. Either class "Drupal\Tests\Core\Utility\CallableResolverTest" does not have a method "method_not_exists", or it is not callable.', + 'The callable definition provided was invalid. Either class "stdClass" does not have a method "method_not_exists", or it is not callable.', ], 'Missing class, static notation' => [ '\NotARealClass::method', @@ -237,5 +237,38 @@ public static function staticMethod($suffix) { } +class MethodCallable { + + /** + * A test __invoke method. + * + * @param string $suffix + * A suffix to append. + * + * @return string + * A test string. + */ + public function __invoke($suffix) { + return __METHOD__ . '+' . $suffix; + } + + /** + * A test method that returns "foo". + * + * @param string $suffix + * A suffix to append. + * + * @return string + * A test string. + * + * @throws \Exception + * Throws an exception when called statically. + */ + public function method($suffix) { + return __METHOD__ . '+' . $suffix; + } + +} + class NoMethodCallable { } diff --git a/core/tests/Drupal/Tests/ExpectDeprecationTest.php b/core/tests/Drupal/Tests/ExpectDeprecationTest.php index 8fc2b4c872a5..349a34f015f1 100644 --- a/core/tests/Drupal/Tests/ExpectDeprecationTest.php +++ b/core/tests/Drupal/Tests/ExpectDeprecationTest.php @@ -4,8 +4,8 @@ namespace Drupal\Tests; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** * Ensures Drupal has test coverage of Symfony's deprecation testing. diff --git a/core/tests/Drupal/Tests/Listeners/DrupalListener.php b/core/tests/Drupal/Tests/Listeners/DrupalListener.php deleted file mode 100644 index 26fa1e5001e3..000000000000 --- a/core/tests/Drupal/Tests/Listeners/DrupalListener.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Listeners; - -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestListenerDefaultImplementation; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestSuite; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * Listens to PHPUnit test runs. - * - * @internal - */ -class DrupalListener implements TestListener { - - use TestListenerDefaultImplementation; - - /** - * The wrapped Symfony test listener. - * - * @var \Symfony\Bridge\PhpUnit\SymfonyTestsListener - */ - private $symfonyListener; - - /** - * Constructs the DrupalListener object. - */ - public function __construct() { - $this->symfonyListener = new SymfonyTestsListener(); - } - - /** - * {@inheritdoc} - */ - public function startTestSuite(TestSuite $suite): void { - $this->symfonyListener->startTestSuite($suite); - } - - /** - * {@inheritdoc} - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void { - $this->symfonyListener->addSkippedTest($test, $t, $time); - } - - /** - * {@inheritdoc} - */ - public function startTest(Test $test): void { - $this->symfonyListener->startTest($test); - } - - /** - * {@inheritdoc} - */ - public function endTest(Test $test, float $time): void { - $this->symfonyListener->endTest($test, $time); - } - -} diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php deleted file mode 100644 index dcfd8e80e2ff..000000000000 --- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Listeners; - -use PHPUnit\Framework\TestResult; -use PHPUnit\TextUI\DefaultResultPrinter; - -/** - * Defines a class for providing html output results for functional tests. - * - * @internal - */ -class HtmlOutputPrinter extends DefaultResultPrinter { - - use HtmlOutputPrinterTrait; - - /** - * {@inheritdoc} - */ - public function printResult(TestResult $result): void { - parent::printResult($result); - - $this->printHtmlOutput(); - } - -} diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php deleted file mode 100644 index 33507046884a..000000000000 --- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Listeners; - -/** - * Defines a class for providing html output results for functional tests. - * - * @internal - */ -trait HtmlOutputPrinterTrait { - - /** - * File to write html links to. - * - * @var string - */ - 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. - * - * @see \Drupal\Tests\BrowserTestBase::initBrowserOutputFile() - */ - protected function setUpHtmlOutput() { - if ($html_output_directory = getenv('BROWSERTEST_OUTPUT_DIRECTORY')) { - // Initialize html output debugging. - $html_output_directory = rtrim($html_output_directory, '/'); - - // Check if directory exists. - if (!is_dir($html_output_directory) || !is_writable($html_output_directory)) { - $this->writeWithColor('bg-red, fg-black', "HTML output directory $html_output_directory is not a writable directory."); - } - else { - // Convert to a canonicalized absolute pathname just in case the current - // working directory is changed. - $html_output_directory = realpath($html_output_directory); - $this->browserOutputFile = tempnam($html_output_directory, 'browser_output_'); - if ($this->browserOutputFile) { - touch($this->browserOutputFile); - } - else { - $this->writeWithColor('bg-red, fg-black', "Unable to create a temporary file in $html_output_directory."); - } - } - } - - if ($this->browserOutputFile) { - putenv('BROWSERTEST_OUTPUT_FILE=' . $this->browserOutputFile); - } - else { - // Remove any environment variable. - putenv('BROWSERTEST_OUTPUT_FILE'); - } - } - - /** - * Prints the list of HTML output generated during the test. - */ - protected function printHtmlOutput() { - if ($this->browserOutputFile) { - $contents = file_get_contents($this->browserOutputFile); - if ($contents) { - $this->writeNewLine(); - $this->writeWithColor('bg-yellow, fg-black', 'HTML output was generated'); - $this->write($contents); - } - // No need to keep the file around any more. - unlink($this->browserOutputFile); - } - } - -} diff --git a/core/tests/Drupal/Tests/PhpUnitCompatibilityTrait.php b/core/tests/Drupal/Tests/PhpUnitCompatibilityTrait.php index 932bd03a3e83..57885d920572 100644 --- a/core/tests/Drupal/Tests/PhpUnitCompatibilityTrait.php +++ b/core/tests/Drupal/Tests/PhpUnitCompatibilityTrait.php @@ -22,7 +22,7 @@ class_alias("Drupal\TestTools\PhpUnitCompatibility\PhpUnit" . RunnerVersion::get */ trait PhpUnitCompatibilityTrait { - use \Drupal\TestTools\PhpUnitCompatibility\PhpUnit9\TestCompatibilityTrait; + use \Drupal\TestTools\PhpUnitCompatibility\PhpUnit10\TestCompatibilityTrait; } diff --git a/core/tests/Drupal/Tests/PhpUnitWarningsTest.php b/core/tests/Drupal/Tests/PhpUnitWarningsTest.php deleted file mode 100644 index 67d632928fb1..000000000000 --- a/core/tests/Drupal/Tests/PhpUnitWarningsTest.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests; - -/** - * @coversDefaultClass \Drupal\Tests\Traits\PhpUnitWarnings - * @group legacy - */ -class PhpUnitWarningsTest extends UnitTestCase { - - /** - * Tests that selected PHPUnit warning is converted to deprecation. - */ - public function testAddWarning() { - $this->expectDeprecation('Test warning for \Drupal\Tests\PhpUnitWarningsTest::testAddWarning()'); - $this->addWarning('Test warning for \Drupal\Tests\PhpUnitWarningsTest::testAddWarning()'); - } - -} diff --git a/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php b/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php deleted file mode 100644 index 180179a8a67a..000000000000 --- a/core/tests/Drupal/Tests/Traits/PhpUnitWarnings.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Traits; - -/** - * Converts deprecation warnings added by PHPUnit to silenced deprecations. - * - * This trait exists to allow Drupal to run tests with multiple versions of - * PHPUnit without failing due to PHPUnit's deprecation warnings. - * - * @internal - */ -trait PhpUnitWarnings { - - /** - * Deprecation warnings from PHPUnit to raise with @trigger_error(). - * - * Add any PHPUnit deprecations that should be handled as deprecation warnings - * (rather than unconditional failures) for core and contrib. - * - * @var string[] - */ - private static $deprecationWarnings = [ - // Warning for testing. - 'Test warning for \Drupal\Tests\PhpUnitWarningsTest::testAddWarning()', - // PHPUnit 9. - 'assertFileNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileDoesNotExist() instead.', - 'assertRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertMatchesRegularExpression() instead.', - 'assertNotRegExp() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDoesNotMatchRegularExpression() instead.', - 'assertDirectoryNotExists() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryDoesNotExist() instead.', - 'Support for using expectException() with PHPUnit\\Framework\\Error\\Warning is deprecated and will be removed in PHPUnit 10. Use expectWarning() instead.', - 'Support for using expectException() with PHPUnit\\Framework\\Error\\Error is deprecated and will be removed in PHPUnit 10. Use expectError() instead.', - 'assertDirectoryNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertDirectoryIsNotWritable() instead.', - 'assertFileNotIsWritable() is deprecated and will be removed in PHPUnit 10. Refactor your code to use assertFileIsNotWritable() instead.', - // cspell:disable-next-line - 'The at() matcher has been deprecated. It will be removed in PHPUnit 10. Please refactor your test to not rely on the order in which methods are invoked.', - // PHPUnit 9.6. - 'Expecting E_WARNING and E_USER_WARNING is deprecated and will no longer be possible in PHPUnit 10.', - 'Expecting E_ERROR and E_USER_ERROR is deprecated and will no longer be possible in PHPUnit 10.', - 'assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() instead.', - 'assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() instead.', - ]; - - /** - * Converts PHPUnit deprecation warnings to E_USER_DEPRECATED. - * - * @param string $warning - * The warning message raised in tests. - * - * @see \PHPUnit\Framework\TestCase::addWarning() - * - * @internal - */ - public function addWarning(string $warning): void { - if (in_array($warning, self::$deprecationWarnings, TRUE)) { - // Convert listed PHPUnit deprecations into E_USER_DEPRECATED and prevent - // each from being raised as a test warning. - @trigger_error($warning, E_USER_DEPRECATED); - return; - } - - // Otherwise, let the parent raise any warning not specifically listed. - parent::addWarning($warning); - } - -} diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php index 2ca419d204be..6bff44c08544 100644 --- a/core/tests/Drupal/Tests/UnitTestCase.php +++ b/core/tests/Drupal/Tests/UnitTestCase.php @@ -10,12 +10,11 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\StringTranslation\PluralTranslatableMarkup; -use Drupal\Tests\Traits\PhpUnitWarnings; +use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait; use Drupal\TestTools\TestVarDumper; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\VarDumper\VarDumper; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** * Provides a base class and helpers for Drupal unit tests. @@ -27,7 +26,6 @@ */ abstract class UnitTestCase extends TestCase { - use PhpUnitWarnings; use PhpUnitCompatibilityTrait; use ProphecyTrait; use ExpectDeprecationTrait; diff --git a/core/tests/TestSuites/BuildTestSuite.php b/core/tests/TestSuites/BuildTestSuite.php deleted file mode 100644 index c26bf44e59f1..000000000000 --- a/core/tests/TestSuites/BuildTestSuite.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\TestSuites; - -require_once __DIR__ . '/TestSuiteBase.php'; - -/** - * Discovers tests for the build test suite. - * - * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no - * replacement and test discovery will be handled differently in PHPUnit 10. - * - * @see https://www.drupal.org/node/3405829 - */ -class BuildTestSuite extends TestSuiteBase { - - /** - * Factory method which loads up a suite with all build tests. - * - * @return static - * The test suite. - */ - public static function suite() { - $root = dirname(__DIR__, 3); - - $suite = new static('build'); - $suite->addTestsBySuiteNamespace($root, 'Build'); - - return $suite; - } - -} diff --git a/core/tests/TestSuites/FunctionalJavascriptTestSuite.php b/core/tests/TestSuites/FunctionalJavascriptTestSuite.php deleted file mode 100644 index 5ae2325121b9..000000000000 --- a/core/tests/TestSuites/FunctionalJavascriptTestSuite.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\TestSuites; - -require_once __DIR__ . '/TestSuiteBase.php'; - -/** - * Discovers tests for the functional-javascript test suite. - * - * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no - * replacement and test discovery will be handled differently in PHPUnit 10. - * - * @see https://www.drupal.org/node/3405829 - */ -class FunctionalJavascriptTestSuite extends TestSuiteBase { - - /** - * Factory method which loads up a suite with all functional javascript tests. - * - * @return static - * The test suite. - */ - public static function suite() { - $root = dirname(__DIR__, 3); - - $suite = new static('functional-javascript'); - $suite->addTestsBySuiteNamespace($root, 'FunctionalJavascript'); - - return $suite; - } - -} diff --git a/core/tests/TestSuites/FunctionalTestSuite.php b/core/tests/TestSuites/FunctionalTestSuite.php deleted file mode 100644 index 20aed98deec6..000000000000 --- a/core/tests/TestSuites/FunctionalTestSuite.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\TestSuites; - -require_once __DIR__ . '/TestSuiteBase.php'; - -/** - * Discovers tests for the functional test suite. - * - * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no - * replacement and test discovery will be handled differently in PHPUnit 10. - * - * @see https://www.drupal.org/node/3405829 - */ -class FunctionalTestSuite extends TestSuiteBase { - - /** - * Factory method which loads up a suite with all functional tests. - * - * @return static - * The test suite. - */ - public static function suite() { - $root = dirname(__DIR__, 3); - - $suite = new static('functional'); - $suite->addTestsBySuiteNamespace($root, 'Functional'); - - return $suite; - } - -} diff --git a/core/tests/TestSuites/KernelTestSuite.php b/core/tests/TestSuites/KernelTestSuite.php deleted file mode 100644 index 0916c8528fac..000000000000 --- a/core/tests/TestSuites/KernelTestSuite.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\TestSuites; - -require_once __DIR__ . '/TestSuiteBase.php'; - -/** - * Discovers tests for the kernel test suite. - * - * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no - * replacement and test discovery will be handled differently in PHPUnit 10. - * - * @see https://www.drupal.org/node/3405829 - */ -class KernelTestSuite extends TestSuiteBase { - - /** - * Factory method which loads up a suite with all kernel tests. - * - * @return static - * The test suite. - */ - public static function suite() { - $root = dirname(__DIR__, 3); - - $suite = new static('kernel'); - $suite->addTestsBySuiteNamespace($root, 'Kernel'); - - return $suite; - } - -} diff --git a/core/tests/TestSuites/TestSuiteBase.php b/core/tests/TestSuites/TestSuiteBase.php deleted file mode 100644 index 435116d86584..000000000000 --- a/core/tests/TestSuites/TestSuiteBase.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\TestSuites; - -use Drupal\Core\Test\TestDiscovery; -use PHPUnit\Framework\TestSuite; - -/** - * Base class for Drupal test suites. - * - * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no - * replacement and test discovery will be handled differently in PHPUnit 10. - * - * @see https://www.drupal.org/node/3405829 - */ -abstract class TestSuiteBase extends TestSuite { - - /** - * Finds extensions in a Drupal installation. - * - * An extension is defined as a directory with an *.info.yml file in it. - * - * @param string $root - * Path to the root of the Drupal installation. - * - * @return string[] - * Associative array of extension paths, with extension name as keys. - */ - protected function findExtensionDirectories($root) { - $extension_roots = \drupal_phpunit_contrib_extension_directory_roots($root); - - $extension_directories = array_map('drupal_phpunit_find_extension_directories', $extension_roots); - return array_reduce($extension_directories, 'array_merge', []); - } - - /** - * Find and add tests to the suite for core and any extensions. - * - * @param string $root - * Path to the root of the Drupal installation. - * @param string $suite_namespace - * SubNamespace used to separate test suite. Examples: Unit, Functional. - */ - protected function addTestsBySuiteNamespace($root, $suite_namespace) { - // Core's tests are in the namespace Drupal\{$suite_namespace}Tests\ and are - // always inside of core/tests/Drupal/{$suite_namespace}Tests. The exception - // to this is Unit tests for historical reasons. - if ($suite_namespace == 'Unit') { - $tests = TestDiscovery::scanDirectory("Drupal\\Tests\\", "$root/core/tests/Drupal/Tests"); - $tests = array_flip(array_filter(array_flip($tests), function ($test_class) { - // The Listeners directory does not contain tests. Use the class name - // to be compatible with all operating systems. - return !preg_match('/^Drupal\\\\Tests\\\\Listeners\\\\/', $test_class); - })); - $this->addTestFiles($tests); - } - else { - $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\{$suite_namespace}Tests\\", "$root/core/tests/Drupal/{$suite_namespace}Tests")); - } - - // Extensions' tests will always be in the namespace - // Drupal\Tests\$extension_name\$suite_namespace\ and be in the - // $extension_path/tests/src/$suite_namespace directory. Not all extensions - // will have all kinds of tests. - foreach ($this->findExtensionDirectories($root) as $extension_name => $dir) { - $test_path = "$dir/tests/src/$suite_namespace"; - if (is_dir($test_path)) { - $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\Tests\\$extension_name\\$suite_namespace\\", $test_path)); - } - } - } - -} diff --git a/core/tests/TestSuites/UnitTestSuite.php b/core/tests/TestSuites/UnitTestSuite.php deleted file mode 100644 index db779652db46..000000000000 --- a/core/tests/TestSuites/UnitTestSuite.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\TestSuites; - -require_once __DIR__ . '/TestSuiteBase.php'; - -/** - * Discovers tests for the unit test suite. - * - * @deprecated in drupal:10.3.0 and is removed from drupal:11.0.0. There is no - * replacement and test discovery will be handled differently in PHPUnit 10. - * - * @see https://www.drupal.org/node/3405829 - */ -class UnitTestSuite extends TestSuiteBase { - - /** - * Factory method which loads up a suite with all unit tests. - * - * @return static - * The test suite. - */ - public static function suite() { - $root = dirname(__DIR__, 3); - - $suite = new static('unit'); - $suite->addTestsBySuiteNamespace($root, 'Unit'); - - return $suite; - } - -} diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php index 05479c1d0e58..56f7cd68a247 100644 --- a/core/tests/bootstrap.php +++ b/core/tests/bootstrap.php @@ -7,7 +7,11 @@ * @see phpunit.xml.dist */ -use Drupal\TestTools\PhpUnitCompatibility\ClassWriter; +use Drupal\TestTools\ErrorHandler\BootstrapErrorHandler; +use Drupal\TestTools\Extension\DeprecationBridge\DeprecationHandler; +use Drupal\TestTools\Extension\HtmlLogging\HtmlOutputLogger; +use PHPUnit\Runner\ErrorHandler as PhpUnitErrorHandler; +use Symfony\Component\ErrorHandler\DebugClassLoader; /** * Finds all valid extension directories recursively within a given directory. @@ -142,8 +146,6 @@ function drupal_phpunit_populate_class_loader() { $loader = drupal_phpunit_populate_class_loader(); class_alias('\Drupal\Tests\DocumentElement', '\Behat\Mink\Element\DocumentElement', TRUE); -ClassWriter::mutateTestBase($loader); - // Set sane locale settings, to ensure consistent string, dates, times and // numbers handling. // @see \Drupal\Core\DrupalKernel::bootEnvironment() @@ -160,11 +162,26 @@ class_alias('\Drupal\Tests\DocumentElement', '\Behat\Mink\Element\DocumentElemen // reduce the fragility of the testing system in general. date_default_timezone_set('Australia/Sydney'); -// Ensure ignored deprecation patterns listed in .deprecation-ignore.txt are -// considered in testing. -if (getenv('SYMFONY_DEPRECATIONS_HELPER') === FALSE) { - $deprecation_ignore_filename = realpath(__DIR__ . "/../.deprecation-ignore.txt"); - putenv("SYMFONY_DEPRECATIONS_HELPER=ignoreFile=$deprecation_ignore_filename"); +// Bootstrap the DeprecationHandler extension and the DebugClassloader to report +// deprecations in PHPUnit 10+. +if ($deprecationBridgeConfiguration = DeprecationHandler::getConfiguration()) { + DeprecationHandler::init($deprecationBridgeConfiguration['ignoreFile'] ?? NULL); + + // Need to have an early error handler to manage deprecations triggered by + // DebugClassLoader, that occur before tests' setUp() methods are called. + // We pass an instance of the PHPUnit error handler to redirect any error not + // managed by our layer back to PHPUnit. + set_error_handler(new BootstrapErrorHandler(new PhpUnitErrorHandler())); + + // Enable the DebugClassLoader to get deprecations for methods' signature + // changes. + DebugClassLoader::enable(); +} + +// Functional tests HTML output logging. +$browserTestOutputDirectory = getenv('BROWSERTEST_OUTPUT_DIRECTORY'); +if ($browserTestOutputDirectory !== FALSE) { + HtmlOutputLogger::init($browserTestOutputDirectory, (bool) getenv('BROWSERTEST_OUTPUT_VERBOSE') ?? FALSE); } // Drupal expects to be run from its root directory. This ensures all test types -- GitLab