Bug 1300385 - Fix flaws in SimpleTest.isDeeply. r=Waldo

- When one of the parameters to isDeeply is an object/function
  and the other is not, isDeeply returned false. Well, isDeeply
  is supposed to report an error instead of returning a value...

- Change the implementation of isDeeply to have SameValue semantics
  instead of weak equality.

- Change the representation of arrays to look like an array, instead
  of its default toString() value which is indistinguishable from a
  string due to the lack of brackets and quotes.

- Account for missing object properties;
  Distinguish them from "undefined" with the special DNE tag.

MozReview-Commit-ID: F1OJhbXcptl

--HG--
extra : rebase_source : 26091a40445064da3f87d61438bd74bbe7491363
This commit is contained in:
Rob Wu 2016-09-03 22:00:45 -07:00
Родитель 54cc14572f
Коммит a03dbe288b
1 изменённых файлов: 23 добавлений и 50 удалений

Просмотреть файл

@ -92,6 +92,8 @@ if (typeof(repr) == 'undefined') {
ostring = (1 / o > 0) ? "+0" : "-0";
} else if (typeof o === "string") {
ostring = JSON.stringify(o);
} else if (Array.isArray(o)) {
ostring = "[" + o.map(val => repr(val)).join(", ") + "]";
} else {
ostring = (o + "");
}
@ -1378,38 +1380,22 @@ if (isPrimaryTestWindow) {
SimpleTest.DNE = {dne: 'Does not exist'};
SimpleTest.LF = "\r\n";
SimpleTest._isRef = function (object) {
var type = typeof(object);
return type == 'object' || type == 'function';
};
SimpleTest._deepCheck = function (e1, e2, stack, seen) {
var ok = false;
// Either they're both references or both not.
var sameRef = !(!SimpleTest._isRef(e1) ^ !SimpleTest._isRef(e2));
if (e1 == null && e2 == null) {
if (Object.is(e1, e2)) {
// Handles identical primitives and references.
ok = true;
} else if (e1 != null ^ e2 != null) {
} else if (typeof e1 != "object" || typeof e2 != "object" || e1 === null || e2 === null) {
// If either argument is a primitive or function, don't consider the arguments the same.
ok = false;
} else if (e1 == SimpleTest.DNE ^ e2 == SimpleTest.DNE) {
} else if (e1 == SimpleTest.DNE || e2 == SimpleTest.DNE) {
ok = false;
} else if (sameRef && e1 == e2) {
// Handles primitives and any variables that reference the same
// object, including functions.
ok = true;
} else if (SimpleTest.isa(e1, 'Array') && SimpleTest.isa(e2, 'Array')) {
ok = SimpleTest._eqArray(e1, e2, stack, seen);
} else if (typeof e1 == "object" && typeof e2 == "object") {
ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
} else if (typeof e1 == "number" && typeof e2 == "number"
&& isNaN(e1) && isNaN(e2)) {
ok = true;
} else {
// If we get here, they're not the same (function references must
// always simply reference the same function).
stack.push({ vals: [e1, e2] });
ok = false;
ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
}
return ok;
};
@ -1437,11 +1423,11 @@ SimpleTest._eqArray = function (a1, a2, stack, seen) {
var ok = true;
// Only examines enumerable attributes. Only works for numeric arrays!
// Associative arrays return 0. So call _eqAssoc() for them, instead.
var max = a1.length > a2.length ? a1.length : a2.length;
var max = Math.max(a1.length, a2.length);
if (max == 0) return SimpleTest._eqAssoc(a1, a2, stack, seen);
for (var i = 0; i < max; i++) {
var e1 = i > a1.length - 1 ? SimpleTest.DNE : a1[i];
var e2 = i > a2.length - 1 ? SimpleTest.DNE : a2[i];
var e1 = i < a1.length ? a1[i] : SimpleTest.DNE;
var e2 = i < a2.length ? a2[i] : SimpleTest.DNE;
stack.push({ type: 'Array', idx: i, vals: [e1, e2] });
ok = SimpleTest._deepCheck(e1, e2, stack, seen);
if (ok) {
@ -1482,8 +1468,8 @@ SimpleTest._eqAssoc = function (o1, o2, stack, seen) {
var o2Size = 0; for (var i in o2) o2Size++;
var bigger = o1Size > o2Size ? o1 : o2;
for (var i in bigger) {
var e1 = o1[i] == undefined ? SimpleTest.DNE : o1[i];
var e2 = o2[i] == undefined ? SimpleTest.DNE : o2[i];
var e1 = i in o1 ? o1[i] : SimpleTest.DNE;
var e2 = i in o2 ? o2[i] : SimpleTest.DNE;
stack.push({ type: 'Object', idx: i, vals: [e1, e2] });
ok = SimpleTest._deepCheck(e1, e2, stack, seen)
if (ok) {
@ -1502,7 +1488,7 @@ SimpleTest._formatStack = function (stack) {
var type = entry['type'];
var idx = entry['idx'];
if (idx != null) {
if (/^\d+$/.test(idx)) {
if (type == 'Array') {
// Numeric array index.
variable += '[' + idx + ']';
} else {
@ -1522,39 +1508,26 @@ SimpleTest._formatStack = function (stack) {
var out = "Structures begin differing at:" + SimpleTest.LF;
for (var i = 0; i < vals.length; i++) {
var val = vals[i];
if (val == null) {
val = 'undefined';
if (val === SimpleTest.DNE) {
val = "Does not exist";
} else {
val == SimpleTest.DNE ? "Does not exist" : "'" + val + "'";
val = repr(val);
}
out += vars[i] + ' = ' + val + SimpleTest.LF;
}
out += vars[0] + ' = ' + vals[0] + SimpleTest.LF;
out += vars[1] + ' = ' + vals[1] + SimpleTest.LF;
return ' ' + out;
};
SimpleTest.isDeeply = function (it, as, name) {
var ok;
// ^ is the XOR operator.
if (SimpleTest._isRef(it) ^ SimpleTest._isRef(as)) {
// One's a reference, one isn't.
ok = false;
} else if (!SimpleTest._isRef(it) && !SimpleTest._isRef(as)) {
// Neither is an object.
ok = SimpleTest.is(it, as, name);
var stack = [{ vals: [it, as] }];
var seen = [];
if ( SimpleTest._deepCheck(it, as, stack, seen)) {
SimpleTest.ok(true, name);
} else {
// We have two objects. Do a deep comparison.
var stack = [], seen = [];
if ( SimpleTest._deepCheck(it, as, stack, seen)) {
ok = SimpleTest.ok(true, name);
} else {
ok = SimpleTest.ok(false, name, SimpleTest._formatStack(stack));
}
SimpleTest.ok(false, name, SimpleTest._formatStack(stack));
}
return ok;
};
SimpleTest.typeOf = function (object) {