From c5fa5b0629c0ed778662276b936136f02d5eb6ec Mon Sep 17 00:00:00 2001 From: John Mellor Date: Thu, 27 Aug 2015 16:01:59 +0100 Subject: [PATCH] Make assert_object_equals more accurate and give better error messages Consider the following tests: test(function() { assert_object_equals(false, true); }, "1"); test(function() { assert_object_equals(1, true); }, "2"); test(function() { assert_object_equals(false, {}); }, "3"); test(function() { assert_object_equals({}, false); }, "4"); test(function() { assert_object_equals("abc", ["a", "b", "c"]); }, "5"); test(function() { assert_object_equals(["a", "b", "c"], "abc"); }, "6"); test(function() { assert_object_equals({a:{}}, {a:false}); }, "7"); test(function() { assert_object_equals({b:{}}, {b:null}); }, "8"); test(function() { assert_object_equals({c:null}, {c:{}}); }, "9"); test(function() { assert_object_equals({d:false}, {d:{}}); }, "10"); test(function() { assert_object_equals({x:{y:{z:false}}}, {x:{y:{z:true}}}); }, "11"); Previously, the first 8 would incorrectly pass, and the last 3 would give confusing error messages: FAIL 9 assert_object_equals: property "c" expected object "[object Object]" got object "[object Object]" FAIL 10 assert_object_equals: property "d" expected object "[object Object]" got object "[object Object]" FAIL 11 assert_object_equals: property "z" expected object "[object Object]" got object "[object Object]" After this patch, all the cases fail correctly and have clear error messages: FAIL 1 assert_object_equals: expected true but got false FAIL 2 assert_object_equals: expected (boolean) true but got (number) 1 FAIL 3 assert_object_equals: expected (object) object "[object Object]" but got (boolean) false FAIL 4 assert_object_equals: expected (boolean) false but got (object) object "[object Object]" FAIL 5 assert_object_equals: expected (object) ["a", "b", "c"] but got (string) "abc" FAIL 6 assert_object_equals: expected (string) "abc" but got (object) ["a", "b", "c"] FAIL 7 assert_object_equals: property ["a"]: expected (boolean) false but got (object) object "[object Object]" FAIL 8 assert_object_equals: property ["b"]: expected null but got object "[object Object]" FAIL 9 assert_object_equals: property ["c"]: expected object "[object Object]" but got null FAIL 10 assert_object_equals: property ["d"]: expected (object) object "[object Object]" but got (boolean) false FAIL 11 assert_object_equals: property ["x"]["y"]["z"]: expected true but got false --- testharness.js | 69 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/testharness.js b/testharness.js index f4c66aa..5e269a0 100644 --- a/testharness.js +++ b/testharness.js @@ -893,34 +893,47 @@ policies and contribution forms [3]. function assert_object_equals(actual, expected, description) { - //This needs to be improved a great deal - function check_equal(actual, expected, stack) - { - stack.push(actual); - - var p; - for (p in actual) { - assert(expected.hasOwnProperty(p), "assert_object_equals", description, - "unexpected property ${p}", {p:p}); - - if (typeof actual[p] === "object" && actual[p] !== null) { - if (stack.indexOf(actual[p]) === -1) { - check_equal(actual[p], expected[p], stack); - } - } else { - assert(same_value(actual[p], expected[p]), "assert_object_equals", description, - "property ${p} expected ${expected} got ${actual}", - {p:p, expected:expected, actual:actual}); - } - } - for (p in expected) { - assert(actual.hasOwnProperty(p), - "assert_object_equals", description, - "expected property ${p} missing", {p:p}); - } - stack.pop(); - } - check_equal(actual, expected, []); + /* + * Recursively test if two objects have equal properties. Does not + * require the objects or their descendents to be the same object. + * May not be fully robust. + */ + function check_equal(actual, expected, stack, path) + { + var pathStr = path.length ? "property [" + path.join("][") + "]: " : ""; + + if (typeof actual !== typeof expected) { + assert(false, "assert_object_equals", description, + pathStr + "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}", + {expected:expected, actual:actual}); + return; + } + if (typeof expected !== "object" || expected === null || actual === null) { + assert(same_value(actual, expected), "assert_object_equals", description, + pathStr + "expected ${expected} but got ${actual}", + {expected:expected, actual:actual}); + return; + } + + stack.push(actual); + for (var p in actual) { + assert(expected.hasOwnProperty(p), "assert_object_equals", description, + pathStr + "unexpected property ${p}", {p:p}); + + if (typeof actual[p] === "object" && actual[p] !== null && stack.indexOf(actual[p]) === -1) + continue; + path.push(JSON.stringify(p)); + check_equal(actual[p], expected[p], stack, path); + path.pop(); + } + for (var p in expected) { + assert(actual.hasOwnProperty(p), + "assert_object_equals", description, + pathStr + "expected property ${p} missing", {p:p}); + } + stack.pop(); + } + check_equal(actual, expected, [], []); } expose(assert_object_equals, "assert_object_equals");