Skip to content

Commit 9d28a53

Browse files
committed
Assert::equal() added flags $matchOrder & $matchIdentity
1 parent 3ec6c66 commit 9d28a53

File tree

3 files changed

+181
-8
lines changed

3 files changed

+181
-8
lines changed

src/Framework/Assert.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,18 @@ public static function notSame(mixed $expected, mixed $actual, ?string $descript
7474

7575
/**
7676
* Asserts that two values are equal and checks expectations. The identity of objects,
77-
* the order of keys in the arrays and marginally different floats are ignored.
77+
* the order of keys in the arrays and marginally different floats are ignored by default.
7878
*/
79-
public static function equal(mixed $expected, mixed $actual, ?string $description = null): void
79+
public static function equal(
80+
mixed $expected,
81+
mixed $actual,
82+
?string $description = null,
83+
bool $matchOrder = false,
84+
bool $matchIdentity = false,
85+
): void
8086
{
8187
self::$counter++;
82-
if (!self::isEqual($expected, $actual)) {
88+
if (!self::isEqual($expected, $actual, $matchOrder, $matchIdentity)) {
8389
self::fail(self::describe('%1 should be equal to %2', $description), $actual, $expected);
8490
}
8591
}
@@ -93,7 +99,7 @@ public static function notEqual(mixed $expected, mixed $actual, ?string $descrip
9399
{
94100
self::$counter++;
95101
try {
96-
$res = self::isEqual($expected, $actual);
102+
$res = self::isEqual($expected, $actual, matchOrder: false, matchIdentity: false);
97103
} catch (AssertException $e) {
98104
}
99105

@@ -602,6 +608,8 @@ public static function expandMatchingPatterns(string $pattern, string $actual):
602608
private static function isEqual(
603609
mixed $expected,
604610
mixed $actual,
611+
bool $matchOrder,
612+
bool $matchIdentity,
605613
int $level = 0,
606614
?\SplObjectStorage $objects = null
607615
): bool
@@ -618,7 +626,7 @@ private static function isEqual(
618626
$diff = abs($expected - $actual);
619627
return ($diff < self::Epsilon) || ($diff / max(abs($expected), abs($actual)) < self::Epsilon);
620628

621-
case is_object($expected) && is_object($actual) && $expected::class === $actual::class:
629+
case !$matchIdentity && is_object($expected) && is_object($actual) && $expected::class === $actual::class:
622630
$objects = $objects ? clone $objects : new \SplObjectStorage;
623631
if (isset($objects[$expected])) {
624632
return $objects[$expected] === $actual;
@@ -633,14 +641,20 @@ private static function isEqual(
633641
// break omitted
634642

635643
case is_array($expected) && is_array($actual):
636-
ksort($expected, SORT_STRING);
637-
ksort($actual, SORT_STRING);
644+
if ($matchOrder) {
645+
reset($expected);
646+
reset($actual);
647+
} else {
648+
ksort($expected, SORT_STRING);
649+
ksort($actual, SORT_STRING);
650+
}
651+
638652
if (array_keys($expected) !== array_keys($actual)) {
639653
return false;
640654
}
641655

642656
foreach ($expected as $value) {
643-
if (!self::isEqual($value, current($actual), $level + 1, $objects)) {
657+
if (!self::isEqual($value, current($actual), $matchOrder, $matchIdentity, $level + 1, $objects)) {
644658
return false;
645659
}
646660

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Tester\Assert;
6+
7+
require __DIR__ . '/../bootstrap.php';
8+
9+
10+
$obj1 = new stdClass;
11+
$obj1->{'0'} = 'a';
12+
$obj1->{'1'} = 'b';
13+
14+
$obj2 = new stdClass;
15+
$obj2->{'1'} = 'b';
16+
$obj2->{'0'} = 'a';
17+
18+
$obj3 = new stdClass;
19+
$obj3->x = $obj3->y = new stdClass;
20+
21+
$obj4 = new stdClass;
22+
$obj4->x = new stdClass;
23+
$obj4->y = new stdClass;
24+
25+
$deep1 = $deep2 = new stdClass;
26+
$deep1->x = $deep2->x = $deep1;
27+
28+
$float1 = 1 / 3;
29+
$float2 = 1 - 2 / 3;
30+
31+
$equals = [
32+
[1, 1],
33+
['1', '1'],
34+
[['1'], ['1']],
35+
[['a', 'b'], [1 => 'b', 0 => 'a']],
36+
[['a' => true, 'b' => false], ['b' => false, 'a' => true]],
37+
[$float1, $float2],
38+
[$float1 * 1e9, $float2 * 1e9],
39+
[$float1 - $float2, 0.0],
40+
[$float1 - $float2, $float2 - $float1],
41+
[0.0, 0.0],
42+
[INF, INF],
43+
[[0 => 'a', 'str' => 'b'], ['str' => 'b', 0 => 'a']],
44+
[$deep1, $deep2],
45+
[Tester\Expect::type('int'), 1],
46+
];
47+
48+
$notEquals = [
49+
[1, 1.0],
50+
[new stdClass, new stdClass],
51+
[[new stdClass], [new stdClass]],
52+
[$obj3, $obj4],
53+
[INF, -INF],
54+
[['a', 'b'], ['b', 'a']],
55+
[NAN, NAN],
56+
[Tester\Expect::type('int'), '1', 'string should be int'],
57+
];
58+
59+
60+
61+
foreach ($equals as [$expected, $value]) {
62+
Assert::equal($expected, $value, matchIdentity: true);
63+
}
64+
65+
foreach ($notEquals as [$expected, $value]) {
66+
Assert::exception(function () use ($expected, $value) {
67+
Assert::equal($expected, $value, matchIdentity: true);
68+
}, Tester\AssertException::class, '%a% should be %a%');
69+
}
70+
71+
Assert::exception(function () {
72+
$rec = [];
73+
$rec[] = &$rec;
74+
Assert::equal($rec, $rec, matchIdentity: true);
75+
}, Exception::class, 'Nesting level too deep or recursive dependency.');
76+
77+
Assert::exception(function () {
78+
Assert::equal(true, false, 'Custom description', matchIdentity: true);
79+
}, Tester\AssertException::class, 'Custom description: %a% should be %a%');
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Tester\Assert;
6+
7+
require __DIR__ . '/../bootstrap.php';
8+
9+
10+
$obj1 = new stdClass;
11+
$obj1->{'0'} = 'a';
12+
$obj1->{'1'} = 'b';
13+
14+
$obj2 = new stdClass;
15+
$obj2->{'1'} = 'b';
16+
$obj2->{'0'} = 'a';
17+
18+
$obj3 = new stdClass;
19+
$obj3->x = $obj3->y = new stdClass;
20+
21+
$obj4 = new stdClass;
22+
$obj4->x = new stdClass;
23+
$obj4->y = new stdClass;
24+
25+
$deep1 = $deep2 = new stdClass;
26+
$deep1->x = $deep2->x = $deep1;
27+
28+
$float1 = 1 / 3;
29+
$float2 = 1 - 2 / 3;
30+
31+
$equals = [
32+
[1, 1],
33+
['1', '1'],
34+
[['1'], ['1']],
35+
[new stdClass, new stdClass],
36+
[[new stdClass], [new stdClass]],
37+
[$float1, $float2],
38+
[$float1 * 1e9, $float2 * 1e9],
39+
[$float1 - $float2, 0.0],
40+
[$float1 - $float2, $float2 - $float1],
41+
[0.0, 0.0],
42+
[INF, INF],
43+
[$deep1, $deep2],
44+
[Tester\Expect::type('int'), 1],
45+
];
46+
47+
$notEquals = [
48+
[1, 1.0],
49+
[['a', 'b'], [1 => 'b', 0 => 'a']],
50+
[['a' => true, 'b' => false], ['b' => false, 'a' => true]],
51+
[INF, -INF],
52+
[$obj1, $obj2],
53+
[$obj3, $obj4],
54+
[[0 => 'a', 'str' => 'b'], ['str' => 'b', 0 => 'a']],
55+
[['a', 'b'], ['b', 'a']],
56+
[NAN, NAN],
57+
[Tester\Expect::type('int'), '1', 'string should be int'],
58+
];
59+
60+
61+
62+
foreach ($equals as [$expected, $value]) {
63+
Assert::equal($expected, $value, matchOrder: true);
64+
}
65+
66+
foreach ($notEquals as [$expected, $value]) {
67+
Assert::exception(function () use ($expected, $value) {
68+
Assert::equal($expected, $value, matchOrder: true);
69+
}, Tester\AssertException::class, '%a% should be %a%');
70+
}
71+
72+
Assert::exception(function () {
73+
$rec = [];
74+
$rec[] = &$rec;
75+
Assert::equal($rec, $rec, matchOrder: true);
76+
}, Exception::class, 'Nesting level too deep or recursive dependency.');
77+
78+
Assert::exception(function () {
79+
Assert::equal(true, false, 'Custom description', matchOrder: true);
80+
}, Tester\AssertException::class, 'Custom description: %a% should be %a%');

0 commit comments

Comments
 (0)