Bug 866849, part 1 - Add assertEqual testing function in jit-test/lib/asserts.js. r=evilpies.
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 10 Jul 2013 08:14:02 -0500
changeset 137969 98dccff45810620b59a105feeb7a9df048b5b73d
parent 137968 32c80f595de57a107195c604ba7a1e9d4e1523da
child 137970 d0c3168c3c47b8d553da124131a10748bb5df349
push id24939
push userryanvm@gmail.com
push dateWed, 10 Jul 2013 17:49:39 +0000
treeherdermozilla-central@dde4dcd6fa46 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpies
bugs866849
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 866849, part 1 - Add assertEqual testing function in jit-test/lib/asserts.js. r=evilpies.
js/src/jit-test/lib/asserts.js
js/src/jit-test/tests/self-test/assertDeepEq.js
--- a/js/src/jit-test/lib/asserts.js
+++ b/js/src/jit-test/lib/asserts.js
@@ -94,8 +94,139 @@ if (typeof assertNoWarning === 'undefine
                   "with warnings-as-errors enabled");
             throw exc;
         } finally {
             if (!hadWerror)
                 options("werror");
         }
     };
 }
+
+if (typeof assertDeepEq === 'undefined') {
+    let call = Function.prototype.call,
+        Map_ = Map,
+        Error_ = Error,
+        Map_has = call.bind(Map.prototype.has),
+        Map_get = call.bind(Map.prototype.get),
+        Map_set = call.bind(Map.prototype.set),
+        Object_toString = call.bind(Object.prototype.toString),
+        Function_toString = call.bind(Function.prototype.toString),
+        Object_getPrototypeOf = Object.getPrototypeOf,
+        Object_hasOwnProperty = call.bind(Object.prototype.hasOwnProperty),
+        Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
+        Object_isExtensible = Object.isExtensible,
+        Object_getOwnPropertyNames = Object.getOwnPropertyNames,
+        uneval_ = uneval;
+
+    // Return true iff ES6 Type(v) isn't Object.
+    // Note that `typeof document.all === "undefined"`.
+    let isPrimitive = v =>
+        v === null ||
+        v === undefined ||
+        typeof v === "boolean" ||
+        typeof v === "number" ||
+        typeof v === "string" ||
+        typeof v === "symbol";
+
+    let assertSameValue = (a, b, msg) => {
+        try {
+            assertEq(a, b);
+        } catch (exc) {
+            throw new Error(exc.message + (msg ? " " + msg : ""));
+        }
+    };
+
+    let assertSameClass = (a, b, msg) => {
+        var ac = Object_toString(a), bc = Object_toString(b);
+        assertSameValue(ac, bc, msg);
+        switch (ac) {
+        case "[object Function]":
+            assertSameValue(Function_toString(a), Function_toString(b), msg);
+        }
+    };
+
+    let at = (prevmsg, segment) => prevmsg ? prevmsg + segment : "at _" + segment;
+
+    // Assert that the arguments a and b are thoroughly structurally equivalent.
+    //
+    // For the sake of speed, we cut a corner:
+    //        var x = {}, y = {}, ax = [x];
+    //        assertDeepEq([ax, x], [ax, y]);  // passes (?!)
+    //
+    // Technically this should fail, since the two object graphs are different.
+    // (The graph of [ax, y] contains one more object than the graph of [ax, x].)
+    //
+    // To get technically correct behavior, pass {strictEquivalence: true}.
+    // This is slower because we have to walk the entire graph, and Object.prototype
+    // is big.
+    //
+    var assertDeepEq = function assertDeepEq(a, b, options) {
+        let strictEquivalence = options ? options.strictEquivalence : false;
+
+        let assertSameProto = (a, b, msg) => {
+            check(Object_getPrototypeOf(a), Object_getPrototypeOf(b), at(msg, ".__proto__"))
+        };
+
+        let failPropList = (na, nb, msg) => {
+            throw Error_("got own properties " + uneval_(na) + ", expected " + uneval_(nb) +
+                         (msg ? " " + msg : ""));
+        }
+
+        let assertSameProps = (a, b, msg) => {
+            var na = Object_getOwnPropertyNames(a),
+                nb = Object_getOwnPropertyNames(b);
+            if (na.length !== nb.length)
+                failPropList(na, nb, msg);
+            for (var i = 0; i < na.length; i++) {
+                var name = na[i];
+                if (name !== nb[i])
+                    failPropList(na, nb, msg);
+                var da = Object_getOwnPropertyDescriptor(a, name),
+                    db = Object_getOwnPropertyDescriptor(b, name);
+                var pmsg = at(msg, /^[_$A-Za-z0-9]+$/.test(name)
+                                   ? /0|[1-9][0-9]*/.test(name) ? "[" + name + "]" : "." + name
+                                   : "[" + uneval_(name) + "]");
+                assertSameValue(da.configurable, db.configurable, at(pmsg, ".[[Configurable]]"));
+                assertSameValue(da.enumerable, db.enumerable, at(pmsg, ".[[Enumerable]]"));
+                if (Object_hasOwnProperty(da, "value")) {
+                    if (!Object_hasOwnProperty(db, "value"))
+                        throw Error_("got data property, expected accessor property" + pmsg);
+                    check(da.value, db.value, pmsg);
+                } else {
+                    if (Object_hasOwnProperty(db, "value"))
+                        throw Error_("got accessor property, expected data property" + pmsg);
+                    check(da.get, db.get, at(pmsg, ".[[Get]]"));
+                    check(da.set, db.set, at(pmsg, ".[[Set]]"));
+                }
+            }
+        };
+
+        var ab = Map_();
+        var bpath = Map_();
+
+        let check = (a, b, path) => {
+            if (isPrimitive(a)) {
+                assertSameValue(a, b, path);
+            } else if (isPrimitive(b)) {
+                throw Error_("got " + Object_toString(a) + ", expected " + uneval_(b) + " " + path);
+            } else if (Map_has(ab, a)) {
+                assertSameValue(Map_get(ab, a), b, path);
+            } else if (Map_has(bpath, b)) {
+                var bPrevPath = Map_get(bpath, b) || "_";
+                throw Error_("got distinct objects " + at(path, "") + " and " + at(bPrevPath, "") +
+                             ", expected the same object both places");
+            } else {
+                Map_set(ab, a, b);
+                Map_set(bpath, b, path);
+                if (a !== b || strictEquivalence) {
+                    assertSameClass(a, b, path);
+                    assertSameProto(a, b, path);
+                    assertSameProps(a, b, path);
+                    assertSameValue(Object_isExtensible(a),
+                                    Object_isExtensible(b),
+                                    at(path, ".[[Extensible]]"));
+                }
+            }
+        }
+
+        check(a, b, "");
+    };
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/self-test/assertDeepEq.js
@@ -0,0 +1,93 @@
+// Tests for the assertEqual function in jit-test/lib/asserts.js
+
+load(libdir + "asserts.js");
+
+function assertNotDeepEq(a, b, options) {
+    assertThrowsInstanceOf(() => assertDeepEq(a, b, options), Error);
+}
+
+// primitives
+assertDeepEq(undefined, undefined);
+assertDeepEq("1", "1");
+assertNotDeepEq(1, "1");
+assertNotDeepEq(undefined, null);
+assertNotDeepEq({}, null);
+
+// objects
+assertDeepEq({}, {});
+assertDeepEq({one: 1, two: 2}, {one: 1, two: 2});
+assertNotDeepEq(Object.freeze({}), {});
+assertDeepEq(Object.create(null), Object.create(null));
+assertNotDeepEq(Object.create(null, {a: {configurable: false, value: 3}}),
+               Object.create(null, {a: {configurable: true, value: 3}}));
+assertNotDeepEq({one: 1}, {one: 1, two: 2});
+assertNotDeepEq({yes: true}, {oui: true});
+assertNotDeepEq({zero: 0}, {zero: "0"});
+
+// test the comment
+var x = {}, y = {}, ax = [x];
+assertDeepEq([ax, x], [ax, y]);  // passes (bogusly)
+assertNotDeepEq([ax, x], [ax, y], {strictEquivalence: true});
+assertDeepEq([x, ax], [y, ax]);  // passes (bogusly)
+assertNotDeepEq([x, ax], [y, ax], {strictEquivalence: true});
+
+// object identity
+assertNotDeepEq([x, y], [x, x]);
+assertDeepEq([x, y], [x, y]);
+assertDeepEq([y, x], [x, y]);
+
+// proto chain
+var x = {};
+assertDeepEq(Object.create(x), Object.create(x));
+assertDeepEq(Object.create({}), Object.create({})); // equivalent but not identical proto objects
+
+// arrays
+assertDeepEq([], []);
+assertNotDeepEq([], [1]);
+assertDeepEq([1], [1]);
+assertNotDeepEq([0], [1]);
+assertDeepEq([1, 2, 3], [1, 2, 3]);
+assertNotDeepEq([1, , 3], [1, undefined, 3]);
+var p = [], q = [];
+p.prop = 1;
+assertNotDeepEq(p, q);
+assertNotDeepEq(q, p);
+q.prop = 1;
+assertDeepEq(q, p);
+
+// functions
+assertNotDeepEq(() => 1, () => 2);
+assertNotDeepEq((...x) => 1, x => 1);
+assertNotDeepEq(function f(){}, function g(){});
+var f1 = function () {}, f2 = function () {};
+assertDeepEq(f1, f1);
+assertDeepEq(f1, f2);  // same text, close enough
+f1.prop = 1;
+assertNotDeepEq(f1, f2);
+f2.prop = 1;
+assertDeepEq(f1, f2);
+
+// recursion
+var a = [], b = [];
+a[0] = a;
+b[0] = b;
+assertDeepEq(a, b);
+a[0] = b;
+assertNotDeepEq(a, b);  // [#1=[#1#]] is not structurally equivalent to #1=[[#1#]]
+b[0] = a;
+assertDeepEq(a, b);
+b[0] = [a];  // a[0] === b, b[0] === c, c[0] === a
+assertDeepEq(a, b);
+
+// objects that merge
+var x = {};
+assertDeepEq({x: x}, {x: x});
+var y = [x];
+assertDeepEq([y], [y]);
+
+// cross-compartment
+var g1 = newGlobal(), g2 = newGlobal();
+assertDeepEq(g1, g2);
+assertDeepEq(g1, g2, {strictEquivalence: true});
+Object.preventExtensions(g2.Math.abs);  // make some miniscule change
+assertNotDeepEq(g1, g2);