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

Consolidates asserts#equal branches for keyed collections (Map/Set) and supports deep equality of Map keys #3258

Merged
merged 13 commits into from
Nov 4, 2019
Merged
50 changes: 26 additions & 24 deletions std/testing/asserts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,13 @@ function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>): string[] {
return messages;
}

function isKeyedCollection(x: unknown): x is Set<unknown> {
return [Symbol.iterator, "size"].every(k => k in (x as Set<unknown>));
}

export function equal(c: unknown, d: unknown): boolean {
const seen = new Map();
return (function compare(a: unknown, b: unknown): boolean {
if (a && a instanceof Set && b && b instanceof Set) {
if (a.size !== b.size) {
return false;
}
for (const item of b) {
if (!a.has(item)) {
return false;
}
}
return true;
}
if (a && b && a instanceof Map && b instanceof Map) {
if (a.size !== b.size) {
return false;
}

for (const [key, value] of a) {
if (!compare(value, b.get(key))) {
return false;
}
}

return true;
}
// Have to render RegExp & Date for string comparison
// unless it's mistreated as object
if (
Expand All @@ -114,6 +94,28 @@ export function equal(c: unknown, d: unknown): boolean {
if (Object.keys(a || {}).length !== Object.keys(b || {}).length) {
return false;
}
if (isKeyedCollection(a) && isKeyedCollection(b)) {
if (a.size !== b.size) {
return false;
}

let unmatchedEntries = a.size;

for (const [aKey, aValue] of a.entries()) {
for (const [bKey, bValue] of b.entries()) {
/* Given that Map keys can be references, we need
* to ensure that they are also deeply equal */
if (
(aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
(compare(aKey, bKey) && compare(aValue, bValue))
) {
unmatchedEntries--;
}
}
}

return unmatchedEntries === 0;
}
const merged = { ...a, ...b };
for (const key in merged) {
type Key = keyof typeof merged;
Expand Down
11 changes: 11 additions & 0 deletions std/testing/asserts_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ test(function testingEqual(): void {
assert(equal(new Set([1]), new Set([1])));
assert(!equal(new Set([1]), new Set([2])));
assert(equal(new Set([1, 2, 3]), new Set([3, 2, 1])));
assert(equal(new Set([1, new Set([2, 3])]), new Set([new Set([3, 2]), 1])));
assert(!equal(new Set([1, 2]), new Set([3, 2, 1])));
assert(!equal(new Set([1, 2, 3]), new Set([4, 5, 6])));
assert(equal(new Set("denosaurus"), new Set("denosaurussss")));
Expand Down Expand Up @@ -83,6 +84,16 @@ test(function testingEqual(): void {
new Map([["foo", new Map([["bar", "qux"]])]])
)
);
assert(equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, true]])));
assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, false]])));
assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 2 }, true]])));
assert(equal([1, 2, 3], [1, 2, 3]));
assert(equal([1, [2, 3]], [1, [2, 3]]));
assert(!equal([1, 2, 3, 4], [1, 2, 3]));
assert(!equal([1, 2, 3, 4], [1, 2, 3]));
assert(!equal([1, 2, 3, 4], [1, 4, 2, 3]));
assert(equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2, 3, 4])));
assert(!equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([2, 1, 4, 3])));
});

test(function testingNotEquals(): void {
Expand Down