From 4dddc8cad8f21a39e1df0ddbf806605773a1d6bb Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Thu, 28 Nov 2024 10:20:28 +0100 Subject: [PATCH] Closes #6047 --- ChangeLog-12.0.md | 1 + src/Framework/Assert.php | 53 +++---------------- .../Traversable/TraversableContainsOnly.php | 27 ++++++---- .../Assert/assertContainsOnlyTest.php | 5 -- .../Assert/assertNotContainsOnlyTest.php | 3 -- .../TraversableContainsOnlyTest.php | 20 ++----- 6 files changed, 28 insertions(+), 81 deletions(-) diff --git a/ChangeLog-12.0.md b/ChangeLog-12.0.md index d611dfb79ee..ecb045da46c 100644 --- a/ChangeLog-12.0.md +++ b/ChangeLog-12.0.md @@ -36,5 +36,6 @@ All notable changes of the PHPUnit 12.0 release series are documented in this fi * [#5959](https://github.com/sebastianbergmann/phpunit/issues/5959): Support for `#[CoversTrait]` and `#[UsesTrait]` attributes * [#5961](https://github.com/sebastianbergmann/phpunit/issues/5961): Support for targeting trait methods with the `#[CoversMethod]` and `#[UsesMethod]` attributes * [#5978](https://github.com/sebastianbergmann/phpunit/issues/5978): Support for PHP 8.2 +* [#6047](https://github.com/sebastianbergmann/phpunit/issues/6047): Support for using `assertContainsOnly()` (and `assertNotContainsOnly()`) with classes and interfaces [12.0.0]: https://github.com/sebastianbergmann/phpunit/compare/11.5...main diff --git a/src/Framework/Assert.php b/src/Framework/Assert.php index c903d65587b..fde9f130e89 100644 --- a/src/Framework/Assert.php +++ b/src/Framework/Assert.php @@ -272,25 +272,11 @@ final public static function assertNotContainsEquals(mixed $needle, iterable $ha * @throws Exception * @throws ExpectationFailedException */ - final public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + final public static function assertContainsOnly(string $type, iterable $haystack, string $message = ''): void { - if ($isNativeType === null) { - $isNativeType = self::isNativeType($type); - } - - if (!$isNativeType || class_exists($type) || interface_exists($type)) { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - null, - 'Using assertContainsOnly() with classes or interfaces is deprecated. Support for this will be removed in PHPUnit 12. Please use assertContainsOnlyInstancesOf() instead.', - ); - } - self::assertThat( $haystack, - new TraversableContainsOnly( - $type, - $isNativeType, - ), + TraversableContainsOnly::forNativeType($type), $message, ); } @@ -308,10 +294,7 @@ final public static function assertContainsOnlyInstancesOf(string $className, it { self::assertThat( $haystack, - new TraversableContainsOnly( - $className, - false, - ), + TraversableContainsOnly::forClassOrInterface($className), $message, ); } @@ -325,26 +308,12 @@ final public static function assertContainsOnlyInstancesOf(string $className, it * @throws Exception * @throws ExpectationFailedException */ - final public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + final public static function assertNotContainsOnly(string $type, iterable $haystack, string $message = ''): void { - if ($isNativeType === null) { - $isNativeType = self::isNativeType($type); - } - - if (!$isNativeType || class_exists($type) || interface_exists($type)) { - Event\Facade::emitter()->testTriggeredPhpunitDeprecation( - null, - 'Using assertNotContainsOnly() with classes or interfaces is deprecated. Support for this will be removed in PHPUnit 12. Please use assertContainsOnlyInstancesOf() instead.', - ); - } - self::assertThat( $haystack, new LogicalNot( - new TraversableContainsOnly( - $type, - $isNativeType, - ), + TraversableContainsOnly::forNativeType($type), ), $message, ); @@ -2223,7 +2192,7 @@ final public static function containsIdentical(mixed $value): TraversableContain */ final public static function containsOnly(string $type): TraversableContainsOnly { - return new TraversableContainsOnly($type); + return TraversableContainsOnly::forNativeType($type); } /** @@ -2233,7 +2202,7 @@ final public static function containsOnly(string $type): TraversableContainsOnly */ final public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly { - return new TraversableContainsOnly($className, false); + return TraversableContainsOnly::forClassOrInterface($className); } final public static function arrayHasKey(int|string $key): ArrayHasKey @@ -2437,12 +2406,4 @@ final public static function resetCount(): void { self::$count = 0; } - - private static function isNativeType(string $type): bool - { - return match ($type) { - 'numeric', 'integer', 'int', 'iterable', 'float', 'string', 'boolean', 'bool', 'null', 'array', 'object', 'resource', 'scalar' => true, - default => false, - }; - } } diff --git a/src/Framework/Constraint/Traversable/TraversableContainsOnly.php b/src/Framework/Constraint/Traversable/TraversableContainsOnly.php index da752e910db..e3e1ac675ff 100644 --- a/src/Framework/Constraint/Traversable/TraversableContainsOnly.php +++ b/src/Framework/Constraint/Traversable/TraversableContainsOnly.php @@ -9,7 +9,6 @@ */ namespace PHPUnit\Framework\Constraint; -use PHPUnit\Framework\Exception; use PHPUnit\Framework\ExpectationFailedException; /** @@ -21,19 +20,25 @@ final class TraversableContainsOnly extends Constraint private readonly string $type; /** - * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string'|class-string $type - * - * @throws Exception + * @param 'array'|'bool'|'boolean'|'callable'|'double'|'float'|'int'|'integer'|'iterable'|'null'|'numeric'|'object'|'real'|'resource (closed)'|'resource'|'scalar'|'string' $type */ - public function __construct(string $type, bool $isNativeType = true) + public static function forNativeType(string $type): self { - if ($isNativeType) { - $this->constraint = new IsType($type); - } else { - $this->constraint = new IsInstanceOf($type); - } + return new self(new IsType($type), $type); + } - $this->type = $type; + /** + * @param class-string $type + */ + public static function forClassOrInterface(string $type): self + { + return new self(new IsInstanceOf($type), $type); + } + + private function __construct(IsInstanceOf|IsType $constraint, string $type) + { + $this->constraint = $constraint; + $this->type = $type; } /** diff --git a/tests/unit/Framework/Assert/assertContainsOnlyTest.php b/tests/unit/Framework/Assert/assertContainsOnlyTest.php index 8623ecaf70b..3d3ac352763 100644 --- a/tests/unit/Framework/Assert/assertContainsOnlyTest.php +++ b/tests/unit/Framework/Assert/assertContainsOnlyTest.php @@ -12,16 +12,13 @@ use function fopen; use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\TestDox; use stdClass; #[CoversMethod(Assert::class, 'assertContainsOnly')] -#[CoversMethod(Assert::class, 'isNativeType')] #[TestDox('assertContainsOnly()')] #[Small] -#[IgnorePhpunitDeprecations] final class assertContainsOnlyTest extends TestCase { /** @@ -42,7 +39,6 @@ public static function successProvider(): array ['resource', [fopen(__FILE__, 'r')]], ['scalar', [true, 1.0, 1, 'string']], ['string', ['string']], - [stdClass::class, [new stdClass]], ]; } @@ -64,7 +60,6 @@ public static function failureProvider(): array ['resource', [fopen(__FILE__, 'r'), null]], ['scalar', [true, 1.0, 1, 'string', null]], ['string', ['string', null]], - [stdClass::class, [new stdClass, null]], ]; } diff --git a/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php b/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php index f7982d5670a..e126b7c5bb4 100644 --- a/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php +++ b/tests/unit/Framework/Assert/assertNotContainsOnlyTest.php @@ -11,15 +11,12 @@ use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\Attributes\DataProviderExternal; -use PHPUnit\Framework\Attributes\IgnorePhpunitDeprecations; use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\Attributes\TestDox; #[CoversMethod(Assert::class, 'assertNotContainsOnly')] -#[CoversMethod(Assert::class, 'isNativeType')] #[TestDox('assertNotContainsOnly()')] #[Small] -#[IgnorePhpunitDeprecations] final class assertNotContainsOnlyTest extends TestCase { #[DataProviderExternal(assertContainsOnlyTest::class, 'failureProvider')] diff --git a/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php b/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php index c6f66a34771..f39fe7904a8 100644 --- a/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php +++ b/tests/unit/Framework/Constraint/Traversable/TraversableContainsOnlyTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\Attributes\Small; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use stdClass; #[CoversClass(TraversableContainsOnly::class)] #[CoversClass(Constraint::class)] @@ -28,18 +27,9 @@ public static function provider(): array true, '', 'integer', - true, [0, 1, 2], ], - [ - true, - '', - stdClass::class, - false, - [new stdClass, new stdClass, new stdClass], - ], - [ false, <<<'EOT' @@ -50,16 +40,15 @@ public static function provider(): array ] contains only values of type "integer". EOT, 'integer', - true, [0, '1', 2], ], ]; } #[DataProvider('provider')] - public function testCanBeEvaluated(bool $result, string $failureDescription, string $expected, bool $isNativeType, mixed $actual): void + public function testCanBeEvaluated(bool $result, string $failureDescription, string $expected, mixed $actual): void { - $constraint = new TraversableContainsOnly($expected, $isNativeType); + $constraint = TraversableContainsOnly::forNativeType($expected); $this->assertSame($result, $constraint->evaluate($actual, returnResult: true)); @@ -75,12 +64,11 @@ public function testCanBeEvaluated(bool $result, string $failureDescription, str public function testCanBeRepresentedAsString(): void { - $this->assertSame('contains only values of type "integer"', (new TraversableContainsOnly('integer'))->toString()); - $this->assertSame('contains only values of type "stdClass"', (new TraversableContainsOnly(stdClass::class, false))->toString()); + $this->assertSame('contains only values of type "integer"', TraversableContainsOnly::forNativeType('integer')->toString()); } public function testIsCountable(): void { - $this->assertCount(1, (new TraversableContainsOnly(stdClass::class, false))); + $this->assertCount(1, TraversableContainsOnly::forNativeType('integer')); } }