├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── package.json ├── test ├── buffer-native-equals.js ├── buffer-buffertools.js ├── buffer-pure-js.js └── basic.js ├── LICENSE ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nyc_output/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "es5": true, 3 | "laxcomma": true, 4 | "eqeqeq": true, 5 | "forin": true, 6 | "immed": true, 7 | "latedef": true, 8 | "newcap": true, 9 | "nonew": true, 10 | "node": true 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - '0.8' 5 | - '0.10' 6 | - '0.12' 7 | - 'iojs-2.5.0' 8 | before_install: 9 | - npm install -g npm@latest 10 | - npm install -g pangyp 11 | - npm config set node-gyp pangyp 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v2.0.0 (2015-08-28): 2 | ## BREAKING CHANGES 3 | - Use a newer version of tap. 4 | - Removed optional dependency on `buffertools` to make deeper easier to bundle. It'll still use `buffertools` if it's installed, but this way won't spook the horses. 5 | - Removed monkeypatching shims. They're out of date and probably not worth bringing up to date. They belong in a separate library anyway. 6 | - Convert the code base to use `standard`. 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deeper", 3 | "version": "2.1.0", 4 | "description": "JavaScript \"deep equality\" / structural equality tester with Node.js flavor.", 5 | "scripts": { 6 | "test": "standard && tap test/*.js --coverage" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/othiym23/node-deeper.git" 11 | }, 12 | "keywords": [ 13 | "deepEqual", 14 | "structural", 15 | "equality", 16 | "assert", 17 | "testing", 18 | "tap" 19 | ], 20 | "author": "Forrest L Norvell (https://github.com/othiym23)", 21 | "license": "BSD-2-Clause", 22 | "devDependencies": { 23 | "buffertools": "^2.1.3", 24 | "standard": "^5.1.1", 25 | "tap": "^1.3.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/buffer-native-equals.js: -------------------------------------------------------------------------------- 1 | var tap = require('tap') 2 | var test = tap.test 3 | var same = require('../') 4 | var fastEquals = require('buffertools').equals 5 | 6 | if (!Buffer.prototype.equals) { 7 | Buffer.prototype.equals = fastEquals 8 | } 9 | 10 | test('should match empty Buffers', function (t) { 11 | t.ok(same(new Buffer([]), new Buffer([]))) 12 | t.end() 13 | }) 14 | 15 | test('should match similar Buffers', function (t) { 16 | var b1 = new Buffer([0]) 17 | var b2 = new Buffer([0]) 18 | t.ok(same(b1, b2)) 19 | 20 | var b3 = new Buffer([0, 1, 3]) 21 | var b4 = new Buffer([0, 1, 3]) 22 | t.ok(same(b3, b4)) 23 | 24 | t.end() 25 | }) 26 | 27 | test('should notice different Buffers', function (t) { 28 | var b1 = new Buffer([0, 1, 2]) 29 | var b2 = new Buffer([0, 1, 23]) 30 | t.notOk(same(b1, b2)) 31 | 32 | var shortb = new Buffer([0, 1]) 33 | var longb = new Buffer(320) 34 | for (var i = 0; i < 160; i++) longb.writeUInt16LE(i, i * 2) 35 | t.notOk(same( 36 | { x: { y: { z: shortb } } }, 37 | { x: { y: { z: longb } } } 38 | )) 39 | t.end() 40 | }) 41 | -------------------------------------------------------------------------------- /test/buffer-buffertools.js: -------------------------------------------------------------------------------- 1 | var tap = require('tap') 2 | var test = tap.test 3 | var same = require('../') 4 | 5 | test('should match empty Buffers', function (t) { 6 | t.ok(same(new Buffer([]), new Buffer([]))) 7 | t.end() 8 | }) 9 | 10 | // remove equals off the list if it's there 11 | test('should match similar Buffers', function (t) { 12 | var b1 = new Buffer([0]) 13 | b1.equals = null 14 | var b2 = new Buffer([0]) 15 | b2.equals = null 16 | t.ok(same(b1, b2)) 17 | 18 | var b3 = new Buffer([0, 1, 3]) 19 | b3.equals = null 20 | var b4 = new Buffer([0, 1, 3]) 21 | b4.equals = null 22 | t.ok(same(b3, b4)) 23 | 24 | t.end() 25 | }) 26 | 27 | test('should notice different Buffers', function (t) { 28 | var b1 = new Buffer([0, 1, 2]) 29 | b1.equals = null 30 | var b2 = new Buffer([0, 1, 23]) 31 | b2.equals = null 32 | t.notOk(same(b1, b2)) 33 | 34 | var shortb = new Buffer([0, 1]) 35 | shortb.equals = null 36 | var longb = new Buffer(320) 37 | longb.equals = null 38 | for (var i = 0; i < 160; i++) longb.writeUInt16LE(i, i * 2) 39 | t.notOk(same( 40 | { x: { y: { z: shortb } } }, 41 | { x: { y: { z: longb } } } 42 | )) 43 | t.end() 44 | }) 45 | -------------------------------------------------------------------------------- /test/buffer-pure-js.js: -------------------------------------------------------------------------------- 1 | var tap = require('tap') 2 | var test = tap.test 3 | var same = require('../') 4 | 5 | // force it to use the pure JS version 6 | delete same.fastEqual 7 | 8 | test('should match empty Buffers', function (t) { 9 | t.ok(same(new Buffer([]), new Buffer([]))) 10 | t.end() 11 | }) 12 | 13 | test('should match similar Buffers', function (t) { 14 | var b1 = new Buffer([0]) 15 | b1.equals = null 16 | var b2 = new Buffer([0]) 17 | b2.equals = null 18 | t.ok(same(b1, b2)) 19 | 20 | var b3 = new Buffer([0, 1, 3]) 21 | b3.equals = null 22 | var b4 = new Buffer([0, 1, 3]) 23 | b4.equals = null 24 | t.ok(same(b3, b4)) 25 | 26 | t.end() 27 | }) 28 | 29 | test('should notice different Buffers', function (t) { 30 | var b1 = new Buffer([0, 1, 2]) 31 | b1.equals = null 32 | var b2 = new Buffer([0, 1, 23]) 33 | b2.equals = null 34 | t.notOk(same(b1, b2)) 35 | 36 | var shortb = new Buffer([0, 1]) 37 | shortb.equals = null 38 | var longb = new Buffer(320) 39 | longb.equals = null 40 | for (var i = 0; i < 160; i++) longb.writeUInt16LE(i, i * 2) 41 | t.notOk(same( 42 | { x: { y: { z: shortb } } }, 43 | { x: { y: { z: longb } } } 44 | )) 45 | t.end() 46 | }) 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Forrest L Norvell 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. Redistributions in binary 9 | form must reproduce the above copyright notice, this list of conditions and the 10 | following disclaimer in the documentation and/or other materials provided with 11 | the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![deeper on npm](https://img.shields.io/npm/v/deeper.svg?style=flat)](http://npm.im/deeper) 2 | [![Build Status](https://travis-ci.org/othiym23/node-deeper.svg?branch=master)](https://travis-ci.org/othiym23/node-deeper) 3 | [![Coverage Status](https://coveralls.io/repos/othiym23/node-deeper/badge.svg?branch=master&service=github)](https://coveralls.io/github/othiym23/node-deeper?branch=master) 4 | [!["standard" style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 5 | 6 | 7 | # deeper 8 | 9 | `deeper` is a library for structurally comparing the equality of JavaScript 10 | values. It supports recursive / cyclical data structures, is written to avoid 11 | try / catch / throw (for speed), and has no dependencies by default. 12 | 13 | If you're running Node 0.12+ or io.js, `deeper` will use the built-in 14 | `Buffer.equals()`. If you're running an older version of Node and you install 15 | [Ben Noordhuis](http://github.com/bnoordhuis)'s 16 | [buffertools](https://github.com/bnoordhuis/node-buffertools) into a project 17 | using `deeper`, it will use that to speed up comparison of Buffers. This used 18 | to be installed as an optional dependency, but it gets in the way of 19 | browserification and also makes using `deeper` in your own projects harder, so 20 | I changed it to just try to use it if it's there. 21 | 22 | It has some optimizations, but stresses correctness over raw speed (unless 23 | you're testing objects with lots of Buffers attached to them, in which case it 24 | plus `buffertools` is likely to be the fastest general-purpose deep-comparison 25 | tool available). 26 | 27 | The core algorithm is based on those used by Node's assertion library and the 28 | implementation of cycle detection in 29 | [isEqual](http://underscorejs.org/#isEqual) in 30 | [Underscore.js](http://underscorejs.org/). 31 | 32 | I like to think the documentation is pretty OK. 33 | 34 | ## installation 35 | 36 | ``` 37 | npm install deeper 38 | ``` 39 | 40 | ## usage 41 | 42 | ```javascript 43 | // vanilla 44 | var deepEqual = require('deeper') 45 | 46 | if (!deepEqual(obj1, obj2)) console.log("yay! diversity!"); 47 | ``` 48 | 49 | ## details 50 | 51 | Copied from the source, here are the details of `deeper`'s algorithm: 52 | 53 | 1. `===` only tests objects and functions by reference. `null` is an object. 54 | Any pairs of identical entities failing this test are therefore objects 55 | (including `null`), which need to be recursed into and compared attribute by 56 | attribute. 57 | 2. Since the only entities to get to this test must be objects, if `a` or `b` 58 | is not an object, they're clearly not the same. All unfiltered `a` and `b` 59 | getting past this are objects (including `null`). 60 | 3. `null` is an object, but `null === null.` All unfiltered `a` and `b` are 61 | non-null `Objects`. 62 | 4. Buffers need to be special-cased because they live partially on the wrong 63 | side of the C++ / JavaScript barrier. Still, calling this on structures 64 | that can contain Buffers is a bad idea, because they can contain 65 | multiple megabytes of data and comparing them byte-by-byte is hella 66 | expensive. 67 | 5. It's much faster to compare dates by numeric value (`.getTime()`) than by 68 | lexical value. 69 | 6. Compare `RegExps` by their components, not the objects themselves. 70 | 7. Treat argumens objects like arrays. The parts of an arguments list most 71 | people care about are the arguments themselves, not `callee`, which you 72 | shouldn't be looking at anyway. 73 | 8. Objects are more complex: 74 | 1. Ensure that `a` and `b` are on the same constructor chain. 75 | 2. Ensure that `a` and `b` have the same number of own properties (which is 76 | what `Object.keys()` returns). 77 | 3. Ensure that cyclical references don't blow up the stack. 78 | 4. Ensure that all the key names match (faster). 79 | 5. Ensure that all of the associated values match, recursively (slower). 80 | 81 | ### (somewhat untested) assumptions: 82 | 83 | - Functions are only considered identical if they unify to the same reference. 84 | To anything else is to invite the wrath of the halting problem. 85 | - V8 is smart enough to optimize treating an Array like any other kind of 86 | object. 87 | - Users of this function are cool with mutually recursive data structures that 88 | are otherwise identical being treated as the same. 89 | 90 | ## license 91 | BSD. Go nuts. 92 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function isArguments (object) { 4 | return Object.prototype.toString.call(object) === '[object Arguments]' 5 | } 6 | 7 | function deeper (a, b) { 8 | return deeper_(a, b, [], []) 9 | } 10 | 11 | module.exports = deeper 12 | 13 | try { 14 | deeper.fastEqual = require('buffertools').equals 15 | } catch (e) { 16 | // whoops, nobody told buffertools it wasn't installed 17 | } 18 | 19 | /** 20 | * This is a Node-specific version of a structural equality test, modeled on 21 | * bits and pieces of loads of other implementations of this algorithm, most 22 | * notably the one in the Node.js source and the Underscore library. It doesn't 23 | * throw and handles cycles. 24 | * 25 | * Everybody who writes one of these functions puts the documentation 26 | * inline, which makes it incredibly hard to follow. Here's what this version 27 | * of the algorithm does, in order: 28 | * 29 | * 1. `===` only tests objects and functions by reference. `null` is an object. 30 | * Any pairs of identical entities failing this test are therefore objects 31 | * (including `null`), which need to be recursed into and compared attribute by 32 | * attribute. 33 | * 2. Since the only entities to get to this test must be objects, if `a` or `b` 34 | * is not an object, they're clearly not the same. All unfiltered `a` and `b` 35 | * getting past this are objects (including `null`). 36 | * 3. `null` is an object, but `null === null.` All unfiltered `a` and `b` are 37 | * non-null `Objects`. 38 | * 4. Buffers need to be special-cased because they live partially on the wrong 39 | * side of the C++ / JavaScript barrier. Still, calling this on structures 40 | * that can contain Buffers is a bad idea, because they can contain 41 | * multiple megabytes of data and comparing them byte-by-byte is hella 42 | * expensive. 43 | * 5. It's much faster to compare dates by numeric value (`.getTime()`) than by 44 | * lexical value. 45 | * 6. Compare `RegExps` by their components, not the objects themselves. 46 | * 7. Treat argumens objects like arrays. The parts of an arguments list most 47 | * people care about are the arguments themselves, not `callee`, which you 48 | * shouldn't be looking at anyway. 49 | * 8. Objects are more complex: 50 | * 1. Ensure that `a` and `b` are on the same constructor chain. 51 | * 2. Ensure that `a` and `b` have the same number of own properties (which is 52 | * what `Object.keys()` returns). 53 | * 3. Ensure that cyclical references don't blow up the stack. 54 | * 4. Ensure that all the key names match (faster). 55 | * 5. Ensure that all of the associated values match, recursively (slower). 56 | * 57 | * (somewhat untested) assumptions: 58 | * 59 | * - Functions are only considered identical if they unify to the same 60 | * reference. To anything else is to invite the wrath of the halting problem. 61 | * - V8 is smart enough to optimize treating an Array like any other kind of 62 | * object. 63 | * - Users of this function are cool with mutually recursive data structures 64 | * that are otherwise identical being treated as the same. 65 | */ 66 | function deeper_ (a, b, ca, cb) { 67 | if (a === b) { 68 | return true 69 | } else if (typeof a !== 'object' || typeof b !== 'object') { 70 | return false 71 | } else if (a === null || b === null) { 72 | return false 73 | } else if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) { 74 | if (a.equals) { 75 | return a.equals(b) 76 | } else if (deeper.fastEqual) { 77 | return deeper.fastEqual.call(a, b) 78 | } else { 79 | if (a.length !== b.length) return false 80 | 81 | for (var i = 0; i < a.length; i++) if (a[i] !== b[i]) return false 82 | 83 | return true 84 | } 85 | } else if (a instanceof Date && b instanceof Date) { 86 | return a.getTime() === b.getTime() 87 | } else if (a instanceof RegExp && b instanceof RegExp) { 88 | return a.source === b.source && 89 | a.global === b.global && 90 | a.multiline === b.multiline && 91 | a.lastIndex === b.lastIndex && 92 | a.ignoreCase === b.ignoreCase 93 | } else if (isArguments(a) || isArguments(b)) { 94 | if (!(isArguments(a) && isArguments(b))) return false 95 | 96 | var slice = Array.prototype.slice 97 | return deeper_(slice.call(a), slice.call(b), ca, cb) 98 | } else { 99 | if (a.constructor !== b.constructor) return false 100 | 101 | var ka = Object.keys(a) 102 | var kb = Object.keys(b) 103 | // don't bother with stack acrobatics if there's nothing there 104 | if (ka.length === 0 && kb.length === 0) return true 105 | if (ka.length !== kb.length) return false 106 | 107 | var cal = ca.length 108 | while (cal--) if (ca[cal] === a) return cb[cal] === b 109 | ca.push(a); cb.push(b) 110 | 111 | ka.sort(); kb.sort() 112 | for (var j = ka.length - 1; j >= 0; j--) if (ka[j] !== kb[j]) return false 113 | 114 | var key 115 | for (var k = ka.length - 1; k >= 0; k--) { 116 | key = ka[k] 117 | if (!deeper_(a[key], b[key], ca, cb)) return false 118 | } 119 | 120 | ca.pop(); cb.pop() 121 | 122 | return true 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var EventEmitter = require('events').EventEmitter 4 | var test = require('tap').test 5 | var d = require('../') 6 | 7 | function functionA (a) { return a } 8 | var heinous = { 9 | nothin: null, 10 | nope: undefined, 11 | number: 0, 12 | funky: functionA, 13 | stringer: 'heya', 14 | then: new Date('1981-03-30'), 15 | rexpy: /^(pi|π)$/, 16 | granular: { 17 | stuff: [0, 1, 2] 18 | } 19 | } 20 | heinous.granular.self = heinous 21 | 22 | var awful = { 23 | nothin: null, 24 | nope: undefined, 25 | number: 0, 26 | funky: functionA, 27 | stringer: 'heya', 28 | then: new Date('1981-03-30'), 29 | rexpy: /^(pi|π)$/, 30 | granular: { 31 | stuff: [0, 1, 2] 32 | } 33 | } 34 | awful.granular.self = awful 35 | 36 | test('deeper handles all the edge cases', function (t) { 37 | /* 38 | * 39 | * SUCCESS 40 | * 41 | */ 42 | 43 | var functionB = functionA 44 | 45 | // 1. === gets the job done 46 | t.ok(d(null, null), 'null is the same as itself') 47 | t.ok(d(undefined, undefined), 'undefined is the same as itself') 48 | t.ok(d(0, 0), 'numbers check out') 49 | t.ok(d(1 / 0, 1 / 0), "it's a travesty that 1 / 0 = Infinity, but Infinities are equal") 50 | t.ok(d('ok', 'ok'), 'strings check out') 51 | t.ok(d(functionA, functionB), 'references to the same function are equal') 52 | 53 | // 4. buffers are compared by value 54 | var bufferA = new Buffer('abc') 55 | var bufferB = new Buffer('abc') 56 | t.ok(d(bufferA, bufferB), 'buffers are compared by value') 57 | 58 | // 5. dates are compared by numeric (time) value 59 | var dateA = new Date('2001-01-11') 60 | var dateB = new Date('2001-01-11') 61 | t.ok(d(dateA, dateB), 'dates are compared by time value') 62 | 63 | // 6. regexps are compared by their properties 64 | var rexpA = /^h[oe][wl][dl][oy]$/ 65 | var rexpB = /^h[oe][wl][dl][oy]$/ 66 | t.ok(d(rexpA, rexpB), 'regexps are compared by their properties') 67 | 68 | // 8. loads of tests for objects 69 | t.ok(d({}, {}), 'bare objects check out') 70 | var a = { a: 'a' } 71 | var b = a 72 | t.ok(d(a, b), 'identical object references check out') 73 | b = { a: 'a' } 74 | t.ok(d(a, b), 'identical simple object values check out') 75 | 76 | t.ok(d([0, 1], [0, 1]), 'arrays check out') 77 | 78 | function onerror (error) { console.err(error.stack) } 79 | var eeA = new EventEmitter() 80 | eeA.on('error', onerror) 81 | var eeB = new EventEmitter() 82 | eeB.on('error', onerror) 83 | t.ok(d(eeA, eeB), 'more complex objects check out') 84 | 85 | var cyclicA = {} 86 | cyclicA.x = cyclicA 87 | var cyclicB = {} 88 | cyclicB.x = cyclicB 89 | t.ok(d(cyclicA, cyclicB), 'can handle cyclic data structures') 90 | 91 | var y = {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {}}}}}}}}}}}}}}}} 92 | y.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v = y 93 | var z = {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {v: {}}}}}}}}}}}}}}}} 94 | z.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v = z 95 | t.ok(d(y, z), 'deeply recursive data structures also work') 96 | 97 | t.ok(d(heinous, awful), 'more complex objects also check out') 98 | 99 | awful.granular.self = heinous 100 | heinous.granular.self = awful 101 | t.ok(d(heinous, awful), 102 | 'mutual recursion with otherwise identical structures fools deepEquals') 103 | 104 | /* 105 | * 106 | * FAILURE 107 | * 108 | */ 109 | 110 | // 1. === does its job 111 | t.notOk(d(NaN, NaN), 'NaN is the only JavaScript value not equal to itself') 112 | t.notOk(d(1 / 0, -1 / 0), 'opposite infinities are different') 113 | t.notOk(d(1, '1'), 'strict equality, no coercion between strings and numbers') 114 | t.notOk(d('ok', 'nok'), 'different strings are different') 115 | t.notOk(d(0, '0'), 'strict equality, no coercion between strings and numbers') 116 | t.notOk(d(undefined, null), 'so many kinds of nothingness!') 117 | t.notOk(d(function nop () {}, 118 | function nop () {}), 'functions are only the same by reference') 119 | 120 | // 2. one is an object, the other is not 121 | t.notOk(d(undefined, {}), "if both aren't objects, not the same") 122 | 123 | // 3. null is an object 124 | t.notOk(d({}, null), 'null is of type object') 125 | 126 | // 4. buffers are compared by both byte length (for speed) and value 127 | bufferB = new Buffer('abcd') 128 | t.notOk(d(bufferA, bufferB), 'Buffers are checked for length') 129 | bufferB = new Buffer('abd') 130 | t.notOk(d(bufferA, bufferB), 'Buffers are also checked for value') 131 | 132 | // 5. dates 133 | dateB = new Date('2001-01-12') 134 | t.notOk(d(dateA, dateB), 'different dates are not the same') 135 | 136 | // 6. regexps 137 | rexpB = /^(howdy|hello)$/ 138 | t.notOk(d(rexpA, rexpB), 'different regexps are not the same') 139 | 140 | // 7. arguments 141 | var outer = arguments 142 | ;(function inner (tt) { 143 | var inner = arguments 144 | t.ok(d(outer, outer)) 145 | t.ok(d(outer, inner)) 146 | t.notOk(d(outer, [t])) 147 | }(t)) 148 | 149 | // 8. objects present edge cases galore 150 | t.notOk(d([], {}), "different object types shouldn't match") 151 | 152 | var nullstructor = Object.create(null) 153 | t.notOk(d({}, nullstructor), 'Object.create(null).constructor === undefined') 154 | 155 | b = { b: 'b' } 156 | t.notOk(d(a, b), "different object values aren't the same") 157 | 158 | var c = { b: 'b', c: undefined } 159 | t.notOk(d(b, c), "different object values aren't the same") 160 | 161 | function ondata (data) { console.log(data) } 162 | eeB.on('data', ondata) 163 | t.notOk(d(eeA, eeB), "changed objects don't match") 164 | 165 | awful.granular.stuff[2] = 3 166 | t.notOk(d(heinous, awful), 'small changes should be found') 167 | 168 | awful.granular.stuff[2] = 2 169 | t.ok(d(heinous, awful), 'small changes should be fixable') 170 | 171 | t.end() 172 | }) 173 | --------------------------------------------------------------------------------