Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.x] Bring in array subset code directly #30989

Merged
merged 3 commits into from Dec 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions composer.json
Expand Up @@ -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",
Expand Down Expand Up @@ -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).",
Expand Down
64 changes: 27 additions & 37 deletions src/Illuminate/Foundation/Testing/Assert.php
Expand Up @@ -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);
}
}
279 changes: 279 additions & 0 deletions src/Illuminate/Foundation/Testing/Constraints/ArraySubset.php
@@ -0,0 +1,279 @@
<?php

namespace Illuminate\Foundation\Testing\Constraints;

use ArrayObject;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Runner\Version;
use SebastianBergmann\Comparator\ComparisonFailure;
use Traversable;

if (class_exists(Version::class) && (int) Version::series()[0] >= 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;
}
}
}