Merge branch 'theefer-fix-map+set-comparison'

- Merges #1406 from @theefer
- Fixes #1402
This commit is contained in:
Gregg Van Hove
2017-08-04 09:21:24 -07:00
4 changed files with 209 additions and 46 deletions

View File

@@ -2547,21 +2547,66 @@ getJasmineRequireObj().matchersUtil = function(j$) {
if (!result) {
return false;
}
} else if (className == '[object Set]' || className == '[object Map]') {
} else if (className == '[object Map]') {
if (a.size != b.size) {
diffBuilder.record(a, b);
return false;
}
var iterA = a.entries(), iterB = b.entries();
var valA, valB;
do {
valA = iterA.next();
valB = iterB.next();
if (!eq(valA.value, valB.value, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
diffBuilder.record(a, b);
return false;
// For both sets of keys, check they map to equal values in both maps
var mapKeys = [a.keys(), b.keys()];
var mapIter, mapKeyIt, mapKey;
for (i = 0; result && i < mapKeys.length; i++) {
mapIter = mapKeys[i];
mapKeyIt = mapIter.next();
while (result && !mapKeyIt.done) {
mapKey = mapKeyIt.value;
result = eq(a.get(mapKey), b.get(mapKey), aStack, bStack, customTesters, j$.NullDiffBuilder());
mapKeyIt = mapIter.next();
}
} while (!valA.done && !valB.done);
}
if (!result) {
diffBuilder.record(a, b);
return false;
}
} else if (className == '[object Set]') {
if (a.size != b.size) {
diffBuilder.record(a, b);
return false;
}
// For both sets, check they are all contained in the other set
var setPairs = [[a, b], [b, a]];
var baseIter, baseValueIt, baseValue;
var otherSet, otherIter, otherValueIt, otherValue, found;
for (i = 0; result && i < setPairs.length; i++) {
baseIter = setPairs[i][0].values();
otherSet = setPairs[i][1];
// For each value in the base set...
baseValueIt = baseIter.next();
while (result && !baseValueIt.done) {
baseValue = baseValueIt.value;
// ... test that it is present in the other set
otherIter = otherSet.values();
otherValueIt = otherIter.next();
// Optimisation: start looking for value by object identity
found = otherSet.has(baseValue);
// If not found, compare by value equality
while (!found && !otherValueIt.done) {
otherValue = otherValueIt.value;
found = eq(baseValue, otherValue, aStack, bStack, customTesters, j$.NullDiffBuilder());
otherValueIt = otherIter.next();
}
result = result && found;
baseValueIt = baseIter.next();
}
}
if (!result) {
diffBuilder.record(a, b);
return false;
}
} else {
// Objects with different constructors are not equivalent, but `Object`s

View File

@@ -393,6 +393,13 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(true);
});
it("passes when comparing identical sets with different insertion order", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([3, 6]);
var setB = new Set([6, 3]);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(true);
});
it("fails for sets with different elements", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([6, 3, 5]);
@@ -407,13 +414,6 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false);
});
it("fails for sets with different insertion order", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([3, 6]);
var setB = new Set([6, 3]);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false);
});
it("passes when comparing two empty maps", function() {
jasmine.getEnv().requireFunctioningMaps();
expect(jasmineUnderTest.matchersUtil.equals(new Map(), new Map())).toBe(true);
@@ -427,6 +427,13 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(true);
});
it("passes when comparing identical maps with different insertion order", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map([['a', 3], [6, 1]]);
var mapB = new Map([[6, 1], ['a', 3]]);
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(true);
});
it("fails for maps with different elements", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map([[6, 3], [5, 1]]);
@@ -441,13 +448,6 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false);
});
it("fails for maps with different insertion order", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map([['a', 3], [6, 1]]);
var mapB = new Map([[6, 1], ['a', 3]]);
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false);
});
describe("when running in an environment with array polyfills", function() {
// IE 8 doesn't support `definePropery` on non-DOM nodes
if (jasmine.getEnv().ieVersion < 9) { return; }

View File

@@ -359,8 +359,9 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("does not report deep mismatches within Sets", function() {
// TODO: implement deep comparison of set elements
// == Sets ==
it("reports mismatches between Sets", function() {
jasmine.getEnv().requireFunctioningSets();
var actual = new Set([1]),
@@ -370,6 +371,16 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports deep mismatches within Sets", function() {
jasmine.getEnv().requireFunctioningSets();
var actual = new Set([{x: 1}]),
expected = new Set([{x: 2}]),
message = 'Expected Set( Object({ x: 1 }) ) to equal Set( Object({ x: 2 }) ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Sets nested in objects", function() {
jasmine.getEnv().requireFunctioningSets();
@@ -390,13 +401,46 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("does not report deep mismatches within Maps", function() {
// TODO: implement deep comparison of Map elements
it("reports mismatches between Sets where actual is missing a value from expected", function() {
jasmine.getEnv().requireFunctioningSets();
// Use 'duplicate' object in actual so sizes match
var actual = new Set([{x: 1}, {x: 1}]),
expected = new Set([{x: 1}, {x: 2}]),
message = 'Expected Set( Object({ x: 1 }), Object({ x: 1 }) ) to equal Set( Object({ x: 1 }), Object({ x: 2 }) ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Sets where actual has a value missing from expected", function() {
jasmine.getEnv().requireFunctioningSets();
// Use 'duplicate' object in expected so sizes match
var actual = new Set([{x: 1}, {x: 2}]),
expected = new Set([{x: 1}, {x: 1}]),
message = 'Expected Set( Object({ x: 1 }), Object({ x: 2 }) ) to equal Set( Object({ x: 1 }), Object({ x: 1 }) ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
// == Maps ==
it("does not report mismatches between deep equal Maps", function() {
jasmine.getEnv().requireFunctioningSets();
// values are the same but with different object identity
var actual = new Map([['a', {x: 1}]]),
expected = new Map([['a', {x: 1}]]);
expect(compareEquals(actual, expected).pass).toBe(true);
});
it("reports deep mismatches within Maps", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map([['a', 1]]),
expected = new Map([['a', 2]]),
message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] ).";
var actual = new Map([['a', {x: 1}]]),
expected = new Map([['a', {x: 2}]]),
message = "Expected Map( [ 'a', Object({ x: 1 }) ] ) to equal Map( [ 'a', Object({ x: 2 }) ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
@@ -405,8 +449,8 @@ describe("toEqual", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = {Maps: [new Map([['a', 1]])]},
expected = {Maps: [new Map([['a', 2], ['b', 1]])]},
message = "Expected $.Maps[0] = Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ], [ 'b', 1 ] ).";
expected = {Maps: [new Map([['a', 2]])]},
message = "Expected $.Maps[0] = Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
@@ -415,11 +459,40 @@ describe("toEqual", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map([['a', 1]]),
expected = new Map([['a', 2]]),
message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] ).";
expected = new Map([['a', 2], ['b', 1]]),
message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ], [ 'b', 1 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
});
it("reports mismatches between Maps with equal values but differing keys", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map([['a', 1]]),
expected = new Map([['b', 1]]),
message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'b', 1 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("does not report mismatches between Maps with keys with same object identity", function() {
jasmine.getEnv().requireFunctioningMaps();
var key = {x: 1},
actual = new Map([[key, 2]]),
expected = new Map([[key, 2]]);
expect(compareEquals(actual, expected).pass).toBe(true);
});
it("reports mismatches between Maps with identical keys with different object identity", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map([[{x: 1}, 2]]),
expected = new Map([[{x: 1}, 2]]),
message = "Expected Map( [ Object({ x: 1 }), 2 ] ) to equal Map( [ Object({ x: 1 }), 2 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
function isNotRunningInBrowser() {
return typeof document === 'undefined'

View File

@@ -239,21 +239,66 @@ getJasmineRequireObj().matchersUtil = function(j$) {
if (!result) {
return false;
}
} else if (className == '[object Set]' || className == '[object Map]') {
} else if (className == '[object Map]') {
if (a.size != b.size) {
diffBuilder.record(a, b);
return false;
}
var iterA = a.entries(), iterB = b.entries();
var valA, valB;
do {
valA = iterA.next();
valB = iterB.next();
if (!eq(valA.value, valB.value, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
diffBuilder.record(a, b);
return false;
// For both sets of keys, check they map to equal values in both maps
var mapKeys = [a.keys(), b.keys()];
var mapIter, mapKeyIt, mapKey;
for (i = 0; result && i < mapKeys.length; i++) {
mapIter = mapKeys[i];
mapKeyIt = mapIter.next();
while (result && !mapKeyIt.done) {
mapKey = mapKeyIt.value;
result = eq(a.get(mapKey), b.get(mapKey), aStack, bStack, customTesters, j$.NullDiffBuilder());
mapKeyIt = mapIter.next();
}
} while (!valA.done && !valB.done);
}
if (!result) {
diffBuilder.record(a, b);
return false;
}
} else if (className == '[object Set]') {
if (a.size != b.size) {
diffBuilder.record(a, b);
return false;
}
// For both sets, check they are all contained in the other set
var setPairs = [[a, b], [b, a]];
var baseIter, baseValueIt, baseValue;
var otherSet, otherIter, otherValueIt, otherValue, found;
for (i = 0; result && i < setPairs.length; i++) {
baseIter = setPairs[i][0].values();
otherSet = setPairs[i][1];
// For each value in the base set...
baseValueIt = baseIter.next();
while (result && !baseValueIt.done) {
baseValue = baseValueIt.value;
// ... test that it is present in the other set
otherIter = otherSet.values();
otherValueIt = otherIter.next();
// Optimisation: start looking for value by object identity
found = otherSet.has(baseValue);
// If not found, compare by value equality
while (!found && !otherValueIt.done) {
otherValue = otherValueIt.value;
found = eq(baseValue, otherValue, aStack, bStack, customTesters, j$.NullDiffBuilder());
otherValueIt = otherIter.next();
}
result = result && found;
baseValueIt = baseIter.next();
}
}
if (!result) {
diffBuilder.record(a, b);
return false;
}
} else {
// Objects with different constructors are not equivalent, but `Object`s