From a03dbe288b88ade0f20f8d4990fa6fdc92362c50 Mon Sep 17 00:00:00 2001 From: Rob Wu Date: Sat, 3 Sep 2016 22:00:45 -0700 Subject: [PATCH] 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 --- .../mochitest/tests/SimpleTest/SimpleTest.js | 73 ++++++------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 692c67c72485..bcd1e206c3b2 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -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) {