diff --git a/composer.json b/composer.json index 1f413f34eeb7..0c52879ab4f1 100644 --- a/composer.json +++ b/composer.json @@ -80,7 +80,6 @@ "aws/aws-sdk-php": "^3.0", "doctrine/dbal": "^2.6", "filp/whoops": "^2.4", - "graham-campbell/testbench-core": "^3.1", "guzzlehttp/guzzle": "^6.3", "league/flysystem-cached-adapter": "^1.0", "mockery/mockery": "^1.3.1", @@ -123,7 +122,6 @@ "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).", "filp/whoops": "Required for friendly error pages in development (^2.4).", "fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).", - "graham-campbell/testbench-core": "Required to use the foundation testing component with PHPUnit 9 (^3.1).", "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.0).", "laravel/tinker": "Required to use the tinker console command (^1.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", diff --git a/src/Illuminate/Foundation/Testing/Assert.php b/src/Illuminate/Foundation/Testing/Assert.php index b6cbc8a0b95e..3a35bebe8852 100644 --- a/src/Illuminate/Foundation/Testing/Assert.php +++ b/src/Illuminate/Foundation/Testing/Assert.php @@ -3,55 +3,45 @@ namespace Illuminate\Foundation\Testing; use ArrayAccess; -use Exception; -use GrahamCampbell\TestBenchCore\ArraySubsetTrait; +use Illuminate\Foundation\Testing\Constraints\ArraySubset; use PHPUnit\Framework\Assert as PHPUnit; -use PHPUnit\Framework\Constraint\ArraySubset; -use PHPUnit\Runner\Version; +use PHPUnit\Framework\InvalidArgumentException; use PHPUnit\Util\InvalidArgumentHelper; -if (trait_exists(ArraySubsetTrait::class)) { +/** + * @internal This class is not meant to be used or overwritten outside the framework itself. + */ +abstract class Assert extends PHPUnit +{ /** - * @internal This class is not meant to be used or overwritten outside the framework itself. + * Asserts that an array has a specified subset. + * + * @param \ArrayAccess|array $subset + * @param \ArrayAccess|array $array + * @param bool $checkForIdentity + * @param string $msg + * @return void */ - abstract class Assert extends PHPUnit + public static function assertArraySubset($subset, $array, bool $checkForIdentity = false, string $msg = ''): void { - use ArraySubsetTrait; - } -} else { - /** - * @internal This class is not meant to be used or overwritten outside the framework itself. - */ - abstract class Assert extends PHPUnit - { - /** - * Asserts that an array has a specified subset. - * - * This method was taken over from PHPUnit where it was deprecated. See link for more info. - * - * @param \ArrayAccess|array $subset - * @param \ArrayAccess|array $array - * @param bool $checkForObjectIdentity - * @param string $message - * @return void - */ - public static function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void - { - if ((int) Version::series()[0] > 8) { - throw new Exception('For PHPUnit 9 support, please install graham-campbell/testbench-core:"^3.1".'); - } - - if (! (is_array($subset) || $subset instanceof ArrayAccess)) { + if (! (is_array($subset) || $subset instanceof ArrayAccess)) { + if (class_exists(InvalidArgumentException::class)) { + throw InvalidArgumentException::create(1, 'array or ArrayAccess'); + } else { throw InvalidArgumentHelper::factory(1, 'array or ArrayAccess'); } + } - if (! (is_array($array) || $array instanceof ArrayAccess)) { + if (! (is_array($array) || $array instanceof ArrayAccess)) { + if (class_exists(InvalidArgumentException::class)) { + throw InvalidArgumentException::create(2, 'array or ArrayAccess'); + } else { throw InvalidArgumentHelper::factory(2, 'array or ArrayAccess'); } + } - $constraint = new ArraySubset($subset, $checkForObjectIdentity); + $constraint = new ArraySubset($subset, $checkForIdentity); - static::assertThat($array, $constraint, $message); - } + PHPUnit::assertThat($array, $constraint, $msg); } } diff --git a/src/Illuminate/Foundation/Testing/Constraints/ArraySubset.php b/src/Illuminate/Foundation/Testing/Constraints/ArraySubset.php new file mode 100644 index 000000000000..edfbf96d4ea6 --- /dev/null +++ b/src/Illuminate/Foundation/Testing/Constraints/ArraySubset.php @@ -0,0 +1,279 @@ += 9) { + /** + * @internal This class is not meant to be used or overwritten outside the framework itself. + */ + final class ArraySubset extends Constraint + { + /** + * @var iterable + */ + private $subset; + + /** + * @var bool + */ + private $strict; + + /** + * Create a new array subset constraint instance. + * + * @param iterable $subset + * @param bool $strict + * @return void + */ + public function __construct(iterable $subset, bool $strict = false) + { + $this->strict = $strict; + $this->subset = $subset; + } + + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other + * @param string $description + * @param bool $returnResult + * @return bool|null + * + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, string $description = '', bool $returnResult = false): ?bool + { + // type cast $other & $this->subset as an array to allow + // support in standard array functions. + $other = $this->toArray($other); + $this->subset = $this->toArray($this->subset); + + $patched = array_replace_recursive($other, $this->subset); + + if ($this->strict) { + $result = $other === $patched; + } else { + $result = $other == $patched; + } + + if ($returnResult) { + return $result; + } + + if (! $result) { + $f = new ComparisonFailure( + $patched, + $other, + var_export($patched, true), + var_export($other, true) + ); + + $this->fail($other, $description, $f); + } + + return null; + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @return string + */ + public function toString(): string + { + return 'has the subset '.$this->exporter()->export($this->subset); + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other + * @return string + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return 'an array '.$this->toString(); + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param iterable $other + * @return array + */ + private function toArray(iterable $other): array + { + if (is_array($other)) { + return $other; + } + + if ($other instanceof ArrayObject) { + return $other->getArrayCopy(); + } + + if ($other instanceof Traversable) { + return iterator_to_array($other); + } + + // Keep BC even if we know that array would not be the expected one + return (array) $other; + } + } +} else { + /** + * @internal This class is not meant to be used or overwritten outside the framework itself. + */ + final class ArraySubset extends Constraint + { + /** + * @var iterable + */ + private $subset; + + /** + * @var bool + */ + private $strict; + + /** + * Create a new array subset constraint instance. + * + * @param iterable $subset + * @param bool $strict + * @return void + */ + public function __construct(iterable $subset, bool $strict = false) + { + $this->strict = $strict; + $this->subset = $subset; + } + + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other + * @param string $description + * @param bool $returnResult + * @return bool|null + * + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, string $description = '', bool $returnResult = false) + { + // type cast $other & $this->subset as an array to allow + // support in standard array functions. + $other = $this->toArray($other); + $this->subset = $this->toArray($this->subset); + + $patched = array_replace_recursive($other, $this->subset); + + if ($this->strict) { + $result = $other === $patched; + } else { + $result = $other == $patched; + } + + if ($returnResult) { + return $result; + } + + if (! $result) { + $f = new ComparisonFailure( + $patched, + $other, + var_export($patched, true), + var_export($other, true) + ); + + $this->fail($other, $description, $f); + } + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @return string + */ + public function toString(): string + { + return 'has the subset '.$this->exporter()->export($this->subset); + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other + * @return string + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return 'an array '.$this->toString(); + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param iterable $other + * @return array + */ + private function toArray(iterable $other): array + { + if (is_array($other)) { + return $other; + } + + if ($other instanceof ArrayObject) { + return $other->getArrayCopy(); + } + + if ($other instanceof Traversable) { + return iterator_to_array($other); + } + + // Keep BC even if we know that array would not be the expected one + return (array) $other; + } + } +}