diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8be658..3c74702 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,13 @@
CHANGELOG
=========
+7.2
+---
+
+ * Add a PHPUnit extension that registers the clock mock and DNS mock and the `DebugClassLoader` from the ErrorHandler component if present
+ * Add `ExpectUserDeprecationMessageTrait` with a polyfill of PHPUnit's `expectUserDeprecationMessage()`
+ * Use `total` for asserting deprecation count when a group is not defined
+
6.4
---
diff --git a/ClockMock.php b/ClockMock.php
index 95cfc6a..4cca8fc 100644
--- a/ClockMock.php
+++ b/ClockMock.php
@@ -72,7 +72,7 @@ public static function microtime($asFloat = false)
return self::$now;
}
- return sprintf('%0.6f00 %d', self::$now - (int) self::$now, (int) self::$now);
+ return \sprintf('%0.6f00 %d', self::$now - (int) self::$now, (int) self::$now);
}
public static function date($format, $timestamp = null): string
@@ -101,7 +101,7 @@ public static function hrtime($asNumber = false)
$ns = (self::$now - (int) self::$now) * 1000000000;
if ($asNumber) {
- $number = sprintf('%d%d', (int) self::$now, $ns);
+ $number = \sprintf('%d%d', (int) self::$now, $ns);
return \PHP_INT_SIZE === 8 ? (int) $number : (float) $number;
}
diff --git a/DeprecationErrorHandler.php b/DeprecationErrorHandler.php
index 6682f42..e597908 100644
--- a/DeprecationErrorHandler.php
+++ b/DeprecationErrorHandler.php
@@ -301,7 +301,7 @@ private function displayDeprecations(array $groups, Configuration $configuration
if ($configuration->shouldWriteToLogFile()) {
if (false === $handle = @fopen($file = $configuration->getLogFile(), 'a')) {
- throw new \InvalidArgumentException(sprintf('The configured log file "%s" is not writeable.', $file));
+ throw new \InvalidArgumentException(\sprintf('The configured log file "%s" is not writeable.', $file));
}
} else {
$handle = fopen('php://output', 'w');
@@ -309,7 +309,7 @@ private function displayDeprecations(array $groups, Configuration $configuration
foreach ($groups as $group) {
if ($this->deprecationGroups[$group]->count()) {
- $deprecationGroupMessage = sprintf(
+ $deprecationGroupMessage = \sprintf(
'%s deprecation notices (%d)',
\in_array($group, ['direct', 'indirect', 'self'], true) ? "Remaining $group" : ucfirst($group),
$this->deprecationGroups[$group]->count()
@@ -328,7 +328,7 @@ private function displayDeprecations(array $groups, Configuration $configuration
uasort($notices, $cmp);
foreach ($notices as $msg => $notice) {
- fwrite($handle, sprintf("\n %sx: %s\n", $notice->count(), $msg));
+ fwrite($handle, \sprintf("\n %sx: %s\n", $notice->count(), $msg));
$countsByCaller = $notice->getCountsByCaller();
arsort($countsByCaller);
@@ -340,7 +340,7 @@ private function displayDeprecations(array $groups, Configuration $configuration
fwrite($handle, " ...\n");
break;
}
- fwrite($handle, sprintf(" %dx in %s\n", $count, preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method)));
+ fwrite($handle, \sprintf(" %dx in %s\n", $count, preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method)));
}
}
}
@@ -411,6 +411,11 @@ private static function hasColorSupport(): bool
return false;
}
+ // Follow https://force-color.org/
+ if ('' !== (($_SERVER['FORCE_COLOR'] ?? getenv('FORCE_COLOR'))[0] ?? '')) {
+ return true;
+ }
+
// Detect msysgit/mingw and assume this is a tty because detection
// does not work correctly, see https://github.com/composer/composer/issues/9690
if (!@stream_isatty(\STDOUT) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) {
diff --git a/DeprecationErrorHandler/Configuration.php b/DeprecationErrorHandler/Configuration.php
index 000deca..c984b73 100644
--- a/DeprecationErrorHandler/Configuration.php
+++ b/DeprecationErrorHandler/Configuration.php
@@ -76,10 +76,10 @@ private function __construct(array $thresholds = [], string $regex = '', array $
foreach ($thresholds as $group => $threshold) {
if (!\in_array($group, $groups, true)) {
- throw new \InvalidArgumentException(sprintf('Unrecognized threshold "%s", expected one of "%s".', $group, implode('", "', $groups)));
+ throw new \InvalidArgumentException(\sprintf('Unrecognized threshold "%s", expected one of "%s".', $group, implode('", "', $groups)));
}
if (!is_numeric($threshold)) {
- throw new \InvalidArgumentException(sprintf('Threshold for group "%s" has invalid value "%s".', $group, $threshold));
+ throw new \InvalidArgumentException(\sprintf('Threshold for group "%s" has invalid value "%s".', $group, $threshold));
}
$this->thresholds[$group] = (int) $threshold;
}
@@ -96,7 +96,7 @@ private function __construct(array $thresholds = [], string $regex = '', array $
}
foreach ($groups as $group) {
if (!isset($this->thresholds[$group])) {
- $this->thresholds[$group] = 999999;
+ $this->thresholds[$group] = $this->thresholds['total'] ?? 999999;
}
}
$this->regex = $regex;
@@ -111,17 +111,17 @@ private function __construct(array $thresholds = [], string $regex = '', array $
foreach ($verboseOutput as $group => $status) {
if (!isset($this->verboseOutput[$group])) {
- throw new \InvalidArgumentException(sprintf('Unsupported verbosity group "%s", expected one of "%s".', $group, implode('", "', array_keys($this->verboseOutput))));
+ throw new \InvalidArgumentException(\sprintf('Unsupported verbosity group "%s", expected one of "%s".', $group, implode('", "', array_keys($this->verboseOutput))));
}
$this->verboseOutput[$group] = $status;
}
if ($ignoreFile) {
if (!is_file($ignoreFile)) {
- throw new \InvalidArgumentException(sprintf('The ignoreFile "%s" does not exist.', $ignoreFile));
+ throw new \InvalidArgumentException(\sprintf('The ignoreFile "%s" does not exist.', $ignoreFile));
}
set_error_handler(static function ($t, $m) use ($ignoreFile, &$line) {
- throw new \RuntimeException(sprintf('Invalid pattern found in "%s" on line "%d"', $ignoreFile, 1 + $line).substr($m, 12));
+ throw new \RuntimeException(\sprintf('Invalid pattern found in "%s" on line "%d"', $ignoreFile, 1 + $line).substr($m, 12));
});
try {
foreach (file($ignoreFile) as $line => $pattern) {
@@ -147,7 +147,7 @@ private function __construct(array $thresholds = [], string $regex = '', array $
$this->baselineDeprecations[$baseline_deprecation->location][$baseline_deprecation->message] = $baseline_deprecation->count;
}
} else {
- throw new \InvalidArgumentException(sprintf('The baselineFile "%s" does not exist.', $this->baselineFile));
+ throw new \InvalidArgumentException(\sprintf('The baselineFile "%s" does not exist.', $this->baselineFile));
}
}
@@ -312,7 +312,7 @@ public static function fromUrlEncodedString(string $serializedConfiguration): se
parse_str($serializedConfiguration, $normalizedConfiguration);
foreach (array_keys($normalizedConfiguration) as $key) {
if (!\in_array($key, ['max', 'disabled', 'verbose', 'quiet', 'ignoreFile', 'generateBaseline', 'baselineFile', 'logFile'], true)) {
- throw new \InvalidArgumentException(sprintf('Unknown configuration option "%s".', $key));
+ throw new \InvalidArgumentException(\sprintf('Unknown configuration option "%s".', $key));
}
}
diff --git a/DeprecationErrorHandler/Deprecation.php b/DeprecationErrorHandler/Deprecation.php
index 2d65648..822e980 100644
--- a/DeprecationErrorHandler/Deprecation.php
+++ b/DeprecationErrorHandler/Deprecation.php
@@ -149,8 +149,6 @@ public function __construct(string $message, array $trace, string $file, bool $l
if (($test instanceof TestCase || $test instanceof TestSuite) && ('trigger_error' !== $trace[$i - 2]['function'] || isset($trace[$i - 2]['class']))) {
$this->originClass = \get_class($test);
$this->originMethod = $test->getName();
-
- return;
}
}
@@ -313,7 +311,7 @@ private function getPackage(string $path): string
}
}
- throw new \RuntimeException(sprintf('No vendors found for path "%s".', $path));
+ throw new \RuntimeException(\sprintf('No vendors found for path "%s".', $path));
}
/**
diff --git a/ExpectUserDeprecationMessageTrait.php b/ExpectUserDeprecationMessageTrait.php
new file mode 100644
index 0000000..ed94c84
--- /dev/null
+++ b/ExpectUserDeprecationMessageTrait.php
@@ -0,0 +1,30 @@
+
+ *
+ * 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\Runner\Version;
+
+if (version_compare(Version::id(), '11.0.0', '<')) {
+ trait ExpectUserDeprecationMessageTrait
+ {
+ use ExpectDeprecationTrait;
+
+ final protected function expectUserDeprecationMessage(string $expectedUserDeprecationMessage): void
+ {
+ $this->expectDeprecation(str_replace('%', '%%', $expectedUserDeprecationMessage));
+ }
+ }
+} else {
+ trait ExpectUserDeprecationMessageTrait
+ {
+ }
+}
diff --git a/Extension/DisableClockMockSubscriber.php b/Extension/DisableClockMockSubscriber.php
new file mode 100644
index 0000000..885e6ea
--- /dev/null
+++ b/Extension/DisableClockMockSubscriber.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Extension;
+
+use PHPUnit\Event\Code\TestMethod;
+use PHPUnit\Event\Test\Finished;
+use PHPUnit\Event\Test\FinishedSubscriber;
+use PHPUnit\Metadata\Group;
+use Symfony\Bridge\PhpUnit\ClockMock;
+
+/**
+ * @internal
+ */
+class DisableClockMockSubscriber implements FinishedSubscriber
+{
+ public function notify(Finished $event): void
+ {
+ $test = $event->test();
+
+ if (!$test instanceof TestMethod) {
+ return;
+ }
+
+ foreach ($test->metadata() as $metadata) {
+ if ($metadata instanceof Group && 'time-sensitive' === $metadata->groupName()) {
+ ClockMock::withClockMock(false);
+ }
+ }
+ }
+}
diff --git a/Extension/DisableDnsMockSubscriber.php b/Extension/DisableDnsMockSubscriber.php
new file mode 100644
index 0000000..fc3e754
--- /dev/null
+++ b/Extension/DisableDnsMockSubscriber.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Extension;
+
+use PHPUnit\Event\Code\TestMethod;
+use PHPUnit\Event\Test\Finished;
+use PHPUnit\Event\Test\FinishedSubscriber;
+use PHPUnit\Metadata\Group;
+use Symfony\Bridge\PhpUnit\DnsMock;
+
+/**
+ * @internal
+ */
+class DisableDnsMockSubscriber implements FinishedSubscriber
+{
+ public function notify(Finished $event): void
+ {
+ $test = $event->test();
+
+ if (!$test instanceof TestMethod) {
+ return;
+ }
+
+ foreach ($test->metadata() as $metadata) {
+ if ($metadata instanceof Group && 'dns-sensitive' === $metadata->groupName()) {
+ DnsMock::withMockedHosts([]);
+ }
+ }
+ }
+}
diff --git a/Extension/EnableClockMockSubscriber.php b/Extension/EnableClockMockSubscriber.php
new file mode 100644
index 0000000..c10c5dc
--- /dev/null
+++ b/Extension/EnableClockMockSubscriber.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Extension;
+
+use PHPUnit\Event\Code\TestMethod;
+use PHPUnit\Event\Test\PreparationStarted;
+use PHPUnit\Event\Test\PreparationStartedSubscriber;
+use PHPUnit\Metadata\Group;
+use Symfony\Bridge\PhpUnit\ClockMock;
+
+/**
+ * @internal
+ */
+class EnableClockMockSubscriber implements PreparationStartedSubscriber
+{
+ public function notify(PreparationStarted $event): void
+ {
+ $test = $event->test();
+
+ if (!$test instanceof TestMethod) {
+ return;
+ }
+
+ foreach ($test->metadata() as $metadata) {
+ if ($metadata instanceof Group && 'time-sensitive' === $metadata->groupName()) {
+ ClockMock::withClockMock(true);
+ }
+ }
+ }
+}
diff --git a/Extension/RegisterClockMockSubscriber.php b/Extension/RegisterClockMockSubscriber.php
new file mode 100644
index 0000000..e2955fe
--- /dev/null
+++ b/Extension/RegisterClockMockSubscriber.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Extension;
+
+use PHPUnit\Event\Code\TestMethod;
+use PHPUnit\Event\TestSuite\Loaded;
+use PHPUnit\Event\TestSuite\LoadedSubscriber;
+use PHPUnit\Metadata\Group;
+use Symfony\Bridge\PhpUnit\ClockMock;
+
+/**
+ * @internal
+ */
+class RegisterClockMockSubscriber implements LoadedSubscriber
+{
+ public function notify(Loaded $event): void
+ {
+ foreach ($event->testSuite()->tests() as $test) {
+ if (!$test instanceof TestMethod) {
+ continue;
+ }
+
+ foreach ($test->metadata() as $metadata) {
+ if ($metadata instanceof Group && 'time-sensitive' === $metadata->groupName()) {
+ ClockMock::register($test->className());
+ }
+ }
+ }
+ }
+}
diff --git a/Extension/RegisterDnsMockSubscriber.php b/Extension/RegisterDnsMockSubscriber.php
new file mode 100644
index 0000000..81382d5
--- /dev/null
+++ b/Extension/RegisterDnsMockSubscriber.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bridge\PhpUnit\Extension;
+
+use PHPUnit\Event\Code\TestMethod;
+use PHPUnit\Event\TestSuite\Loaded;
+use PHPUnit\Event\TestSuite\LoadedSubscriber;
+use PHPUnit\Metadata\Group;
+use Symfony\Bridge\PhpUnit\DnsMock;
+
+/**
+ * @internal
+ */
+class RegisterDnsMockSubscriber implements LoadedSubscriber
+{
+ public function notify(Loaded $event): void
+ {
+ foreach ($event->testSuite()->tests() as $test) {
+ if (!$test instanceof TestMethod) {
+ continue;
+ }
+
+ foreach ($test->metadata() as $metadata) {
+ if ($metadata instanceof Group && 'dns-sensitive' === $metadata->groupName()) {
+ DnsMock::register($test->className());
+ }
+ }
+ }
+ }
+}
diff --git a/SymfonyExtension.php b/SymfonyExtension.php
new file mode 100644
index 0000000..1df4f20
--- /dev/null
+++ b/SymfonyExtension.php
@@ -0,0 +1,52 @@
+
+ *
+ * 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\Runner\Extension\Extension;
+use PHPUnit\Runner\Extension\Facade;
+use PHPUnit\Runner\Extension\ParameterCollection;
+use PHPUnit\TextUI\Configuration\Configuration;
+use Symfony\Bridge\PhpUnit\Extension\DisableClockMockSubscriber;
+use Symfony\Bridge\PhpUnit\Extension\DisableDnsMockSubscriber;
+use Symfony\Bridge\PhpUnit\Extension\EnableClockMockSubscriber;
+use Symfony\Bridge\PhpUnit\Extension\RegisterClockMockSubscriber;
+use Symfony\Bridge\PhpUnit\Extension\RegisterDnsMockSubscriber;
+use Symfony\Component\ErrorHandler\DebugClassLoader;
+
+class SymfonyExtension implements Extension
+{
+ public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
+ {
+ if (class_exists(DebugClassLoader::class)) {
+ DebugClassLoader::enable();
+ }
+
+ if ($parameters->has('clock-mock-namespaces')) {
+ foreach (explode(',', $parameters->get('clock-mock-namespaces')) as $namespace) {
+ ClockMock::register($namespace.'\DummyClass');
+ }
+ }
+
+ $facade->registerSubscriber(new RegisterClockMockSubscriber());
+ $facade->registerSubscriber(new EnableClockMockSubscriber());
+ $facade->registerSubscriber(new DisableClockMockSubscriber());
+
+ if ($parameters->has('dns-mock-namespaces')) {
+ foreach (explode(',', $parameters->get('dns-mock-namespaces')) as $namespace) {
+ DnsMock::register($namespace.'\DummyClass');
+ }
+ }
+
+ $facade->registerSubscriber(new RegisterDnsMockSubscriber());
+ $facade->registerSubscriber(new DisableDnsMockSubscriber());
+ }
+}
diff --git a/Tests/CoverageListenerTest.php b/Tests/CoverageListenerTest.php
index 22f3565..99d4a4b 100644
--- a/Tests/CoverageListenerTest.php
+++ b/Tests/CoverageListenerTest.php
@@ -13,6 +13,9 @@
use PHPUnit\Framework\TestCase;
+/**
+ * @requires PHPUnit < 10
+ */
class CoverageListenerTest extends TestCase
{
public function test()
diff --git a/Tests/DeprecationErrorHandler/ConfigurationTest.php b/Tests/DeprecationErrorHandler/ConfigurationTest.php
index a2259fc..7eec029 100644
--- a/Tests/DeprecationErrorHandler/ConfigurationTest.php
+++ b/Tests/DeprecationErrorHandler/ConfigurationTest.php
@@ -463,6 +463,9 @@ public function testExistingBaselineAndGeneration()
$this->assertEquals(json_encode($expected, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename));
}
+ /**
+ * @requires PHPUnit < 10
+ */
public function testBaselineGenerationWithDeprecationTriggeredByDebugClassLoader()
{
$filename = $this->createFile();
@@ -474,7 +477,7 @@ public function testBaselineGenerationWithDeprecationTriggeredByDebugClassLoader
$trace[2] = [
'class' => DebugClassLoader::class,
'function' => 'testBaselineGenerationWithDeprecationTriggeredByDebugClassLoader',
- 'args' => [self::class]
+ 'args' => [self::class],
];
$deprecation = new Deprecation('Deprecation by debug class loader', $trace, '');
diff --git a/Tests/DeprecationErrorHandler/log_file.phpt b/Tests/DeprecationErrorHandler/log_file.phpt
index 51f8d6c..12f9ed4 100644
--- a/Tests/DeprecationErrorHandler/log_file.phpt
+++ b/Tests/DeprecationErrorHandler/log_file.phpt
@@ -1,8 +1,10 @@
--TEST--
Test DeprecationErrorHandler with log file
+--SKIPIF--
+=')) echo 'Skipping on PHPUnit 10+';
--FILE--
+
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: