michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: if (typeof assertThrowsInstanceOf === 'undefined') { michael@0: var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) { michael@0: var fullmsg; michael@0: try { michael@0: f(); michael@0: } catch (exc) { michael@0: if (exc instanceof ctor) michael@0: return; michael@0: fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc; michael@0: } michael@0: if (fullmsg === undefined) michael@0: fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown"; michael@0: if (msg !== undefined) michael@0: fullmsg += " - " + msg; michael@0: throw new Error(fullmsg); michael@0: }; michael@0: } michael@0: michael@0: if (typeof assertThrowsValue === 'undefined') { michael@0: var assertThrowsValue = function assertThrowsValue(f, val, msg) { michael@0: var fullmsg; michael@0: try { michael@0: f(); michael@0: } catch (exc) { michael@0: if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val)) michael@0: return; michael@0: fullmsg = "Assertion failed: expected exception " + val + ", got " + exc; michael@0: } michael@0: if (fullmsg === undefined) michael@0: fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown"; michael@0: if (msg !== undefined) michael@0: fullmsg += " - " + msg; michael@0: throw new Error(fullmsg); michael@0: }; michael@0: } michael@0: michael@0: if (typeof assertDeepEq === 'undefined') { michael@0: var assertDeepEq = (function(){ michael@0: var call = Function.prototype.call, michael@0: Map_ = Map, michael@0: Error_ = Error, michael@0: Map_has = call.bind(Map.prototype.has), michael@0: Map_get = call.bind(Map.prototype.get), michael@0: Map_set = call.bind(Map.prototype.set), michael@0: Object_toString = call.bind(Object.prototype.toString), michael@0: Function_toString = call.bind(Function.prototype.toString), michael@0: Object_getPrototypeOf = Object.getPrototypeOf, michael@0: Object_hasOwnProperty = call.bind(Object.prototype.hasOwnProperty), michael@0: Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, michael@0: Object_isExtensible = Object.isExtensible, michael@0: Object_getOwnPropertyNames = Object.getOwnPropertyNames, michael@0: uneval_ = uneval; michael@0: michael@0: // Return true iff ES6 Type(v) isn't Object. michael@0: // Note that `typeof document.all === "undefined"`. michael@0: function isPrimitive(v) { michael@0: return (v === null || michael@0: v === undefined || michael@0: typeof v === "boolean" || michael@0: typeof v === "number" || michael@0: typeof v === "string" || michael@0: typeof v === "symbol"); michael@0: } michael@0: michael@0: function assertSameValue(a, b, msg) { michael@0: try { michael@0: assertEq(a, b); michael@0: } catch (exc) { michael@0: throw new Error(exc.message + (msg ? " " + msg : "")); michael@0: } michael@0: } michael@0: michael@0: function assertSameClass(a, b, msg) { michael@0: var ac = Object_toString(a), bc = Object_toString(b); michael@0: assertSameValue(ac, bc, msg); michael@0: switch (ac) { michael@0: case "[object Function]": michael@0: assertSameValue(Function_toString(a), Function_toString(b), msg); michael@0: } michael@0: } michael@0: michael@0: function at(prevmsg, segment) { michael@0: return prevmsg ? prevmsg + segment : "at _" + segment; michael@0: } michael@0: michael@0: // Assert that the arguments a and b are thoroughly structurally equivalent. michael@0: // michael@0: // For the sake of speed, we cut a corner: michael@0: // var x = {}, y = {}, ax = [x]; michael@0: // assertDeepEq([ax, x], [ax, y]); // passes (?!) michael@0: // michael@0: // Technically this should fail, since the two object graphs are different. michael@0: // (The graph of [ax, y] contains one more object than the graph of [ax, x].) michael@0: // michael@0: // To get technically correct behavior, pass {strictEquivalence: true}. michael@0: // This is slower because we have to walk the entire graph, and Object.prototype michael@0: // is big. michael@0: // michael@0: return function assertDeepEq(a, b, options) { michael@0: var strictEquivalence = options ? options.strictEquivalence : false; michael@0: michael@0: function assertSameProto(a, b, msg) { michael@0: check(Object_getPrototypeOf(a), Object_getPrototypeOf(b), at(msg, ".__proto__")); michael@0: } michael@0: michael@0: function failPropList(na, nb, msg) { michael@0: throw Error_("got own properties " + uneval_(na) + ", expected " + uneval_(nb) + michael@0: (msg ? " " + msg : "")); michael@0: } michael@0: michael@0: function assertSameProps(a, b, msg) { michael@0: var na = Object_getOwnPropertyNames(a), michael@0: nb = Object_getOwnPropertyNames(b); michael@0: if (na.length !== nb.length) michael@0: failPropList(na, nb, msg); michael@0: for (var i = 0; i < na.length; i++) { michael@0: var name = na[i]; michael@0: if (name !== nb[i]) michael@0: failPropList(na, nb, msg); michael@0: var da = Object_getOwnPropertyDescriptor(a, name), michael@0: db = Object_getOwnPropertyDescriptor(b, name); michael@0: var pmsg = at(msg, /^[_$A-Za-z0-9]+$/.test(name) michael@0: ? /0|[1-9][0-9]*/.test(name) ? "[" + name + "]" : "." + name michael@0: : "[" + uneval_(name) + "]"); michael@0: assertSameValue(da.configurable, db.configurable, at(pmsg, ".[[Configurable]]")); michael@0: assertSameValue(da.enumerable, db.enumerable, at(pmsg, ".[[Enumerable]]")); michael@0: if (Object_hasOwnProperty(da, "value")) { michael@0: if (!Object_hasOwnProperty(db, "value")) michael@0: throw Error_("got data property, expected accessor property" + pmsg); michael@0: check(da.value, db.value, pmsg); michael@0: } else { michael@0: if (Object_hasOwnProperty(db, "value")) michael@0: throw Error_("got accessor property, expected data property" + pmsg); michael@0: check(da.get, db.get, at(pmsg, ".[[Get]]")); michael@0: check(da.set, db.set, at(pmsg, ".[[Set]]")); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: var ab = Map_(); michael@0: var bpath = Map_(); michael@0: michael@0: function check(a, b, path) { michael@0: if (isPrimitive(a)) { michael@0: assertSameValue(a, b, path); michael@0: } else if (isPrimitive(b)) { michael@0: throw Error_("got " + Object_toString(a) + ", expected " + uneval_(b) + " " + path); michael@0: } else if (Map_has(ab, a)) { michael@0: assertSameValue(Map_get(ab, a), b, path); michael@0: } else if (Map_has(bpath, b)) { michael@0: var bPrevPath = Map_get(bpath, b) || "_"; michael@0: throw Error_("got distinct objects " + at(path, "") + " and " + at(bPrevPath, "") + michael@0: ", expected the same object both places"); michael@0: } else { michael@0: Map_set(ab, a, b); michael@0: Map_set(bpath, b, path); michael@0: if (a !== b || strictEquivalence) { michael@0: assertSameClass(a, b, path); michael@0: assertSameProto(a, b, path); michael@0: assertSameProps(a, b, path); michael@0: assertSameValue(Object_isExtensible(a), michael@0: Object_isExtensible(b), michael@0: at(path, ".[[Extensible]]")); michael@0: } michael@0: } michael@0: } michael@0: michael@0: check(a, b, ""); michael@0: }; michael@0: })(); michael@0: }