From 4183bf446c195d69405e181bac270760e984287d Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 6 May 2016 12:36:49 +0900 Subject: [PATCH] feat(assert): Allow circular references assert.deepEqual() and assert.deepStrictEqual() will no longer throw a RangeError if passed objects with circular references. fix: #3 refs:https://github.com/nodejs/node/issues/6416 https://github.com/nodejs/node/pull/6432 --- index.js | 20 ++++++++++++++++---- test/test-deep-equal.js | 22 ++++++++++++++++++++++ test/test-deep-strict-equal.js | 21 +++++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index ca3d43e..a4838a4 100644 --- a/index.js +++ b/index.js @@ -113,7 +113,7 @@ var objectKeys = (function () { }; })(); -function _deepEqual(actual, expected, strict) { +function _deepEqual(actual, expected, strict, memos) { // 7.1. All identical values are equivalent, as determined by ===. if (actual === expected) { return true; @@ -162,11 +162,22 @@ function _deepEqual(actual, expected, strict) { // corresponding key, and an identical 'prototype' property. Note: this // accounts for both named and indexed properties on Arrays. } else { - return objEquiv(actual, expected, strict); + memos = memos || {actual: [], expected: []}; + var actualIndex = memos.actual.indexOf(actual); + if (actualIndex !== -1) { + if (actualIndex === memos.expected.indexOf(expected)) { + return true; + } + } + + memos.actual.push(actual); + memos.expected.push(expected); + + return objEquiv(actual, expected, strict, memos) } } -function objEquiv(a, b, strict) { +function objEquiv(a, b, strict, actualVisitedObjects) { if (a === null || a === undefined || b === null || b === undefined) return false; // if one is a primitive, the other must be same @@ -202,7 +213,8 @@ function objEquiv(a, b, strict) { //~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; - if (!_deepEqual(a[key], b[key], strict)) return false; + if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) + return false; } return true; } diff --git a/test/test-deep-equal.js b/test/test-deep-equal.js index a3f62e5..d505b14 100644 --- a/test/test-deep-equal.js +++ b/test/test-deep-equal.js @@ -118,6 +118,28 @@ assert.throws(makeBlock(deepEqual, true, {}), assert.AssertionError); if (typeof Symbol !== 'undefined') { assert.throws(makeBlock(deepEqual, Symbol(), {}), assert.AssertionError); } +}); +// https://github.com/nodejs/node/issues/6416 +it("Make sure circular refs don't throw", function(){ +var b = {}; +b.b = b; + +var c = {}; +c.b = c; + +assert.doesNotThrow(makeBlock(deepEqual, b, c)); +assert.doesNotThrow(makeBlock(deepEqual, b, c)); + +var d = {}; +d.a = 1; +d.b = d; + +var e = {}; +e.a = 1; +e.b = e.a; + +assert.throws(makeBlock(deepEqual, d, e), /AssertionError/); +assert.throws(makeBlock(deepEqual, d, e), /AssertionError/); }); describe('primitive wrappers and object', function () { diff --git a/test/test-deep-strict-equal.js b/test/test-deep-strict-equal.js index 257080e..3105730 100644 --- a/test/test-deep-strict-equal.js +++ b/test/test-deep-strict-equal.js @@ -159,3 +159,24 @@ assert.throws(makeBlock(deepStrictEqual, new Boolean(true), {}), }); }); + +// https://github.com/nodejs/node/issues/6416 +it("Make sure circular refs don't throw", function(){ +var b = {}; +b.b = b; + +var c = {}; +c.b = c; + +assert.doesNotThrow(makeBlock(deepStrictEqual, b, c)); + +var d = {}; +d.a = 1; +d.b = d; + +var e = {}; +e.a = 1; +e.b = e.a; + +assert.throws(makeBlock(deepStrictEqual, d, e), /AssertionError/); +}); \ No newline at end of file