Skip to content

Commit

Permalink
fixed asymmetrical equality of cyclic objects (#7730)
Browse files Browse the repository at this point in the history
* fixed assymetrical equality of cyclic objects

* updated changelog

* added more test cases and changed algorithm

* reverted to the old algorith and added additional check

* removed unnecessary check

* added more assertions to check for symmetrical equality
  • Loading branch information
grosto authored and pedrottimark committed Feb 27, 2019
1 parent 3bd384b commit 4023b8c
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
- `[jest-runtime]` Exclude setup/teardown files from coverage report ([#7790](https://github.com/facebook/jest/pull/7790)
- `[babel-jest]` Throw an error if `babel-jest` tries to transform a file ignored by Babel ([#7797](https://github.com/facebook/jest/pull/7797))
- `[babel-plugin-jest-hoist]` Ignore TS type references when looking for out-of-scope references ([#7799](https://github.com/facebook/jest/pull/7799)
- `[expect]` fixed asymmetrical equality of cyclic objects ([#7730](https://github.com/facebook/jest/pull/7730))

### Chore & Maintenance

Expand Down
53 changes: 53 additions & 0 deletions packages/expect/src/__tests__/matchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,59 @@ describe('.toEqual()', () => {
});
expect(actual).toEqual({x: 3});
});

describe('cyclic object equality', () => {
test('properties with the same circularity are equal', () => {
const a = {};
a.x = a;
const b = {};
b.x = b;
expect(a).toEqual(b);
expect(b).toEqual(a);

const c = {};
c.x = a;
const d = {};
d.x = b;
expect(c).toEqual(d);
expect(d).toEqual(c);
});

test('properties with different circularity are not equal', () => {
const a = {};
a.x = {y: a};
const b = {};
const bx = {};
b.x = bx;
bx.y = bx;
expect(a).not.toEqual(b);
expect(b).not.toEqual(a);

const c = {};
c.x = a;
const d = {};
d.x = b;
expect(c).not.toEqual(d);
expect(d).not.toEqual(c);
});

test('are not equal if circularity is not on the same property', () => {
const a = {};
const b = {};
a.a = a;
b.a = {};
b.a.a = a;
expect(a).not.toEqual(b);
expect(b).not.toEqual(a);

const c = {};
c.x = {x: c};
const d = {};
d.x = d;
expect(c).not.toEqual(d);
expect(d).not.toEqual(c);
});
});
});

describe('.toBeInstanceOf()', () => {
Expand Down
17 changes: 10 additions & 7 deletions packages/expect/src/jasmineUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ function asymmetricMatch(a: any, b: any) {
function eq(
a: any,
b: any,
aStack: any,
bStack: any,
customTesters: any,
aStack: Array<unknown>,
bStack: Array<unknown>,
customTesters: Array<Tester>,
hasKey: any,
): boolean {
var result = true;
Expand Down Expand Up @@ -149,14 +149,17 @@ function eq(
return false;
}

// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Used to detect circular references.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) {
return bStack[length] == b;
// circular references at same depth are equal
// circular reference is not equal to non-circular one
if (aStack[length] === a) {
return bStack[length] === b;
} else if (bStack[length] === b) {
return false;
}
}
// Add the first object to the stack of traversed objects.
Expand Down

0 comments on commit 4023b8c

Please sign in to comment.