Skip to content

Commit

Permalink
Closes #6047
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbergmann committed Nov 28, 2024
1 parent 43368e6 commit da21665
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 79 deletions.
1 change: 1 addition & 0 deletions ChangeLog-12.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
53 changes: 7 additions & 46 deletions src/Framework/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
Expand All @@ -308,10 +294,7 @@ final public static function assertContainsOnlyInstancesOf(string $className, it
{
self::assertThat(
$haystack,
new TraversableContainsOnly(
$className,
false,
),
TraversableContainsOnly::forClassOrInterface($className),
$message,
);
}
Expand All @@ -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,
);
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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
Expand Down Expand Up @@ -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,
};
}
}
27 changes: 16 additions & 11 deletions src/Framework/Constraint/Traversable/TraversableContainsOnly.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
*/
namespace PHPUnit\Framework\Constraint;

use PHPUnit\Framework\Exception;
use PHPUnit\Framework\ExpectationFailedException;

/**
Expand All @@ -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;
}

/**
Expand Down
5 changes: 0 additions & 5 deletions tests/unit/Framework/Assert/assertContainsOnlyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/**
Expand All @@ -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]],
];
}

Expand All @@ -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]],
];
}

Expand Down
1 change: 0 additions & 1 deletion tests/unit/Framework/Assert/assertNotContainsOnlyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use PHPUnit\Framework\Attributes\TestDox;

#[CoversMethod(Assert::class, 'assertNotContainsOnly')]
#[CoversMethod(Assert::class, 'isNativeType')]
#[TestDox('assertNotContainsOnly()')]
#[Small]
#[IgnorePhpunitDeprecations]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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'
Expand All @@ -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));

Expand All @@ -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'));
}
}

0 comments on commit da21665

Please sign in to comment.