diff --git a/.appveyor.yml b/.appveyor.yml
index 889aafe26929b..fddaf7b75802c 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -1,6 +1,7 @@
build: false
clone_depth: 2
clone_folder: c:\projects\symfony
+image: Visual Studio 2019
cache:
- composer.phar
@@ -16,17 +17,17 @@ init:
install:
- mkdir c:\php && cd c:\php
- - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-7.1.3-Win32-VC14-x86.zip
- - 7z x php-7.1.3-Win32-VC14-x86.zip -y >nul
+ - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php-8.0.2-Win32-vs16-x86.zip
+ - 7z x php-8.0.2-Win32-vs16-x86.zip -y >nul
- cd ext
- - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-5.1.18-7.1-ts-vc14-x86.zip
- - 7z x php_apcu-5.1.18-7.1-ts-vc14-x86.zip -y >nul
- - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_redis-5.1.1-7.1-ts-vc14-x86.zip
- - 7z x php_redis-5.1.1-7.1-ts-vc14-x86.zip -y >nul
+ - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-5.1.21-8.0-ts-vs16-x86.zip
+ - 7z x php_apcu-5.1.21-8.0-ts-vs16-x86.zip -y >nul
+ - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_redis-5.3.5-8.0-ts-vs16-x86.zip
+ - 7z x php_redis-5.3.5-8.0-ts-vs16-x86.zip -y >nul
- cd ..
- copy /Y php.ini-development php.ini-min
- echo memory_limit=-1 >> php.ini-min
- - echo serialize_precision=14 >> php.ini-min
+ - echo serialize_precision=-1 >> php.ini-min
- echo max_execution_time=1200 >> php.ini-min
- echo post_max_size=4G >> php.ini-min
- echo upload_max_filesize=4G >> php.ini-min
@@ -45,6 +46,7 @@ install:
- echo extension=php_fileinfo.dll >> php.ini-max
- echo extension=php_pdo_sqlite.dll >> php.ini-max
- echo extension=php_curl.dll >> php.ini-max
+ - echo extension=php_sodium.dll >> php.ini-max
- copy /Y php.ini-max php.ini
- cd c:\projects\symfony
- IF NOT EXIST composer.phar (appveyor DownloadFile https://github.com/composer/composer/releases/download/2.0.0/composer.phar)
@@ -64,7 +66,10 @@ test_script:
- SET SYMFONY_PHPUNIT_SKIPPED_TESTS=phpunit.skipped
- copy /Y c:\php\php.ini-min c:\php\php.ini
- IF %APPVEYOR_REPO_BRANCH:~-2% neq .x (rm -Rf src\Symfony\Bridge\PhpUnit)
+ - mv src\Symfony\Component\HttpClient\phpunit.xml.dist src\Symfony\Component\HttpClient\phpunit.xml
- php phpunit src\Symfony --exclude-group tty,benchmark,intl-data,network,transient-on-windows || SET X=!errorlevel!
+ - php phpunit src\Symfony\Component\HttpClient || SET X=!errorlevel!
- copy /Y c:\php\php.ini-max c:\php\php.ini
- php phpunit src\Symfony --exclude-group tty,benchmark,intl-data,network,transient-on-windows || SET X=!errorlevel!
+ - php phpunit src\Symfony\Component\HttpClient || SET X=!errorlevel!
- exit %X%
diff --git a/.gitattributes b/.gitattributes
index c255f66722075..d30fb22a3bdbb 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,6 @@
/src/Symfony/Contracts export-ignore
/src/Symfony/Bridge/PhpUnit export-ignore
+/src/Symfony/Component/Mailer/Bridge export-ignore
+/src/Symfony/Component/Messenger/Bridge export-ignore
+/src/Symfony/Component/Notifier/Bridge export-ignore
+/src/Symfony/Component/Runtime export-ignore
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 0870dcfdd5cc4..b958112d1b58a 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -31,8 +31,12 @@
# Messenger
/src/Symfony/Bridge/Doctrine/Messenger/ @sroze
/src/Symfony/Component/Messenger/ @sroze
+# Notifer
+/src/Symfony/Component/Notifier/ @OskarStark
# OptionsResolver
/src/Symfony/Component/OptionsResolver/ @yceruto
+# PasswordHasher
+/src/Symfony/Component/PasswordHasher/ @chalasr
# PropertyInfo
/src/Symfony/Component/PropertyInfo/ @dunglas
/src/Symfony/Bridge/Doctrine/PropertyInfo/ @dunglas
diff --git a/.github/composer-config.json b/.github/composer-config.json
index 6e17bc21e4582..65919964fa8a1 100644
--- a/.github/composer-config.json
+++ b/.github/composer-config.json
@@ -4,7 +4,10 @@
"preferred-install": {
"symfony/form": "source",
"symfony/http-kernel": "source",
+ "symfony/messenger": "source",
+ "symfony/notifier": "source",
"symfony/proxy-manager-bridge": "source",
+ "symfony/translation": "source",
"symfony/validator": "source",
"*": "dist"
}
diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff
new file mode 100644
index 0000000000000..d45a91c61c7b8
--- /dev/null
+++ b/.github/expected-missing-return-types.diff
@@ -0,0 +1,1015 @@
+# Run these steps to update this file:
+sed -i 's/ *"\*\*\/Tests\/"//' composer.json
+composer u -o
+SYMFONY_PATCH_TYPE_DECLARATIONS='force=2&php=8.0' php .github/patch-types.php
+head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff)
+(echo "$head" && echo && git diff -U2 composer.json src/) > .github/expected-missing-return-types.diff
+
+diff --git a/composer.json b/composer.json
+index 978743d34d..1f185f682c 100644
+--- a/composer.json
++++ b/composer.json
+@@ -180,5 +180,5 @@
+ ],
+ "exclude-from-classmap": [
+- "**/Tests/"
++
+ ]
+ },
+diff --git a/src/Symfony/Component/BrowserKit/AbstractBrowser.php b/src/Symfony/Component/BrowserKit/AbstractBrowser.php
+index 152050159b..e2ec1aeea2 100644
+--- a/src/Symfony/Component/BrowserKit/AbstractBrowser.php
++++ b/src/Symfony/Component/BrowserKit/AbstractBrowser.php
+@@ -408,5 +408,5 @@ abstract class AbstractBrowser
+ * @throws \RuntimeException When processing returns exit code
+ */
+- protected function doRequestInProcess(object $request)
++ protected function doRequestInProcess(object $request): object
+ {
+ $deprecationsFile = tempnam(sys_get_temp_dir(), 'deprec');
+@@ -441,5 +441,5 @@ abstract class AbstractBrowser
+ * @return object
+ */
+- abstract protected function doRequest(object $request);
++ abstract protected function doRequest(object $request): object;
+
+ /**
+@@ -460,5 +460,5 @@ abstract class AbstractBrowser
+ * @return object
+ */
+- protected function filterRequest(Request $request)
++ protected function filterRequest(Request $request): object
+ {
+ return $request;
+@@ -470,5 +470,5 @@ abstract class AbstractBrowser
+ * @return Response
+ */
+- protected function filterResponse(object $response)
++ protected function filterResponse(object $response): Response
+ {
+ return $response;
+diff --git a/src/Symfony/Component/Config/Definition/ConfigurationInterface.php b/src/Symfony/Component/Config/Definition/ConfigurationInterface.php
+index 7b5d443fe6..d64ae0d024 100644
+--- a/src/Symfony/Component/Config/Definition/ConfigurationInterface.php
++++ b/src/Symfony/Component/Config/Definition/ConfigurationInterface.php
+@@ -26,4 +26,4 @@ interface ConfigurationInterface
+ * @return TreeBuilder
+ */
+- public function getConfigTreeBuilder();
++ public function getConfigTreeBuilder(): TreeBuilder;
+ }
+diff --git a/src/Symfony/Component/Config/FileLocator.php b/src/Symfony/Component/Config/FileLocator.php
+index 21122e52c9..206029e705 100644
+--- a/src/Symfony/Component/Config/FileLocator.php
++++ b/src/Symfony/Component/Config/FileLocator.php
+@@ -34,5 +34,5 @@ class FileLocator implements FileLocatorInterface
+ * {@inheritdoc}
+ */
+- public function locate(string $name, string $currentPath = null, bool $first = true)
++ public function locate(string $name, string $currentPath = null, bool $first = true): string|array
+ {
+ if ('' === $name) {
+diff --git a/src/Symfony/Component/Config/FileLocatorInterface.php b/src/Symfony/Component/Config/FileLocatorInterface.php
+index e3ca1d49c4..526d350484 100644
+--- a/src/Symfony/Component/Config/FileLocatorInterface.php
++++ b/src/Symfony/Component/Config/FileLocatorInterface.php
+@@ -31,4 +31,4 @@ interface FileLocatorInterface
+ * @throws FileLocatorFileNotFoundException If a file is not found
+ */
+- public function locate(string $name, string $currentPath = null, bool $first = true);
++ public function locate(string $name, string $currentPath = null, bool $first = true): string|array;
+ }
+diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php
+index c479f75d34..0d16baaff7 100644
+--- a/src/Symfony/Component/Config/Loader/FileLoader.php
++++ b/src/Symfony/Component/Config/Loader/FileLoader.php
+@@ -69,5 +69,5 @@ abstract class FileLoader extends Loader
+ * @throws FileLocatorFileNotFoundException
+ */
+- public function import(mixed $resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null, string|array $exclude = null)
++ public function import(mixed $resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null, string|array $exclude = null): mixed
+ {
+ if (\is_string($resource) && \strlen($resource) !== ($i = strcspn($resource, '*?{[')) && !str_contains($resource, "\n")) {
+diff --git a/src/Symfony/Component/Config/Loader/Loader.php b/src/Symfony/Component/Config/Loader/Loader.php
+index e0974fb151..f698b5271b 100644
+--- a/src/Symfony/Component/Config/Loader/Loader.php
++++ b/src/Symfony/Component/Config/Loader/Loader.php
+@@ -50,5 +50,5 @@ abstract class Loader implements LoaderInterface
+ * @return mixed
+ */
+- public function import(mixed $resource, string $type = null)
++ public function import(mixed $resource, string $type = null): mixed
+ {
+ return $this->resolve($resource, $type)->load($resource, $type);
+diff --git a/src/Symfony/Component/Config/Loader/LoaderInterface.php b/src/Symfony/Component/Config/Loader/LoaderInterface.php
+index b94a4378f5..db502e12a7 100644
+--- a/src/Symfony/Component/Config/Loader/LoaderInterface.php
++++ b/src/Symfony/Component/Config/Loader/LoaderInterface.php
+@@ -26,5 +26,5 @@ interface LoaderInterface
+ * @throws \Exception If something went wrong
+ */
+- public function load(mixed $resource, string $type = null);
++ public function load(mixed $resource, string $type = null): mixed;
+
+ /**
+@@ -35,5 +35,5 @@ interface LoaderInterface
+ * @return bool
+ */
+- public function supports(mixed $resource, string $type = null);
++ public function supports(mixed $resource, string $type = null): bool;
+
+ /**
+@@ -42,5 +42,5 @@ interface LoaderInterface
+ * @return LoaderResolverInterface
+ */
+- public function getResolver();
++ public function getResolver(): LoaderResolverInterface;
+
+ /**
+diff --git a/src/Symfony/Component/Config/ResourceCheckerInterface.php b/src/Symfony/Component/Config/ResourceCheckerInterface.php
+index 6b1c6c5fbe..bb80ed461e 100644
+--- a/src/Symfony/Component/Config/ResourceCheckerInterface.php
++++ b/src/Symfony/Component/Config/ResourceCheckerInterface.php
+@@ -33,5 +33,5 @@ interface ResourceCheckerInterface
+ * @return bool
+ */
+- public function supports(ResourceInterface $metadata);
++ public function supports(ResourceInterface $metadata): bool;
+
+ /**
+@@ -42,4 +42,4 @@ interface ResourceCheckerInterface
+ * @return bool
+ */
+- public function isFresh(ResourceInterface $resource, int $timestamp);
++ public function isFresh(ResourceInterface $resource, int $timestamp): bool;
+ }
+diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
+index 09234f5eb5..24d995ad6d 100644
+--- a/src/Symfony/Component/Console/Application.php
++++ b/src/Symfony/Component/Console/Application.php
+@@ -218,5 +218,5 @@ class Application implements ResetInterface
+ * @return int 0 if everything went fine, or an error code
+ */
+- public function doRun(InputInterface $input, OutputInterface $output)
++ public function doRun(InputInterface $input, OutputInterface $output): int
+ {
+ if (true === $input->hasParameterOption(['--version', '-V'], true)) {
+@@ -445,5 +445,5 @@ class Application implements ResetInterface
+ * @return string
+ */
+- public function getLongVersion()
++ public function getLongVersion(): string
+ {
+ if ('UNKNOWN' !== $this->getName()) {
+@@ -488,5 +488,5 @@ class Application implements ResetInterface
+ * @return Command|null
+ */
+- public function add(Command $command)
++ public function add(Command $command): ?Command
+ {
+ $this->init();
+@@ -525,5 +525,5 @@ class Application implements ResetInterface
+ * @throws CommandNotFoundException When given command name does not exist
+ */
+- public function get(string $name)
++ public function get(string $name): Command
+ {
+ $this->init();
+@@ -632,5 +632,5 @@ class Application implements ResetInterface
+ * @throws CommandNotFoundException When command name is incorrect or ambiguous
+ */
+- public function find(string $name)
++ public function find(string $name): Command
+ {
+ $this->init();
+@@ -742,5 +742,5 @@ class Application implements ResetInterface
+ * @return Command[]
+ */
+- public function all(string $namespace = null)
++ public function all(string $namespace = null): array
+ {
+ $this->init();
+@@ -941,5 +941,5 @@ class Application implements ResetInterface
+ * @return int 0 if everything went fine, or an error code
+ */
+- protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
++ protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int
+ {
+ foreach ($command->getHelperSet() as $helper) {
+diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
+index e69bae0982..3390628d0d 100644
+--- a/src/Symfony/Component/Console/Command/Command.php
++++ b/src/Symfony/Component/Console/Command/Command.php
+@@ -171,5 +171,5 @@ class Command
+ * @return bool
+ */
+- public function isEnabled()
++ public function isEnabled(): bool
+ {
+ return true;
+@@ -197,5 +197,5 @@ class Command
+ * @see setCode()
+ */
+- protected function execute(InputInterface $input, OutputInterface $output)
++ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ throw new LogicException('You must override the execute() method in the concrete command class.');
+diff --git a/src/Symfony/Component/Console/Helper/HelperInterface.php b/src/Symfony/Component/Console/Helper/HelperInterface.php
+index 1d2b7bfb84..cb1f66152d 100644
+--- a/src/Symfony/Component/Console/Helper/HelperInterface.php
++++ b/src/Symfony/Component/Console/Helper/HelperInterface.php
+@@ -34,4 +34,4 @@ interface HelperInterface
+ * @return string
+ */
+- public function getName();
++ public function getName(): string;
+ }
+diff --git a/src/Symfony/Component/Console/Input/InputInterface.php b/src/Symfony/Component/Console/Input/InputInterface.php
+index 024da1884e..943790e875 100644
+--- a/src/Symfony/Component/Console/Input/InputInterface.php
++++ b/src/Symfony/Component/Console/Input/InputInterface.php
+@@ -54,5 +54,5 @@ interface InputInterface
+ * @return mixed
+ */
+- public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false);
++ public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed;
+
+ /**
+@@ -84,5 +84,5 @@ interface InputInterface
+ * @throws InvalidArgumentException When argument given doesn't exist
+ */
+- public function getArgument(string $name);
++ public function getArgument(string $name): mixed;
+
+ /**
+@@ -112,5 +112,5 @@ interface InputInterface
+ * @throws InvalidArgumentException When option given doesn't exist
+ */
+- public function getOption(string $name);
++ public function getOption(string $name): mixed;
+
+ /**
+diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+index e9fa5a6808..016e9d893a 100644
+--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
++++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+@@ -71,5 +71,5 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
+ * @return mixed
+ */
+- protected function processValue(mixed $value, bool $isRoot = false)
++ protected function processValue(mixed $value, bool $isRoot = false): mixed
+ {
+ if (\is_array($value)) {
+diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php
+index 0532120adf..78fba5ef79 100644
+--- a/src/Symfony/Component/DependencyInjection/Container.php
++++ b/src/Symfony/Component/DependencyInjection/Container.php
+@@ -108,5 +108,5 @@ class Container implements ContainerInterface, ResetInterface
+ * @throws InvalidArgumentException if the parameter is not defined
+ */
+- public function getParameter(string $name)
++ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null
+ {
+ return $this->parameterBag->get($name);
+diff --git a/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
+index aa5d6b317e..31ffbca4ef 100644
+--- a/src/Symfony/Component/DependencyInjection/ContainerInterface.php
++++ b/src/Symfony/Component/DependencyInjection/ContainerInterface.php
+@@ -53,5 +53,5 @@ interface ContainerInterface extends PsrContainerInterface
+ * @throws InvalidArgumentException if the parameter is not defined
+ */
+- public function getParameter(string $name);
++ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null;
+
+ public function hasParameter(string $name): bool;
+diff --git a/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php
+index a42967f4da..4e86e16f9d 100644
+--- a/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php
++++ b/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php
+@@ -27,4 +27,4 @@ interface ConfigurationExtensionInterface
+ * @return ConfigurationInterface|null
+ */
+- public function getConfiguration(array $config, ContainerBuilder $container);
++ public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface;
+ }
+diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
+index d553203c43..1163f4b107 100644
+--- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php
++++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php
+@@ -32,5 +32,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn
+ * {@inheritdoc}
+ */
+- public function getXsdValidationBasePath()
++ public function getXsdValidationBasePath(): string|false
+ {
+ return false;
+@@ -40,5 +40,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn
+ * {@inheritdoc}
+ */
+- public function getNamespace()
++ public function getNamespace(): string
+ {
+ return 'http://example.org/schema/dic/'.$this->getAlias();
+@@ -77,5 +77,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn
+ * {@inheritdoc}
+ */
+- public function getConfiguration(array $config, ContainerBuilder $container)
++ public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface
+ {
+ $class = static::class;
+diff --git a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
+index f2373ed5ea..1eec21a938 100644
+--- a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
++++ b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php
+@@ -33,5 +33,5 @@ interface ExtensionInterface
+ * @return string
+ */
+- public function getNamespace();
++ public function getNamespace(): string;
+
+ /**
+@@ -40,5 +40,5 @@ interface ExtensionInterface
+ * @return string|false
+ */
+- public function getXsdValidationBasePath();
++ public function getXsdValidationBasePath(): string|false;
+
+ /**
+@@ -49,4 +49,4 @@ interface ExtensionInterface
+ * @return string
+ */
+- public function getAlias();
++ public function getAlias(): string;
+ }
+diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php
+index a9d78115dd..8b3b420a9c 100644
+--- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php
++++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php
+@@ -31,4 +31,4 @@ interface InstantiatorInterface
+ * @return object
+ */
+- public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator);
++ public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator): object;
+ }
+diff --git a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php
+index 2085e428e9..ca0d6964e5 100644
+--- a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php
++++ b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php
+@@ -46,4 +46,4 @@ interface EventSubscriberInterface
+ * @return array
- * - * @internal - */ -class SymfonyTestsListenerForV5 extends \PHPUnit_Framework_BaseTestListener -{ - private $trait; - - public function __construct(array $mockedNamespaces = []) - { - $this->trait = new SymfonyTestsListenerTrait($mockedNamespaces); - } - - public function globalListenerDisabled() - { - $this->trait->globalListenerDisabled(); - } - - public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) - { - $this->trait->startTestSuite($suite); - } - - public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) - { - $this->trait->addSkippedTest($test, $e, $time); - } - - public function startTest(\PHPUnit_Framework_Test $test) - { - $this->trait->startTest($test); - } - - public function endTest(\PHPUnit_Framework_Test $test, $time) - { - $this->trait->endTest($test, $time); - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV6.php deleted file mode 100644 index 8f2f6b5a7ed54..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerForV6.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\Framework\BaseTestListener; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestSuite; - -/** - * Collects and replays skipped tests. - * - * @author Nicolas Grekas
- *
- * @internal
- */
-class SymfonyTestsListenerForV6 extends BaseTestListener
-{
- private $trait;
-
- public function __construct(array $mockedNamespaces = [])
- {
- $this->trait = new SymfonyTestsListenerTrait($mockedNamespaces);
- }
-
- public function globalListenerDisabled()
- {
- $this->trait->globalListenerDisabled();
- }
-
- public function startTestSuite(TestSuite $suite)
- {
- $this->trait->startTestSuite($suite);
- }
-
- public function addSkippedTest(Test $test, \Exception $e, $time)
- {
- $this->trait->addSkippedTest($test, $e, $time);
- }
-
- public function startTest(Test $test)
- {
- $this->trait->startTest($test);
- }
-
- public function endTest(Test $test, $time)
- {
- $this->trait->endTest($test, $time);
- }
-}
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
index 0f9238bdd9c1c..c84ec1b66ddbd 100644
--- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php
@@ -13,6 +13,7 @@
use Doctrine\Common\Annotations\AnnotationRegistry;
use PHPUnit\Framework\AssertionFailedError;
+use PHPUnit\Framework\RiskyTestError;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Runner\BaseTestRunner;
@@ -21,6 +22,7 @@
use PHPUnit\Util\Test;
use Symfony\Bridge\PhpUnit\ClockMock;
use Symfony\Bridge\PhpUnit\DnsMock;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
use Symfony\Component\ErrorHandler\DebugClassLoader;
@@ -33,16 +35,16 @@
*/
class SymfonyTestsListenerTrait
{
+ public static $expectedDeprecations = [];
+ public static $previousErrorHandler;
+ private static $gatheredDeprecations = [];
private static $globallyEnabled = false;
private $state = -1;
private $skippedFile = false;
private $wasSkipped = [];
private $isSkipped = [];
- private $expectedDeprecations = [];
- private $gatheredDeprecations = [];
- private $previousErrorHandler;
- private $error;
private $runsInSeparateProcess = false;
+ private $checkNumAssertions = false;
/**
* @param array $mockedNamespaces List of namespaces, indexed by mocked features (time-sensitive or dns-sensitive)
@@ -121,7 +123,7 @@ public function startTestSuite($suite)
$suiteName = $suite->getName();
foreach ($suite->tests() as $test) {
- if (!($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) {
+ if (!$test instanceof TestCase) {
continue;
}
if (null === Test::getPreserveGlobalStateSettings(\get_class($test), $test->getName(false))) {
@@ -156,7 +158,7 @@ public function startTestSuite($suite)
$testSuites = [$suite];
for ($i = 0; isset($testSuites[$i]); ++$i) {
foreach ($testSuites[$i]->tests() as $test) {
- if ($test instanceof \PHPUnit_Framework_TestSuite || $test instanceof TestSuite) {
+ if ($test instanceof TestSuite) {
if (!class_exists($test->getName(), false)) {
$testSuites[] = $test;
continue;
@@ -176,11 +178,11 @@ public function startTestSuite($suite)
$skipped = [];
while ($s = array_shift($suites)) {
foreach ($s->tests() as $test) {
- if ($test instanceof \PHPUnit_Framework_TestSuite || $test instanceof TestSuite) {
+ if ($test instanceof TestSuite) {
$suites[] = $test;
continue;
}
- if (($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)
+ if ($test instanceof TestCase
&& isset($this->wasSkipped[\get_class($test)][$test->getName()])
) {
$skipped[] = $test;
@@ -200,11 +202,12 @@ public function addSkippedTest($test, \Exception $e, $time)
public function startTest($test)
{
- if (-2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) {
+ if (-2 < $this->state && $test instanceof TestCase) {
// This event is triggered before the test is re-run in isolation
if ($this->willBeIsolated($test)) {
$this->runsInSeparateProcess = tempnam(sys_get_temp_dir(), 'deprec');
putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$this->runsInSeparateProcess);
+ putenv('SYMFONY_EXPECTED_DEPRECATIONS_SERIALIZE='.tempnam(sys_get_temp_dir(), 'expectdeprec'));
}
$groups = Test::getGroups(\get_class($test), $test->getName(false));
@@ -228,21 +231,35 @@ public function startTest($test)
if (isset($annotations['class']['expectedDeprecation'])) {
$test->getTestResultObject()->addError($test, new AssertionFailedError('`@expectedDeprecation` annotations are not allowed at the class level.'), 0);
}
- if (isset($annotations['method']['expectedDeprecation'])) {
- if (!\in_array('legacy', $groups, true)) {
- $this->error = new AssertionFailedError('Only tests with the `@group legacy` annotation can have `@expectedDeprecation`.');
+ if (isset($annotations['method']['expectedDeprecation']) || $this->checkNumAssertions = method_exists($test, 'expectDeprecation') && (new \ReflectionMethod($test, 'expectDeprecation'))->getFileName() === (new \ReflectionMethod(ExpectDeprecationTrait::class, 'expectDeprecation'))->getFileName()) {
+ if (isset($annotations['method']['expectedDeprecation'])) {
+ self::$expectedDeprecations = $annotations['method']['expectedDeprecation'];
+ self::$previousErrorHandler = set_error_handler([self::class, 'handleError']);
+ @trigger_error('Since symfony/phpunit-bridge 5.1: Using "@expectedDeprecation" annotations in tests is deprecated, use the "ExpectDeprecationTrait::expectDeprecation()" method instead.', \E_USER_DEPRECATED);
}
- $test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false);
+ if ($this->checkNumAssertions) {
+ $this->checkNumAssertions = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything();
+ }
- $this->expectedDeprecations = $annotations['method']['expectedDeprecation'];
- $this->previousErrorHandler = set_error_handler([$this, 'handleError']);
+ $test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything(false);
}
}
}
public function endTest($test, $time)
{
+ if ($file = getenv('SYMFONY_EXPECTED_DEPRECATIONS_SERIALIZE')) {
+ putenv('SYMFONY_EXPECTED_DEPRECATIONS_SERIALIZE');
+ $expectedDeprecations = file_get_contents($file);
+ if ($expectedDeprecations) {
+ self::$expectedDeprecations = array_merge(self::$expectedDeprecations, unserialize($expectedDeprecations));
+ if (!self::$previousErrorHandler) {
+ self::$previousErrorHandler = set_error_handler([self::class, 'handleError']);
+ }
+ }
+ }
+
if (class_exists(DebugClassLoader::class, false)) {
DebugClassLoader::checkClasses();
}
@@ -250,9 +267,15 @@ public function endTest($test, $time)
$className = \get_class($test);
$groups = Test::getGroups($className, $test->getName(false));
- if ($errored = null !== $this->error) {
- $test->getTestResultObject()->addError($test, $this->error, 0);
- $this->error = null;
+ if ($this->checkNumAssertions) {
+ $assertions = \count(self::$expectedDeprecations) + $test->getNumAssertions();
+ if ($test->doesNotPerformAssertions() && $assertions > 0) {
+ $test->getTestResultObject()->addFailure($test, new RiskyTestError(sprintf('This test is annotated with "@doesNotPerformAssertions", but performed %s assertions', $assertions)), $time);
+ } elseif ($assertions === 0 && $test->getTestResultObject()->noneSkipped()) {
+ $test->getTestResultObject()->addFailure($test, new RiskyTestError('This test did not perform any assertions'), $time);
+ }
+
+ $this->checkNumAssertions = false;
}
if ($this->runsInSeparateProcess) {
@@ -260,7 +283,7 @@ public function endTest($test, $time)
unlink($this->runsInSeparateProcess);
putenv('SYMFONY_DEPRECATIONS_SERIALIZE');
foreach ($deprecations ? unserialize($deprecations) : [] as $deprecation) {
- $error = serialize(['deprecation' => $deprecation[1], 'class' => $className, 'method' => $test->getName(false), 'triggering_file' => isset($deprecation[2]) ? $deprecation[2] : null, 'files_stack' => isset($deprecation[3]) ? $deprecation[3] : []]);
+ $error = serialize(['deprecation' => $deprecation[1], 'class' => $className, 'method' => $test->getName(false), 'triggering_file' => $deprecation[2] ?? null, 'files_stack' => $deprecation[3] ?? []]);
if ($deprecation[0]) {
// unsilenced on purpose
trigger_error($error, \E_USER_DEPRECATED);
@@ -271,26 +294,28 @@ public function endTest($test, $time)
$this->runsInSeparateProcess = false;
}
- if ($this->expectedDeprecations) {
+ if (self::$expectedDeprecations) {
if (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE], true)) {
- $test->addToAssertionCount(\count($this->expectedDeprecations));
+ $test->addToAssertionCount(\count(self::$expectedDeprecations));
}
restore_error_handler();
- if (!$errored && !\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
+ if (!\in_array('legacy', $groups, true)) {
+ $test->getTestResultObject()->addError($test, new AssertionFailedError('Only tests with the `@group legacy` annotation can expect a deprecation.'), 0);
+ } elseif (!\in_array($test->getStatus(), [BaseTestRunner::STATUS_SKIPPED, BaseTestRunner::STATUS_INCOMPLETE, BaseTestRunner::STATUS_FAILURE, BaseTestRunner::STATUS_ERROR], true)) {
try {
$prefix = "@expectedDeprecation:\n";
- $test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", $this->expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", $this->gatheredDeprecations)."\n");
+ $test->assertStringMatchesFormat($prefix.'%A '.implode("\n%A ", self::$expectedDeprecations)."\n%A", $prefix.' '.implode("\n ", self::$gatheredDeprecations)."\n");
} catch (AssertionFailedError $e) {
$test->getTestResultObject()->addFailure($test, $e, $time);
}
}
- $this->expectedDeprecations = $this->gatheredDeprecations = [];
- $this->previousErrorHandler = null;
+ self::$expectedDeprecations = self::$gatheredDeprecations = [];
+ self::$previousErrorHandler = null;
}
- if (!$this->runsInSeparateProcess && -2 < $this->state && ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase)) {
+ if (!$this->runsInSeparateProcess && -2 < $this->state && $test instanceof TestCase) {
if (\in_array('time-sensitive', $groups, true)) {
ClockMock::withClockMock(false);
}
@@ -300,10 +325,10 @@ public function endTest($test, $time)
}
}
- public function handleError($type, $msg, $file, $line, $context = [])
+ public static function handleError($type, $msg, $file, $line, $context = [])
{
if (\E_USER_DEPRECATED !== $type && \E_DEPRECATED !== $type) {
- $h = $this->previousErrorHandler;
+ $h = self::$previousErrorHandler;
return $h ? $h($type, $msg, $file, $line, $context) : false;
}
@@ -316,7 +341,7 @@ public function handleError($type, $msg, $file, $line, $context = [])
if (error_reporting() & $type) {
$msg = 'Unsilenced deprecation: '.$msg;
}
- $this->gatheredDeprecations[] = $msg;
+ self::$gatheredDeprecations[] = $msg;
return null;
}
diff --git a/src/Symfony/Bridge/PhpUnit/SetUpTearDownTrait.php b/src/Symfony/Bridge/PhpUnit/SetUpTearDownTrait.php
deleted file mode 100644
index e27c3a4fb0934..0000000000000
--- a/src/Symfony/Bridge/PhpUnit/SetUpTearDownTrait.php
+++ /dev/null
@@ -1,28 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\PhpUnit;
-
-use PHPUnit\Framework\TestCase;
-
-// A trait to provide forward compatibility with newest PHPUnit versions
-$r = new \ReflectionClass(TestCase::class);
-if (\PHP_VERSION_ID < 70000 || !$r->getMethod('setUp')->hasReturnType()) {
- trait SetUpTearDownTrait
- {
- use Legacy\SetUpTearDownTraitForV5;
- }
-} else {
- trait SetUpTearDownTrait
- {
- use Legacy\SetUpTearDownTraitForV8;
- }
-}
diff --git a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php
index d3cd7563bd41f..47f0f42afc8fd 100644
--- a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php
+++ b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php
@@ -11,13 +11,7 @@
namespace Symfony\Bridge\PhpUnit;
-if (version_compare(\PHPUnit\Runner\Version::id(), '6.0.0', '<')) {
- class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
-} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) {
- class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV6', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
-} else {
- class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
-}
+class_alias('Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7', 'Symfony\Bridge\PhpUnit\SymfonyTestsListener');
if (false) {
class SymfonyTestsListener
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/BootstrapTest.php b/src/Symfony/Bridge/PhpUnit/Tests/BootstrapTest.php
deleted file mode 100644
index d1811575087df..0000000000000
--- a/src/Symfony/Bridge/PhpUnit/Tests/BootstrapTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Bridge\PhpUnit\Tests;
-
-use PHPUnit\Framework\TestCase;
-
-class BootstrapTest extends TestCase
-{
- /**
- * @requires PHPUnit < 6.0
- */
- public function testAliasingOfErrorClasses()
- {
- $this->assertInstanceOf(
- \PHPUnit_Framework_Error::class,
- new \PHPUnit\Framework\Error\Error('message', 0, __FILE__, __LINE__)
- );
- $this->assertInstanceOf(
- \PHPUnit_Framework_Error_Deprecated::class,
- new \PHPUnit\Framework\Error\Deprecated('message', 0, __FILE__, __LINE__)
- );
- $this->assertInstanceOf(
- \PHPUnit_Framework_Error_Notice::class,
- new \PHPUnit\Framework\Error\Notice('message', 0, __FILE__, __LINE__)
- );
- $this->assertInstanceOf(
- \PHPUnit_Framework_Error_Warning::class,
- new \PHPUnit\Framework\Error\Warning('message', 0, __FILE__, __LINE__)
- );
- }
-}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php
index 53b2bb8d6cdff..b309606d5bd4e 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php
@@ -14,7 +14,7 @@ public function test()
exec('type phpdbg 2> /dev/null', $output, $returnCode);
- if (\PHP_VERSION_ID >= 70000 && 0 === $returnCode) {
+ if (0 === $returnCode) {
$php = 'phpdbg -qrr';
} else {
exec('php --ri xdebug -d zend_extension=xdebug.so 2> /dev/null', $output, $returnCode);
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php
index 39e792cd3a2cb..5d36a43bff54f 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php
@@ -13,9 +13,13 @@
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration;
+use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
+use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\DeprecationGroup;
class ConfigurationTest extends TestCase
{
+ private $files;
+
public function testItThrowsOnStringishValue()
{
$this->expectException(\InvalidArgumentException::class);
@@ -47,122 +51,122 @@ public function testItThrowsOnStringishThreshold()
public function testItNoticesExceededTotalThreshold()
{
$configuration = Configuration::fromUrlEncodedString('max[total]=3');
- $this->assertTrue($configuration->tolerates([
- 'unsilencedCount' => 1,
- 'remaining selfCount' => 0,
- 'legacyCount' => 1,
- 'otherCount' => 0,
- 'remaining directCount' => 1,
- 'remaining indirectCount' => 1,
- ]));
- $this->assertFalse($configuration->tolerates([
- 'unsilencedCount' => 1,
- 'remaining selfCount' => 1,
- 'legacyCount' => 1,
- 'otherCount' => 0,
- 'remaining directCount' => 1,
- 'remaining indirectCount' => 1,
- ]));
+ $this->assertTrue($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1,
+ 'self' => 0,
+ 'legacy' => 1,
+ 'other' => 0,
+ 'direct' => 1,
+ 'indirect' => 1,
+ ])));
+ $this->assertFalse($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1,
+ 'self' => 1,
+ 'legacy' => 1,
+ 'other' => 0,
+ 'direct' => 1,
+ 'indirect' => 1,
+ ])));
}
public function testItNoticesExceededSelfThreshold()
{
$configuration = Configuration::fromUrlEncodedString('max[self]=1');
- $this->assertTrue($configuration->tolerates([
- 'unsilencedCount' => 1234,
- 'remaining selfCount' => 1,
- 'legacyCount' => 23,
- 'otherCount' => 13,
- 'remaining directCount' => 124,
- 'remaining indirectCount' => 3244,
- ]));
- $this->assertFalse($configuration->tolerates([
- 'unsilencedCount' => 1234,
- 'remaining selfCount' => 2,
- 'legacyCount' => 23,
- 'otherCount' => 13,
- 'remaining directCount' => 124,
- 'remaining indirectCount' => 3244,
- ]));
+ $this->assertTrue($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1234,
+ 'self' => 1,
+ 'legacy' => 23,
+ 'other' => 13,
+ 'direct' => 124,
+ 'indirect' => 3244,
+ ])));
+ $this->assertFalse($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1234,
+ 'self' => 2,
+ 'legacy' => 23,
+ 'other' => 13,
+ 'direct' => 124,
+ 'indirect' => 3244,
+ ])));
}
public function testItNoticesExceededDirectThreshold()
{
$configuration = Configuration::fromUrlEncodedString('max[direct]=1&max[self]=999999');
- $this->assertTrue($configuration->tolerates([
- 'unsilencedCount' => 1234,
- 'remaining selfCount' => 123,
- 'legacyCount' => 23,
- 'otherCount' => 13,
- 'remaining directCount' => 1,
- 'remaining indirectCount' => 3244,
- ]));
- $this->assertFalse($configuration->tolerates([
- 'unsilencedCount' => 1234,
- 'remaining selfCount' => 124,
- 'legacyCount' => 23,
- 'otherCount' => 13,
- 'remaining directCount' => 2,
- 'remaining indirectCount' => 3244,
- ]));
+ $this->assertTrue($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1234,
+ 'self' => 123,
+ 'legacy' => 23,
+ 'other' => 13,
+ 'direct' => 1,
+ 'indirect' => 3244,
+ ])));
+ $this->assertFalse($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1234,
+ 'self' => 124,
+ 'legacy' => 23,
+ 'other' => 13,
+ 'direct' => 2,
+ 'indirect' => 3244,
+ ])));
}
public function testItNoticesExceededIndirectThreshold()
{
$configuration = Configuration::fromUrlEncodedString('max[indirect]=1&max[direct]=999999&max[self]=999999');
- $this->assertTrue($configuration->tolerates([
- 'unsilencedCount' => 1234,
- 'remaining selfCount' => 123,
- 'legacyCount' => 23,
- 'otherCount' => 13,
- 'remaining directCount' => 1234,
- 'remaining indirectCount' => 1,
- ]));
- $this->assertFalse($configuration->tolerates([
- 'unsilencedCount' => 1234,
- 'remaining selfCount' => 124,
- 'legacyCount' => 23,
- 'otherCount' => 13,
- 'remaining directCount' => 2324,
- 'remaining indirectCount' => 2,
- ]));
+ $this->assertTrue($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1234,
+ 'self' => 123,
+ 'legacy' => 23,
+ 'other' => 13,
+ 'direct' => 1234,
+ 'indirect' => 1,
+ ])));
+ $this->assertFalse($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 1234,
+ 'self' => 124,
+ 'legacy' => 23,
+ 'other' => 13,
+ 'direct' => 2324,
+ 'indirect' => 2,
+ ])));
}
public function testIndirectThresholdIsUsedAsADefaultForDirectAndSelfThreshold()
{
$configuration = Configuration::fromUrlEncodedString('max[indirect]=1');
- $this->assertTrue($configuration->tolerates([
- 'unsilencedCount' => 0,
- 'remaining selfCount' => 1,
- 'legacyCount' => 0,
- 'otherCount' => 0,
- 'remaining directCount' => 0,
- 'remaining indirectCount' => 0,
- ]));
- $this->assertFalse($configuration->tolerates([
- 'unsilencedCount' => 0,
- 'remaining selfCount' => 2,
- 'legacyCount' => 0,
- 'otherCount' => 0,
- 'remaining directCount' => 0,
- 'remaining indirectCount' => 0,
- ]));
- $this->assertTrue($configuration->tolerates([
- 'unsilencedCount' => 0,
- 'remaining selfCount' => 0,
- 'legacyCount' => 0,
- 'otherCount' => 0,
- 'remaining directCount' => 1,
- 'remaining indirectCount' => 0,
- ]));
- $this->assertFalse($configuration->tolerates([
- 'unsilencedCount' => 0,
- 'remaining selfCount' => 0,
- 'legacyCount' => 0,
- 'otherCount' => 0,
- 'remaining directCount' => 2,
- 'remaining indirectCount' => 0,
- ]));
+ $this->assertTrue($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 0,
+ 'self' => 1,
+ 'legacy' => 0,
+ 'other' => 0,
+ 'direct' => 0,
+ 'indirect' => 0,
+ ])));
+ $this->assertFalse($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 0,
+ 'self' => 2,
+ 'legacy' => 0,
+ 'other' => 0,
+ 'direct' => 0,
+ 'indirect' => 0,
+ ])));
+ $this->assertTrue($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 0,
+ 'self' => 0,
+ 'legacy' => 0,
+ 'other' => 0,
+ 'direct' => 1,
+ 'indirect' => 0,
+ ])));
+ $this->assertFalse($configuration->tolerates($this->buildGroups([
+ 'unsilenced' => 0,
+ 'self' => 0,
+ 'legacy' => 0,
+ 'other' => 0,
+ 'direct' => 2,
+ 'indirect' => 0,
+ ])));
}
public function testItCanTellWhetherToDisplayAStackTrace()
@@ -175,21 +179,237 @@ public function testItCanTellWhetherToDisplayAStackTrace()
$this->assertTrue($configuration->shouldDisplayStackTrace('interesting'));
}
- public function testItCanBeDisabled()
+ public function provideItCanBeDisabled(): array
+ {
+ return [
+ ['disabled', false],
+ ['disabled=1', false],
+ ['disabled=0', true],
+ ];
+ }
+
+ /**
+ * @dataProvider provideItCanBeDisabled
+ */
+ public function testItCanBeDisabled(string $encodedString, bool $expectedEnabled)
{
- $configuration = Configuration::fromUrlEncodedString('disabled');
- $this->assertFalse($configuration->isEnabled());
+ $configuration = Configuration::fromUrlEncodedString($encodedString);
+ $this->assertSame($expectedEnabled, $configuration->isEnabled());
}
public function testItCanBeShushed()
{
$configuration = Configuration::fromUrlEncodedString('verbose');
- $this->assertFalse($configuration->verboseOutput());
+ $this->assertFalse($configuration->verboseOutput('unsilenced'));
+ $this->assertFalse($configuration->verboseOutput('direct'));
+ $this->assertFalse($configuration->verboseOutput('indirect'));
+ $this->assertFalse($configuration->verboseOutput('self'));
+ $this->assertFalse($configuration->verboseOutput('other'));
+ }
+
+ public function testItCanBePartiallyShushed()
+ {
+ $configuration = Configuration::fromUrlEncodedString('quiet[]=unsilenced&quiet[]=indirect&quiet[]=other');
+ $this->assertFalse($configuration->verboseOutput('unsilenced'));
+ $this->assertTrue($configuration->verboseOutput('direct'));
+ $this->assertFalse($configuration->verboseOutput('indirect'));
+ $this->assertTrue($configuration->verboseOutput('self'));
+ $this->assertFalse($configuration->verboseOutput('other'));
+ }
+
+ public function testItThrowsOnUnknownVerbosityGroup()
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('made-up');
+ Configuration::fromUrlEncodedString('quiet[]=made-up');
}
public function testOutputIsNotVerboseInWeakMode()
{
$configuration = Configuration::inWeakMode();
- $this->assertFalse($configuration->verboseOutput());
+ $this->assertFalse($configuration->verboseOutput('unsilenced'));
+ $this->assertFalse($configuration->verboseOutput('direct'));
+ $this->assertFalse($configuration->verboseOutput('indirect'));
+ $this->assertFalse($configuration->verboseOutput('self'));
+ $this->assertFalse($configuration->verboseOutput('other'));
+ }
+
+ private function buildGroups($counts)
+ {
+ $groups = [];
+ foreach ($counts as $name => $count) {
+ $groups[$name] = new DeprecationGroup();
+ $i = 0;
+ while ($i++ < $count) {
+ $groups[$name]->addNotice();
+ }
+ }
+
+ return $groups;
+ }
+
+ public function testBaselineGenerationEmptyFile()
+ {
+ $filename = $this->createFile();
+ $configuration = Configuration::fromUrlEncodedString('generateBaseline=true&baselineFile='.urlencode($filename));
+ $this->assertTrue($configuration->isGeneratingBaseline());
+ $trace = debug_backtrace();
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 2', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, '')));
+ $configuration->writeBaseline();
+ $this->assertEquals($filename, $configuration->getBaselineFile());
+ $expected_baseline = [
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 1',
+ 'count' => 2,
+ ],
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 2',
+ 'count' => 1,
+ ],
+ ];
+ $this->assertEquals(json_encode($expected_baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename));
+ }
+
+ public function testBaselineGenerationNoFile()
+ {
+ $filename = $this->createFile();
+ $configuration = Configuration::fromUrlEncodedString('generateBaseline=true&baselineFile='.urlencode($filename));
+ $this->assertTrue($configuration->isGeneratingBaseline());
+ $trace = debug_backtrace();
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 2', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 2', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, '')));
+ $configuration->writeBaseline();
+ $this->assertEquals($filename, $configuration->getBaselineFile());
+ $expected_baseline = [
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 1',
+ 'count' => 2,
+ ],
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 2',
+ 'count' => 2,
+ ],
+ ];
+ $this->assertEquals(json_encode($expected_baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename));
+ }
+
+ public function testExistingBaseline()
+ {
+ $filename = $this->createFile();
+ $baseline = [
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 1',
+ 'count' => 1,
+ ],
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 2',
+ 'count' => 1,
+ ],
+ ];
+ file_put_contents($filename, json_encode($baseline));
+
+ $configuration = Configuration::fromUrlEncodedString('baselineFile='.urlencode($filename));
+ $this->assertFalse($configuration->isGeneratingBaseline());
+ $trace = debug_backtrace();
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 2', $trace, '')));
+ $this->assertFalse($configuration->isBaselineDeprecation(new Deprecation('Test message 3', $trace, '')));
+ $this->assertEquals($filename, $configuration->getBaselineFile());
+ }
+
+ public function testExistingBaselineAndGeneration()
+ {
+ $filename = $this->createFile();
+ $baseline = [
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 1',
+ 'count' => 1,
+ ],
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 2',
+ 'count' => 1,
+ ],
+ ];
+ file_put_contents($filename, json_encode($baseline));
+ $configuration = Configuration::fromUrlEncodedString('generateBaseline=true&baselineFile='.urlencode($filename));
+ $this->assertTrue($configuration->isGeneratingBaseline());
+ $trace = debug_backtrace();
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 2', $trace, '')));
+ $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 3', $trace, '')));
+ $configuration->writeBaseline();
+ $this->assertEquals($filename, $configuration->getBaselineFile());
+ $expected_baseline = [
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 2',
+ 'count' => 1,
+ ],
+ [
+ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest',
+ 'message' => 'Test message 3',
+ 'count' => 1,
+ ],
+ ];
+ $this->assertEquals(json_encode($expected_baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename));
+ }
+
+ public function testBaselineArgumentException()
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('You cannot use the "generateBaseline" configuration option without providing a "baselineFile" configuration option.');
+ Configuration::fromUrlEncodedString('generateBaseline=true');
+ }
+
+ public function testBaselineFileException()
+ {
+ $filename = $this->createFile();
+ unlink($filename);
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage(sprintf('The baselineFile "%s" does not exist.', $filename));
+ Configuration::fromUrlEncodedString('baselineFile='.urlencode($filename));
+ }
+
+ public function testBaselineFileWriteError()
+ {
+ $filename = $this->createFile();
+ chmod($filename, 0444);
+ $this->expectError();
+ $this->expectErrorMessageMatches('/[Ff]ailed to open stream: Permission denied/');
+ $configuration = Configuration::fromUrlEncodedString('generateBaseline=true&baselineFile='.urlencode($filename));
+ $configuration->writeBaseline();
+ }
+
+ protected function setUp(): void
+ {
+ $this->files = [];
+ }
+
+ protected function tearDown(): void
+ {
+ foreach ($this->files as $file) {
+ if (file_exists($file)) {
+ @unlink($file);
+ }
+ }
+ }
+
+ private function createFile()
+ {
+ $filename = tempnam(sys_get_temp_dir(), 'sf-');
+ $this->files[] = $filename;
+
+ return $filename;
}
}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationGroupTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationGroupTest.php
new file mode 100644
index 0000000000000..df746e5e38907
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationGroupTest.php
@@ -0,0 +1,30 @@
+addNoticeFromObject(
+ 'Calling sfContext::getInstance() is deprecated',
+ 'MonsterController',
+ 'get5klocMethod'
+ );
+ $group->addNoticeFromProceduralCode('Calling sfContext::getInstance() is deprecated');
+ $this->assertCount(1, $group->notices());
+ $this->assertSame(2, $group->count());
+ }
+
+ public function testItAllowsAddingANoticeWithoutClutteringTheMemory()
+ {
+ // this is useful for notices in the legacy group
+ $group = new DeprecationGroup();
+ $group->addNotice();
+ $this->assertSame(1, $group->count());
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationNoticeTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationNoticeTest.php
new file mode 100644
index 0000000000000..c0a88c443b4d7
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationNoticeTest.php
@@ -0,0 +1,35 @@
+addObjectOccurrence('MyAction', '__invoke');
+ $notice->addObjectOccurrence('MyAction', '__invoke');
+ $notice->addObjectOccurrence('MyOtherAction', '__invoke');
+
+ $countsByCaller = $notice->getCountsByCaller();
+
+ $this->assertCount(2, $countsByCaller);
+ $this->assertArrayHasKey('MyAction::__invoke', $countsByCaller);
+ $this->assertArrayHasKey('MyOtherAction::__invoke', $countsByCaller);
+ $this->assertSame(2, $countsByCaller['MyAction::__invoke']);
+ $this->assertSame(1, $countsByCaller['MyOtherAction::__invoke']);
+ }
+
+ public function testItCountsBothTypesOfOccurrences()
+ {
+ $notice = new DeprecationNotice();
+ $notice->addObjectOccurrence('MyAction', '__invoke');
+ $this->assertSame(1, $notice->count());
+
+ $notice->addProceduralOccurrence();
+ $this->assertSame(2, $notice->count());
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php
index 9cb0a0e32ce3a..a1d3c06ea668f 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php
@@ -14,7 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation;
-use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV5;
+use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7;
class DeprecationTest extends TestCase
{
@@ -30,7 +30,7 @@ private static function getVendorDir()
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
- $vendorDir = \dirname(\dirname($r->getFileName()));
+ $vendorDir = \dirname($r->getFileName(), 2);
if (file_exists($vendorDir.'/composer/installed.json') && @mkdir($vendorDir.'/myfakevendor/myfakepackage1', 0777, true)) {
break;
}
@@ -58,7 +58,7 @@ public function testItCanTellWhetherItIsInternal()
{
$r = new \ReflectionClass(Deprecation::class);
- if (\dirname(\dirname($r->getFileName())) !== \dirname(\dirname(__DIR__))) {
+ if (\dirname($r->getFileName(), 2) !== \dirname(__DIR__, 2)) {
$this->markTestSkipped('Test case is not compatible with having the bridge in vendor/');
}
@@ -161,7 +161,7 @@ public function providerGetTypeDetectsSelf()
'triggering_file' => 'dummy_vendor_path',
'files_stack' => [],
]),
- SymfonyTestsListenerForV5::class,
+ SymfonyTestsListenerForV7::class,
'',
],
];
@@ -188,7 +188,7 @@ public function providerGetTypeUsesRightTrace()
$fakeTrace = [
['function' => 'trigger_error'],
['class' => SymfonyTestsListenerTrait::class, 'function' => 'endTest'],
- ['class' => SymfonyTestsListenerForV5::class, 'function' => 'endTest'],
+ ['class' => SymfonyTestsListenerForV7::class, 'function' => 'endTest'],
];
return [
@@ -270,7 +270,7 @@ public static function setupBeforeClass(): void
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
- $v = \dirname(\dirname($r->getFileName()));
+ $v = \dirname($r->getFileName(), 2);
if (file_exists($v.'/composer/installed.json')) {
$loader = require $v.'/autoload.php';
$reflection = new \ReflectionClass($loader);
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline.phpt
new file mode 100644
index 0000000000000..533912c106cbd
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline.phpt
@@ -0,0 +1,80 @@
+--TEST--
+Test DeprecationErrorHandler in baseline mode
+--FILE--
+ 'FooTestCase::testLegacyFoo',
+ 'message' => 'silenced foo deprecation',
+ 'count' => 1,
+],
+[
+ 'location' => 'FooTestCase::testNonLegacyBar',
+ 'message' => 'silenced bar deprecation',
+ 'count' => 1,
+],
+[
+ 'location' => 'procedural code',
+ 'message' => 'root deprecation',
+ 'count' => 1,
+]];
+file_put_contents($filename, json_encode($baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
+
+$k = 'SYMFONY_DEPRECATIONS_HELPER';
+unset($_SERVER[$k], $_ENV[$k]);
+putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'baselineFile=' . urlencode($filename));
+putenv('ANSICON');
+putenv('ConEmuANSI');
+putenv('TERM');
+
+$vendor = __DIR__;
+while (!file_exists($vendor.'/vendor')) {
+ $vendor = dirname($vendor);
+}
+define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
+require PHPUNIT_COMPOSER_INSTALL;
+require_once __DIR__.'/../../bootstrap.php';
+
+@trigger_error('root deprecation', E_USER_DEPRECATED);
+
+eval(<<<'EOPHP'
+namespace PHPUnit\Util;
+
+class Test
+{
+ public static function getGroups()
+ {
+ return array();
+ }
+}
+EOPHP
+);
+
+class PHPUnit_Util_Test
+{
+ public static function getGroups()
+ {
+ return array();
+ }
+}
+
+class FooTestCase
+{
+ public function testLegacyFoo()
+ {
+ @trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
+ }
+
+ public function testNonLegacyBar()
+ {
+ @trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
+ }
+}
+
+$foo = new FooTestCase();
+$foo->testLegacyFoo();
+$foo->testNonLegacyBar();
+print "Cannot test baselineFile contents because it is generated in a shutdown function registered by another shutdown function."
+?>
+--EXPECT--
+Cannot test baselineFile contents because it is generated in a shutdown function registered by another shutdown function.
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline2.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline2.phpt
new file mode 100644
index 0000000000000..f520912694a1e
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline2.phpt
@@ -0,0 +1,74 @@
+--TEST--
+Test DeprecationErrorHandler in baseline mode
+--FILE--
+ 'FooTestCase::testLegacyFoo',
+ 'message' => 'silenced foo deprecation',
+ 'count' => 1,
+]];
+file_put_contents($filename, json_encode($baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
+
+$k = 'SYMFONY_DEPRECATIONS_HELPER';
+unset($_SERVER[$k], $_ENV[$k]);
+putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'baselineFile=' . urlencode($filename));
+putenv('ANSICON');
+putenv('ConEmuANSI');
+putenv('TERM');
+
+$vendor = __DIR__;
+while (!file_exists($vendor.'/vendor')) {
+ $vendor = dirname($vendor);
+}
+define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
+require PHPUNIT_COMPOSER_INSTALL;
+require_once __DIR__.'/../../bootstrap.php';
+
+@trigger_error('root deprecation', E_USER_DEPRECATED);
+
+eval(<<<'EOPHP'
+namespace PHPUnit\Util;
+
+class Test
+{
+ public static function getGroups()
+ {
+ return array();
+ }
+}
+EOPHP
+);
+
+class PHPUnit_Util_Test
+{
+ public static function getGroups()
+ {
+ return array();
+ }
+}
+
+class FooTestCase
+{
+ public function testLegacyFoo()
+ {
+ @trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
+ }
+
+ public function testNonLegacyBar()
+ {
+ @trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
+ }
+}
+
+$foo = new FooTestCase();
+$foo->testLegacyFoo();
+$foo->testNonLegacyBar();
+?>
+--EXPECTF--
+Other deprecation notices (2)
+
+ 1x: root deprecation
+
+ 1x: silenced bar deprecation
+ 1x in FooTestCase::testNonLegacyBar
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline3.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline3.phpt
new file mode 100644
index 0000000000000..28d1a74ffd427
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/baseline3.phpt
@@ -0,0 +1,79 @@
+--TEST--
+Test DeprecationErrorHandler in baseline mode
+--FILE--
+ 'FooTestCase::testLegacyFoo',
+ 'message' => 'silenced foo deprecation',
+ 'count' => 1,
+]];
+file_put_contents($filename, json_encode($baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
+
+$k = 'SYMFONY_DEPRECATIONS_HELPER';
+unset($_SERVER[$k], $_ENV[$k]);
+putenv($k.'='.$_SERVER[$k] = $_ENV[$k] = 'baselineFile=' . urlencode($filename));
+putenv('ANSICON');
+putenv('ConEmuANSI');
+putenv('TERM');
+
+$vendor = __DIR__;
+while (!file_exists($vendor.'/vendor')) {
+ $vendor = dirname($vendor);
+}
+define('PHPUNIT_COMPOSER_INSTALL', $vendor.'/vendor/autoload.php');
+require PHPUNIT_COMPOSER_INSTALL;
+require_once __DIR__.'/../../bootstrap.php';
+
+@trigger_error('root deprecation', E_USER_DEPRECATED);
+
+eval(<<<'EOPHP'
+namespace PHPUnit\Util;
+
+class Test
+{
+ public static function getGroups()
+ {
+ return array();
+ }
+}
+EOPHP
+);
+
+class PHPUnit_Util_Test
+{
+ public static function getGroups()
+ {
+ return array();
+ }
+}
+
+class FooTestCase
+{
+ public function testLegacyFoo()
+ {
+ @trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
+ // This will cause a deprecation because the baseline only expects 1
+ // deprecation.
+ @trigger_error('silenced foo deprecation', E_USER_DEPRECATED);
+ }
+
+ public function testNonLegacyBar()
+ {
+ @trigger_error('silenced bar deprecation', E_USER_DEPRECATED);
+ }
+}
+
+$foo = new FooTestCase();
+$foo->testLegacyFoo();
+$foo->testNonLegacyBar();
+?>
+--EXPECTF--
+Legacy deprecation notices (1)
+
+Other deprecation notices (2)
+
+ 1x: root deprecation
+
+ 1x: silenced bar deprecation
+ 1x in FooTestCase::testNonLegacyBar
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/disabled_1.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/disabled_1.phpt
new file mode 100644
index 0000000000000..acb5f096306d0
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/disabled_1.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Test DeprecationErrorHandler in default mode
+--FILE--
+
+--EXPECTREGEX--
+.{0}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php
index 2e5ccf3c78811..2b6cb316af143 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_app/AppService.php
@@ -7,6 +7,37 @@
final class AppService
{
+ public function directDeprecationsTwoVendors()
+ {
+ $service1 = new SomeService();
+ $service1->deprecatedApi();
+
+ $service2 = new SomeOtherService();
+ $service2->deprecatedApi();
+ }
+
+ public function selfDeprecation(bool $useContracts = false)
+ {
+ $args = [__FUNCTION__, __FUNCTION__];
+ if ($useContracts) {
+ trigger_deprecation('App', '3.0', sprintf('%s is deprecated, use %s_new instead.', ...$args));
+ } else {
+ @trigger_error(sprintf('Since App 3.0: %s is deprecated, use %s_new instead.', ...$args), \E_USER_DEPRECATED);
+ }
+ }
+
+ public function directDeprecation(bool $useContracts = false)
+ {
+ $service = new SomeService();
+ $service->deprecatedApi($useContracts);
+ }
+
+ public function indirectDeprecation(bool $useContracts = false)
+ {
+ $service = new SomeService();
+ $service->indirectDeprecatedApi($useContracts);
+ }
+
public function directDeprecations()
{
$service1 = new SomeService();
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php
index d18cfbd3319bd..cc237e6146c23 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php
@@ -2,13 +2,22 @@
namespace acme\lib;
+use bar\lib\AnotherService;
+
class SomeService
{
- public function deprecatedApi()
+ public function deprecatedApi(bool $useContracts = false)
+ {
+ $args = [__FUNCTION__, __FUNCTION__];
+ if ($useContracts) {
+ trigger_deprecation('acme/lib', '3.0', sprintf('%s is deprecated, use %s_new instead.', ...$args));
+ } else {
+ @trigger_error(sprintf('Since acme/lib 3.0: %s is deprecated, use %s_new instead.', ...$args), \E_USER_DEPRECATED);
+ }
+ }
+
+ public function indirectDeprecatedApi(bool $useContracts = false)
{
- @trigger_error(
- __FUNCTION__.' is deprecated! You should stop relying on it!',
- \E_USER_DEPRECATED
- );
+ (new AnotherService())->deprecatedApi($useContracts);
}
}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php
new file mode 100644
index 0000000000000..2e2f0f9b6b4b5
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/bar/lib/AnotherService.php
@@ -0,0 +1,16 @@
+ [__DIR__.'/../../fake_app/'],
'acme\\lib\\' => [__DIR__.'/../acme/lib/'],
+ 'bar\\lib\\' => [__DIR__.'/../bar/lib/'],
'fcy\\lib\\' => [__DIR__.'/../fcy/lib/'],
];
}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/generate_baseline.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/generate_baseline.phpt
new file mode 100644
index 0000000000000..112a02b4c41a0
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/generate_baseline.phpt
@@ -0,0 +1,64 @@
+--TEST--
+Test DeprecationErrorHandler in baseline generation mode
+--FILE--
+testLegacyFoo();
+$foo->testNonLegacyBar();
+print "Cannot test baselineFile contents because it is generated in a shutdown function registered by another shutdown function."
+?>
+--EXPECT--
+Cannot test baselineFile contents because it is generated in a shutdown function registered by another shutdown function.
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt
index 2f7686fd98819..b5253df4299e4 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt
@@ -35,5 +35,5 @@ require __DIR__.'/fake_vendor/acme/outdated-lib/outdated_file.php';
--EXPECTF--
Remaining indirect deprecation notices (1)
- 1x: deprecatedApi is deprecated! You should stop relying on it!
+ 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
1x in SomeService::deprecatedApi from acme\lib
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt
new file mode 100644
index 0000000000000..7f114ab5e2e5a
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/log_file.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Test DeprecationErrorHandler with log file
+--FILE--
+testLegacyFoo();
+$foo->testLegacyBar();
+
+register_shutdown_function(function () use ($filename) {
+ var_dump(file_get_contents($filename));
+});
+?>
+--EXPECTF--
+string(234) "
+Unsilenced deprecation notices (3)
+
+ 2x: unsilenced foo deprecation
+ 2x in FooTestCase::testLegacyFoo
+
+ 1x: unsilenced bar deprecation
+ 1x in FooTestCase::testLegacyBar
+
+Other deprecation notices (1)
+
+ 1x: root deprecation
+
+"
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/multiple_autoloads.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/multiple_autoloads.phpt
index 336001f500113..edf9f4f6f3731 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/multiple_autoloads.phpt
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/multiple_autoloads.phpt
@@ -38,7 +38,7 @@ require __DIR__.'/fake_vendor_bis/autoload.php';
--EXPECTF--
Remaining direct deprecation notices (2)
- 1x: deprecatedApi is deprecated! You should stop relying on it!
+ 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
1x in AppService::directDeprecations from App\Services
1x: deprecatedApi from foo is deprecated! You should stop relying on it!
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt
new file mode 100644
index 0000000000000..83b135b34bab7
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/partially_quiet.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Test DeprecationErrorHandler quiet on everything but indirect deprecations
+--FILE--
+
+--EXPECTF--
+Unsilenced deprecation notices (3)
+
+Remaining direct deprecation notices (2)
+
+Remaining indirect deprecation notices (1)
+
+ 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
+ 1x in SomeService::deprecatedApi from acme\lib
+
+Legacy deprecation notices (2)
+
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt
new file mode 100644
index 0000000000000..8d8f8b4ff490d
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet_but_failing.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Test DeprecationErrorHandler when failing and not verbose
+--FILE--
+
+--EXPECTF--
+Remaining indirect deprecation notices (1)
+
+ 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
+ 1x in SomeService::deprecatedApi from acme\lib
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt
new file mode 100644
index 0000000000000..261b6ec83f675
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_deprecation_types.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Test deprecation types with trigger_deprecation
+--FILE--
+selfDeprecation(true);
+(new \App\Services\AppService())->directDeprecation(true);
+(new \App\Services\AppService())->indirectDeprecation(true);
+trigger_deprecation('foo/bar', '2.0', 'func is deprecated, use new instead.');
+?>
+--EXPECTF--
+Remaining self deprecation notices (1)
+
+ 1x: Since App 3.0: selfDeprecation is deprecated, use selfDeprecation_new instead.
+ 1x in AppService::selfDeprecation from App\Services
+
+Remaining direct deprecation notices (1)
+
+ 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
+ 1x in AppService::directDeprecation from App\Services
+
+Remaining indirect deprecation notices (1)
+
+ 1x: Since bar/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
+ 1x in AppService::indirectDeprecation from App\Services
+
+Other deprecation notices (1)
+
+ 1x: Since foo/bar 2.0: func is deprecated, use new instead.
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_error_types.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_error_types.phpt
new file mode 100644
index 0000000000000..4dd6bdaed9f0b
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/trigger_error_types.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Test deprecation types with trigger_error
+--FILE--
+selfDeprecation();
+(new \App\Services\AppService())->directDeprecation();
+(new \App\Services\AppService())->indirectDeprecation();
+@trigger_error('Since foo/bar 2.0: func is deprecated, use new instead.', E_USER_DEPRECATED);
+?>
+--EXPECTF--
+Remaining self deprecation notices (1)
+
+ 1x: Since App 3.0: selfDeprecation is deprecated, use selfDeprecation_new instead.
+ 1x in AppService::selfDeprecation from App\Services
+
+Remaining direct deprecation notices (1)
+
+ 1x: Since acme/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
+ 1x in AppService::directDeprecation from App\Services
+
+Remaining indirect deprecation notices (1)
+
+ 1x: Since bar/lib 3.0: deprecatedApi is deprecated, use deprecatedApi_new instead.
+ 1x in AppService::indirectDeprecation from App\Services
+
+Other deprecation notices (1)
+
+ 1x: Since foo/bar 2.0: func is deprecated, use new instead.
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php b/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php
new file mode 100644
index 0000000000000..5e6350e673101
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
+
+final class ExpectDeprecationTraitTest extends TestCase
+{
+ use ExpectDeprecationTrait;
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ */
+ public function testOne()
+ {
+ $this->expectDeprecation('foo');
+ @trigger_error('foo', \E_USER_DEPRECATED);
+ }
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ * @runInSeparateProcess
+ */
+ public function testOneInIsolation()
+ {
+ $this->expectDeprecation('foo');
+ @trigger_error('foo', \E_USER_DEPRECATED);
+ }
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ */
+ public function testMany()
+ {
+ $this->expectDeprecation('foo');
+ $this->expectDeprecation('bar');
+ @trigger_error('foo', \E_USER_DEPRECATED);
+ @trigger_error('bar', \E_USER_DEPRECATED);
+ }
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ *
+ * @expectedDeprecation foo
+ */
+ public function testOneWithAnnotation()
+ {
+ $this->expectDeprecation('bar');
+ @trigger_error('foo', \E_USER_DEPRECATED);
+ @trigger_error('bar', \E_USER_DEPRECATED);
+ }
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ *
+ * @expectedDeprecation foo
+ * @expectedDeprecation bar
+ */
+ public function testManyWithAnnotation()
+ {
+ $this->expectDeprecation('ccc');
+ $this->expectDeprecation('fcy');
+ @trigger_error('foo', \E_USER_DEPRECATED);
+ @trigger_error('bar', \E_USER_DEPRECATED);
+ @trigger_error('ccc', \E_USER_DEPRECATED);
+ @trigger_error('fcy', \E_USER_DEPRECATED);
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php b/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php
new file mode 100644
index 0000000000000..ba35b268deebe
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Tests\FailTests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
+
+/**
+ * Class ExpectDeprecationTraitTestFail.
+ *
+ * This class is deliberately suffixed with *TestFail.php so that it is ignored
+ * by PHPUnit. This test is designed to fail. See ../expectdeprecationfail.phpt.
+ */
+final class ExpectDeprecationTraitTestFail extends TestCase
+{
+ use ExpectDeprecationTrait;
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ */
+ public function testOne()
+ {
+ $this->expectDeprecation('foo');
+ @trigger_error('bar', \E_USER_DEPRECATED);
+ }
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ * @runInSeparateProcess
+ */
+ public function testOneInIsolation()
+ {
+ $this->expectDeprecation('foo');
+ @trigger_error('bar', \E_USER_DEPRECATED);
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/FailTests/NoAssertionsTestRisky.php b/src/Symfony/Bridge/PhpUnit/Tests/FailTests/NoAssertionsTestRisky.php
new file mode 100644
index 0000000000000..4a22baf125a7f
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/FailTests/NoAssertionsTestRisky.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Tests\FailTests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
+
+/**
+ * This class is deliberately suffixed with *TestRisky.php so that it is ignored
+ * by PHPUnit. This test is designed to fail. See ../expectrisky.phpt.
+ */
+final class NoAssertionsTestRisky extends TestCase
+{
+ use ExpectDeprecationTrait;
+
+ /**
+ * Do not remove this test in the next major version.
+ *
+ * @group legacy
+ */
+ public function testOne()
+ {
+ $this->expectNotToPerformAssertions();
+ $this->expectDeprecation('foo');
+ @trigger_error('foo', \E_USER_DEPRECATED);
+ }
+
+ /**
+ * Do not remove this test in the next major version.
+ */
+ public function testTwo()
+ {
+ $this->expectNotToPerformAssertions();
+ }
+}
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php
index 3e45381dce2a0..e302fa05ea74c 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php
@@ -12,14 +12,4 @@
require __DIR__.'/../src/BarCov.php';
require __DIR__.'/../src/FooCov.php';
-require __DIR__.'/../../../../Legacy/CoverageListenerTrait.php';
-
-if (version_compare(\PHPUnit\Runner\Version::id(), '6.0.0', '<')) {
- require_once __DIR__.'/../../../../Legacy/CoverageListenerForV5.php';
-} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) {
- require_once __DIR__.'/../../../../Legacy/CoverageListenerForV6.php';
-} else {
- require_once __DIR__.'/../../../../Legacy/CoverageListenerForV7.php';
-}
-
require __DIR__.'/../../../../CoverageListener.php';
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/expectdeprecationfail.phpt b/src/Symfony/Bridge/PhpUnit/Tests/expectdeprecationfail.phpt
new file mode 100644
index 0000000000000..f968cd188a0a7
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/expectdeprecationfail.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Test ExpectDeprecationTrait failing tests
+--FILE--
+
+--EXPECTF--
+PHPUnit %s
+
+%ATesting Symfony\Bridge\PhpUnit\Tests\FailTests\ExpectDeprecationTraitTestFail
+FF 2 / 2 (100%)
+
+Time: %s, Memory: %s
+
+There were 2 failures:
+
+1) Symfony\Bridge\PhpUnit\Tests\FailTests\ExpectDeprecationTraitTestFail::testOne
+Failed asserting that string matches format description.
+--- Expected
++++ Actual
+@@ @@
+ @expectedDeprecation:
+-%A foo
++ bar
+
+2) Symfony\Bridge\PhpUnit\Tests\FailTests\ExpectDeprecationTraitTestFail::testOneInIsolation
+Failed asserting that string matches format description.
+--- Expected
++++ Actual
+@@ @@
+ @expectedDeprecation:
+-%A foo
++ bar
+
+FAILURES!
+Tests: 2, Assertions: 2, Failures: 2.
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/expectrisky.phpt b/src/Symfony/Bridge/PhpUnit/Tests/expectrisky.phpt
new file mode 100644
index 0000000000000..91e0830553950
--- /dev/null
+++ b/src/Symfony/Bridge/PhpUnit/Tests/expectrisky.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Test NoAssertionsTestRisky risky test
+--SKIPIF--
+
+--EXPECTF--
+PHPUnit %s
+
+%ATesting Symfony\Bridge\PhpUnit\Tests\FailTests\NoAssertionsTestRisky
+R. 2 / 2 (100%)
+
+Time: %s, Memory: %s
+
+There was 1 risky test:
+
+1) Symfony\Bridge\PhpUnit\Tests\FailTests\NoAssertionsTestRisky::testOne
+This test is annotated with "@doesNotPerformAssertions", but performed 1 assertions
+
+OK, but incomplete, skipped, or risky tests!
+Tests: 2, Assertions: 1, Risky: 1.
diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php
index 8690812b56b57..3cc158f6b8e72 100644
--- a/src/Symfony/Bridge/PhpUnit/TextUI/Command.php
+++ b/src/Symfony/Bridge/PhpUnit/TextUI/Command.php
@@ -11,10 +11,8 @@
namespace Symfony\Bridge\PhpUnit\TextUI;
-if (version_compare(\PHPUnit\Runner\Version::id(), '6.0.0', '<')) {
- class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV5', 'Symfony\Bridge\PhpUnit\TextUI\Command');
-} elseif (version_compare(\PHPUnit\Runner\Version::id(), '9.0.0', '<')) {
- class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV6', 'Symfony\Bridge\PhpUnit\TextUI\Command');
+if (version_compare(\PHPUnit\Runner\Version::id(), '9.0.0', '<')) {
+ class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV7', 'Symfony\Bridge\PhpUnit\TextUI\Command');
} else {
class_alias('Symfony\Bridge\PhpUnit\Legacy\CommandForV9', 'Symfony\Bridge\PhpUnit\TextUI\Command');
}
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
index e7c3da2b8d7a3..ea88872ae9b86 100644
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
@@ -15,8 +15,8 @@
error_reporting(-1);
global $argv, $argc;
-$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : [];
-$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0;
+$argv = $_SERVER['argv'] ?? [];
+$argc = $_SERVER['argc'] ?? 0;
$getEnvVar = function ($name, $default = false) use ($argv) {
if (false !== $value = getenv($name)) {
return $value;
@@ -99,18 +99,14 @@
} elseif (\PHP_VERSION_ID >= 70200) {
// PHPUnit 8 requires PHP 7.2+
$PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '8.5') ?: '8.5';
-} elseif (\PHP_VERSION_ID >= 70100) {
- // PHPUnit 7 requires PHP 7.1+
- $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '7.5') ?: '7.5';
-} elseif (\PHP_VERSION_ID >= 70000) {
- // PHPUnit 6 requires PHP 7.0+
- $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '6.5') ?: '6.5';
-} elseif (\PHP_VERSION_ID >= 50600) {
- // PHPUnit 4 does not support PHP 7
- $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '5.7') ?: '5.7';
} else {
- // PHPUnit 5.1 requires PHP 5.6+
- $PHPUNIT_VERSION = '4.8';
+ $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '7.5') ?: '7.5';
+}
+
+$MAX_PHPUNIT_VERSION = $getEnvVar('SYMFONY_MAX_PHPUNIT_VERSION', false);
+
+if ($MAX_PHPUNIT_VERSION && version_compare($MAX_PHPUNIT_VERSION, $PHPUNIT_VERSION, '<')) {
+ $PHPUNIT_VERSION = $MAX_PHPUNIT_VERSION;
}
$PHPUNIT_REMOVE_RETURN_TYPEHINT = filter_var($getEnvVar('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT', '0'), \FILTER_VALIDATE_BOOLEAN);
@@ -137,6 +133,7 @@
'COMPOSER' => 'composer.json',
'COMPOSER_VENDOR_DIR' => 'vendor',
'COMPOSER_BIN_DIR' => 'bin',
+ 'SYMFONY_SIMPLE_PHPUNIT_BIN_DIR' => __DIR__,
];
foreach ($defaultEnvs as $envName => $envValue) {
@@ -171,7 +168,8 @@
}
}
$SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy'.($PHPUNIT_VERSION < 6.0 ? ' symfony/yaml' : ''));
-$configurationHash = md5(implode(\PHP_EOL, [md5_file(__FILE__), $SYMFONY_PHPUNIT_REMOVE, (int) $PHPUNIT_REMOVE_RETURN_TYPEHINT]));
+$SYMFONY_PHPUNIT_REQUIRE = $getEnvVar('SYMFONY_PHPUNIT_REQUIRE', '');
+$configurationHash = md5(implode(\PHP_EOL, [md5_file(__FILE__), $SYMFONY_PHPUNIT_REMOVE, $SYMFONY_PHPUNIT_REQUIRE, (int) $PHPUNIT_REMOVE_RETURN_TYPEHINT]));
$PHPUNIT_VERSION_DIR = sprintf('phpunit-%s-%d', $PHPUNIT_VERSION, $PHPUNIT_REMOVE_RETURN_TYPEHINT);
if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationHash !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION_DIR.md5")) {
// Build a standalone phpunit without symfony/yaml nor prophecy by default
@@ -228,6 +226,9 @@
if ($SYMFONY_PHPUNIT_REMOVE) {
$passthruOrFail("$COMPOSER remove --no-update --no-interaction ".$SYMFONY_PHPUNIT_REMOVE);
}
+ if ($SYMFONY_PHPUNIT_REQUIRE) {
+ $passthruOrFail("$COMPOSER require --no-update --no-interaction ".$SYMFONY_PHPUNIT_REQUIRE);
+ }
if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) {
$passthruOrFail("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\"");
}
@@ -271,12 +272,12 @@
if ($PHPUNIT_REMOVE_RETURN_TYPEHINT) {
$alteredCode = preg_replace('/^ ((?:protected|public)(?: static)? function \w+\(\)): void/m', ' $1', $alteredCode);
}
- $alteredCode = preg_replace('/abstract class (?:TestCase|PHPUnit_Framework_TestCase)[^\{]+\{/', '$0 '.\PHP_EOL." use \Symfony\Bridge\PhpUnit\Legacy\PolyfillTestCaseTrait;", $alteredCode, 1);
+ $alteredCode = preg_replace('/abstract class TestCase[^\{]+\{/', '$0 '.\PHP_EOL." use \Symfony\Bridge\PhpUnit\Legacy\PolyfillTestCaseTrait;", $alteredCode, 1);
file_put_contents($alteredFile, $alteredCode);
// Mutate Assert code
$alteredCode = file_get_contents($alteredFile = './src/Framework/Assert.php');
- $alteredCode = preg_replace('/abstract class (?:Assert|PHPUnit_Framework_Assert)[^\{]+\{/', '$0 '.\PHP_EOL." use \Symfony\Bridge\PhpUnit\Legacy\PolyfillAssertTrait;", $alteredCode, 1);
+ $alteredCode = preg_replace('/abstract class Assert[^\{]+\{/', '$0 '.\PHP_EOL." use \Symfony\Bridge\PhpUnit\Legacy\PolyfillAssertTrait;", $alteredCode, 1);
file_put_contents($alteredFile, $alteredCode);
file_put_contents('phpunit', <<<'EOPHP'
@@ -373,7 +374,7 @@ class_exists(\SymfonyExcludeListSimplePhpunit::class, false) && PHPUnit\Util\Bla
}
if ($components) {
- $skippedTests = isset($_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS']) ? $_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS'] : false;
+ $skippedTests = $_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS'] ?? false;
$runningProcs = [];
foreach ($components as $component) {
diff --git a/src/Symfony/Bridge/PhpUnit/bootstrap.php b/src/Symfony/Bridge/PhpUnit/bootstrap.php
index d9947a7f4e1c8..e07c8d6cf5de8 100644
--- a/src/Symfony/Bridge/PhpUnit/bootstrap.php
+++ b/src/Symfony/Bridge/PhpUnit/bootstrap.php
@@ -12,96 +12,6 @@
use Doctrine\Common\Annotations\AnnotationRegistry;
use Symfony\Bridge\PhpUnit\DeprecationErrorHandler;
-if (class_exists(\PHPUnit_Runner_Version::class) && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
- $classes = [
- 'PHPUnit_Framework_Assert', // override PhpUnit's ForwardCompat child class
- 'PHPUnit_Framework_AssertionFailedError', // override PhpUnit's ForwardCompat child class
- 'PHPUnit_Framework_BaseTestListener', // override PhpUnit's ForwardCompat child class
-
- 'PHPUnit_Framework_Constraint',
- 'PHPUnit_Framework_Constraint_ArrayHasKey',
- 'PHPUnit_Framework_Constraint_ArraySubset',
- 'PHPUnit_Framework_Constraint_Attribute',
- 'PHPUnit_Framework_Constraint_Callback',
- 'PHPUnit_Framework_Constraint_ClassHasAttribute',
- 'PHPUnit_Framework_Constraint_ClassHasStaticAttribute',
- 'PHPUnit_Framework_Constraint_Composite',
- 'PHPUnit_Framework_Constraint_Count',
- 'PHPUnit_Framework_Constraint_Exception',
- 'PHPUnit_Framework_Constraint_ExceptionCode',
- 'PHPUnit_Framework_Constraint_ExceptionMessage',
- 'PHPUnit_Framework_Constraint_ExceptionMessageRegExp',
- 'PHPUnit_Framework_Constraint_FileExists',
- 'PHPUnit_Framework_Constraint_GreaterThan',
- 'PHPUnit_Framework_Constraint_IsAnything',
- 'PHPUnit_Framework_Constraint_IsEmpty',
- 'PHPUnit_Framework_Constraint_IsEqual',
- 'PHPUnit_Framework_Constraint_IsFalse',
- 'PHPUnit_Framework_Constraint_IsIdentical',
- 'PHPUnit_Framework_Constraint_IsInstanceOf',
- 'PHPUnit_Framework_Constraint_IsJson',
- 'PHPUnit_Framework_Constraint_IsNull',
- 'PHPUnit_Framework_Constraint_IsTrue',
- 'PHPUnit_Framework_Constraint_IsType',
- 'PHPUnit_Framework_Constraint_JsonMatches',
- 'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider',
- 'PHPUnit_Framework_Constraint_LessThan',
- 'PHPUnit_Framework_Constraint_ObjectHasAttribute',
- 'PHPUnit_Framework_Constraint_PCREMatch',
- 'PHPUnit_Framework_Constraint_SameSize',
- 'PHPUnit_Framework_Constraint_StringContains',
- 'PHPUnit_Framework_Constraint_StringEndsWith',
- 'PHPUnit_Framework_Constraint_StringMatches',
- 'PHPUnit_Framework_Constraint_StringStartsWith',
- 'PHPUnit_Framework_Constraint_TraversableContains',
- 'PHPUnit_Framework_Constraint_TraversableContainsOnly',
-
- 'PHPUnit_Framework_Error_Deprecated',
- 'PHPUnit_Framework_Error_Notice',
- 'PHPUnit_Framework_Error_Warning',
- 'PHPUnit_Framework_Exception',
- 'PHPUnit_Framework_ExpectationFailedException',
-
- 'PHPUnit_Framework_MockObject_MockObject',
-
- 'PHPUnit_Framework_IncompleteTest',
- 'PHPUnit_Framework_IncompleteTestCase',
- 'PHPUnit_Framework_IncompleteTestError',
- 'PHPUnit_Framework_RiskyTest',
- 'PHPUnit_Framework_RiskyTestError',
- 'PHPUnit_Framework_SkippedTest',
- 'PHPUnit_Framework_SkippedTestCase',
- 'PHPUnit_Framework_SkippedTestError',
- 'PHPUnit_Framework_SkippedTestSuiteError',
-
- 'PHPUnit_Framework_SyntheticError',
-
- 'PHPUnit_Framework_Test',
- 'PHPUnit_Framework_TestCase', // override PhpUnit's ForwardCompat child class
- 'PHPUnit_Framework_TestFailure',
- 'PHPUnit_Framework_TestListener',
- 'PHPUnit_Framework_TestResult',
- 'PHPUnit_Framework_TestSuite', // override PhpUnit's ForwardCompat child class
-
- 'PHPUnit_Runner_BaseTestRunner',
- 'PHPUnit_Runner_Version',
-
- 'PHPUnit_Util_Blacklist',
- 'PHPUnit_Util_ErrorHandler',
- 'PHPUnit_Util_Test',
- 'PHPUnit_Util_XML',
- ];
- foreach ($classes as $class) {
- class_alias($class, '\\'.strtr($class, '_', '\\'));
- }
-
- class_alias('PHPUnit_Framework_Constraint_And', 'PHPUnit\Framework\Constraint\LogicalAnd');
- class_alias('PHPUnit_Framework_Constraint_Not', 'PHPUnit\Framework\Constraint\LogicalNot');
- class_alias('PHPUnit_Framework_Constraint_Or', 'PHPUnit\Framework\Constraint\LogicalOr');
- class_alias('PHPUnit_Framework_Constraint_Xor', 'PHPUnit\Framework\Constraint\LogicalXor');
- class_alias('PHPUnit_Framework_Error', 'PHPUnit\Framework\Error\Error');
-}
-
// Detect if we need to serialize deprecations to a file.
if ($file = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) {
DeprecationErrorHandler::collectDeprecations($file);
@@ -110,7 +20,7 @@ class_alias('PHPUnit_Framework_Error', 'PHPUnit\Framework\Error\Error');
}
// Detect if we're loaded by an actual run of phpunit
-if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists(\PHPUnit_TextUI_Command::class, false) && !class_exists(\PHPUnit\TextUI\Command::class, false)) {
+if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists(\PHPUnit\TextUI\Command::class, false)) {
return;
}
diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json
index 54cb26cf70164..b0ccda04315f1 100644
--- a/src/Symfony/Bridge/PhpUnit/composer.json
+++ b/src/Symfony/Bridge/PhpUnit/composer.json
@@ -16,18 +16,19 @@
}
],
"require": {
- "php": ">=5.5.9 EVEN ON LATEST SYMFONY VERSIONS TO ALLOW USING",
+ "php": ">=7.1.3 EVEN ON LATEST SYMFONY VERSIONS TO ALLOW USING",
"php": "THIS BRIDGE WHEN TESTING LOWEST SYMFONY VERSIONS.",
- "php": ">=5.5.9"
+ "php": ">=7.1.3"
},
"require-dev": {
- "symfony/error-handler": "^4.4|^5.0"
+ "symfony/deprecation-contracts": "^2.1|^3.0",
+ "symfony/error-handler": "^5.4|^6.0"
},
"suggest": {
"symfony/error-handler": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
},
"conflict": {
- "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0|9.1.2"
+ "phpunit/phpunit": "<7.5|9.1.2"
},
"autoload": {
"files": [ "bootstrap.php" ],
diff --git a/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist b/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist
index d37d2eac3650a..cde576e2c7536 100644
--- a/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist
+++ b/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist
@@ -1,7 +1,7 @@
- *
- * @final since Symfony 4.4
*/
-class DumpExtension extends AbstractExtension
+final class DumpExtension extends AbstractExtension
{
private $cloner;
private $dumper;
@@ -39,9 +36,9 @@ public function __construct(ClonerInterface $cloner, HtmlDumper $dumper = null)
}
/**
- * @return TwigFunction[]
+ * {@inheritdoc}
*/
- public function getFunctions()
+ public function getFunctions(): array
{
return [
new TwigFunction('dump', [$this, 'dump'], ['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true]),
@@ -49,19 +46,14 @@ public function getFunctions()
}
/**
- * @return TokenParserInterface[]
+ * {@inheritdoc}
*/
- public function getTokenParsers()
+ public function getTokenParsers(): array
{
return [new DumpTokenParser()];
}
- public function getName()
- {
- return 'dump';
- }
-
- public function dump(Environment $env, $context)
+ public function dump(Environment $env, array $context): ?string
{
if (!$env->isDebug()) {
return null;
diff --git a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php
index af7be97c4f9bd..8d2a35c99f682 100644
--- a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php
+++ b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php
@@ -19,35 +19,21 @@
* ExpressionExtension gives a way to create Expressions from a template.
*
* @author Fabien Potencier
{{ email.subject }}
Notification e-mail sent by Symfony {{ footer_text }}'.self::fixCodeMarkup($content[$i - 1]).'
'.self::fixCodeMarkup($content[$i - 1]).'
'.implode("\n", $lines).'
';
@@ -168,14 +145,8 @@ public function fileExcerpt($file, $line, $srcContext = 3)
/**
* Formats a file path.
- *
- * @param string $file An absolute file path
- * @param int $line The line number
- * @param string $text Use this text for the link rather than the file path
- *
- * @return string
*/
- public function formatFile($file, $line, $text = null)
+ public function formatFile(string $file, int $line, string $text = null): string
{
$file = trim($file);
@@ -198,15 +169,7 @@ public function formatFile($file, $line, $text = null)
return $text;
}
- /**
- * Returns the link for a given file/line pair.
- *
- * @param string $file An absolute file path
- * @param int $line The line number
- *
- * @return string|false A link or false
- */
- public function getFileLink($file, $line)
+ public function getFileLink(string $file, int $line): string|false
{
if ($fmt = $this->fileLinkFormat) {
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line);
@@ -226,7 +189,7 @@ public function getFileRelative(string $file): ?string
return null;
}
- public function formatFileFromText($text)
+ public function formatFileFromText(string $text): string
{
return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) {
return 'in '.$this->formatFile($match[2], $match[3]);
@@ -254,15 +217,7 @@ public function formatLogMessage(string $message, array $context): string
return htmlspecialchars($message, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'code';
- }
-
- protected static function fixCodeMarkup($line)
+ protected static function fixCodeMarkup(string $line): string
{
// ending tag from previous line
$opening = strpos($line, '
* @author Titouan Galopin