☠☠ backed out by 83e259349f52 ☠ ☠ | |
author | Georg Fritzsche <georg.fritzsche@googlemail.com> |
Fri, 27 Mar 2015 21:01:19 +0100 | |
changeset 236315 | 9623fa2b2e16de005b1508ba257e78b53c55270b |
parent 236314 | d7a94bbfa3ef6a267d711d4e806fd3c6bbde0a77 |
child 236316 | a54f84a3d26537d9ed5ed0472a4abd480b2c8b79 |
push id | 57652 |
push user | philringnalda@gmail.com |
push date | Sat, 28 Mar 2015 18:56:57 +0000 |
treeherder | mozilla-inbound@87e10bac6fd1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | yoric |
bugs | 1140558 |
milestone | 39.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
|
--- a/testing/modules/Assert.jsm +++ b/testing/modules/Assert.jsm @@ -12,16 +12,17 @@ "use strict"; this.EXPORTED_SYMBOLS = [ "Assert" ]; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/ObjectUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); /** * 1. The assert module provides functions that throw AssertionError's when * particular conditions are not met. * * To use the module you'll need to instantiate it first, which allows consumers @@ -253,105 +254,19 @@ proto.notEqual = function notEqual(actua * @param actual * (mixed) Test subject to be evaluated as equivalent to `expected`, including nested properties * @param expected * (mixed) Test reference to evaluate against `actual` * @param message (optional) * (string) Short explanation of the expected result */ proto.deepEqual = function deepEqual(actual, expected, message) { - this.report(!_deepEqual(actual, expected), actual, expected, message, "deepEqual"); + this.report(!ObjectUtils.deepEqual(actual, expected), actual, expected, message, "deepEqual"); }; -function _deepEqual(actual, expected) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (instanceOf(actual, "Date") && instanceOf(expected, "Date")) { - if (isNaN(actual.getTime()) && isNaN(expected.getTime())) - return true; - return actual.getTime() === expected.getTime(); - // 7.3 If the expected value is a RegExp object, the actual value is - // equivalent if it is also a RegExp object with the same source and - // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). - } else if (instanceOf(actual, "RegExp") && instanceOf(expected, "RegExp")) { - return actual.source === expected.source && - actual.global === expected.global && - actual.multiline === expected.multiline && - actual.lastIndex === expected.lastIndex && - actual.ignoreCase === expected.ignoreCase; - // 7.4. Other pairs that do not both pass typeof value == "object", - // equivalence is determined by ==. - } else if (typeof actual != "object" && typeof expected != "object") { - return actual == expected; - // 7.5 For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical 'prototype' property. Note: this - // accounts for both named and indexed properties on Arrays. - } else { - return objEquiv(actual, expected); - } -} - -function isUndefinedOrNull(value) { - return value === null || value === undefined; -} - -function isArguments(object) { - return instanceOf(object, "Arguments"); -} - -function objEquiv(a, b) { - if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) { - return false; - } - // An identical 'prototype' property. - if (a.prototype !== b.prototype) { - return false; - } - // Object.keys may be broken through screwy arguments passing. Converting to - // an array solves the problem. - if (isArguments(a)) { - if (!isArguments(b)) { - return false; - } - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b); - } - let ka, kb, key, i; - try { - ka = Object.keys(a); - kb = Object.keys(b); - } catch (e) { - // Happens when one is a string literal and the other isn't - return false; - } - // Having the same number of owned properties (keys incorporates - // hasOwnProperty) - if (ka.length != kb.length) - return false; - // The same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - // Equivalent values for every corresponding key, and possibly expensive deep - // test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!_deepEqual(a[key], b[key])) { - return false; - } - } - return true; -} - /** * 8. The non-equivalence assertion tests for any deep inequality. * assert.notDeepEqual(actual, expected, message_opt); * * @param actual * (mixed) Test subject to be evaluated as NOT equivalent to `expected`, including nested properties * @param expected * (mixed) Test reference to evaluate against `actual`
--- a/testing/modules/tests/xpcshell/test_assert.js +++ b/testing/modules/tests/xpcshell/test_assert.js @@ -204,34 +204,16 @@ function run_test() { // use a fn to validate error object assert.throws(makeBlock(thrower, TypeError), function(err) { if ((err instanceof TypeError) && /test/.test(err)) { return true; } }); - // Make sure deepEqual doesn't loop forever on circular refs - - let b = {}; - b.b = b; - - let c = {}; - c.b = c; - - let gotError = false; - try { - assert.deepEqual(b, c); - } catch (e) { - gotError = true; - } - - dump("All OK\n"); - assert.ok(gotError); - function testAssertionMessage(actual, expected) { try { assert.equal(actual, ""); } catch (e) { assert.equal(e.toString(), ["AssertionError:", expected, "==", '""'].join(" ")); } }
new file mode 100644 --- /dev/null +++ b/toolkit/modules/ObjectUtils.jsm @@ -0,0 +1,131 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Portions of this file are originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// MIT license: http://opensource.org/licenses/MIT + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "ObjectUtils" +]; + +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; + +this.ObjectUtils = { + /** + * This tests objects & values for deep equality. + * + * We check using the most exact approximation of equality between two objects + * to keep the chance of false positives to a minimum. + * `JSON.stringify` is not designed to be used for this purpose; objects may + * have ambiguous `toJSON()` implementations that would influence the test. + * + * @param a (mixed) Object or value to be compared. + * @param b (mixed) Object or value to be compared. + * @return Boolean Whether the objects are deep equal. + */ + deepEqual: function(a, b) { + return _deepEqual(a, b); + }, +}; + +// ... Start of previously MIT-licensed code. +// This deepEqual implementation is originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// MIT license: http://opensource.org/licenses/MIT + +function _deepEqual(a, b) { + // The numbering below refers to sections in the CommonJS spec. + + // 7.1 All identical values are equivalent, as determined by ===. + if (a === b) { + return true; + // 7.2 If the b value is a Date object, the a value is + // equivalent if it is also a Date object that refers to the same time. + } else if (instanceOf(a, "Date") && instanceOf(b, "Date")) { + if (isNaN(a.getTime()) && isNaN(b.getTime())) + return true; + return a.getTime() === b.getTime(); + // 7.3 If the b value is a RegExp object, the a value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (instanceOf(a, "RegExp") && instanceOf(b, "RegExp")) { + return a.source === b.source && + a.global === b.global && + a.multiline === b.multiline && + a.lastIndex === b.lastIndex && + a.ignoreCase === b.ignoreCase; + // 7.4 Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof a != "object" && typeof b != "object") { + return a == b; + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(a, b); + } +} + +function instanceOf(object, type) { + return Object.prototype.toString.call(object) == "[object " + type + "]"; +} + +function isUndefinedOrNull(value) { + return value === null || value === undefined; +} + +function isArguments(object) { + return instanceOf(object, "Arguments"); +} + +function objEquiv(a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) { + return false; + } + // An identical 'prototype' property. + if (a.prototype !== b.prototype) { + return false; + } + // Object.keys may be broken through screwy arguments passing. Converting to + // an array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + let ka, kb; + try { + ka = Object.keys(a); + kb = Object.keys(b); + } catch (e) { + // Happens when one is a string literal and the other isn't + return false; + } + // Having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + // The same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + // Equivalent values for every corresponding key, and possibly expensive deep + // test + for (let key of ka) { + if (!_deepEqual(a[key], b[key])) { + return false; + } + } + return true; +} + +// ... End of previously MIT-licensed code.
--- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -25,16 +25,17 @@ EXTRA_JS_MODULES += [ 'Finder.jsm', 'Geometry.jsm', 'Http.jsm', 'InlineSpellChecker.jsm', 'InlineSpellCheckerContent.jsm', 'LoadContextInfo.jsm', 'Log.jsm', 'NewTabUtils.jsm', + 'ObjectUtils.jsm', 'PageMenu.jsm', 'PageMetadata.jsm', 'PermissionsUtils.jsm', 'PopupNotifications.jsm', 'Preferences.jsm', 'PrivateBrowsingUtils.jsm', 'ProfileAge.jsm', 'Promise-backend.js',
new file mode 100644 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_ObjectUtils.js @@ -0,0 +1,92 @@ +Components.utils.import("resource://gre/modules/ObjectUtils.jsm"); + +function run_test() { + run_next_test(); +} + +add_task(function* test_deepEqual() { + let deepEqual = ObjectUtils.deepEqual.bind(ObjectUtils); + // CommonJS 7.2 + Assert.ok(deepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)), "deepEqual date"); + Assert.ok(deepEqual(new Date(NaN), new Date(NaN)), "deepEqual invalid dates"); + + Assert.ok(!deepEqual(new Date(), new Date(2000, 3, 14)), "deepEqual date"); + + // 7.3 + Assert.ok(deepEqual(/a/, /a/)); + Assert.ok(deepEqual(/a/g, /a/g)); + Assert.ok(deepEqual(/a/i, /a/i)); + Assert.ok(deepEqual(/a/m, /a/m)); + Assert.ok(deepEqual(/a/igm, /a/igm)); + Assert.ok(!deepEqual(/ab/, /a/)); + Assert.ok(!deepEqual(/a/g, /a/)); + Assert.ok(!deepEqual(/a/i, /a/)); + Assert.ok(!deepEqual(/a/m, /a/)); + Assert.ok(!deepEqual(/a/igm, /a/im)); + + let re1 = /a/; + re1.lastIndex = 3; + Assert.ok(!deepEqual(re1, /a/)); + + // 7.4 + Assert.ok(deepEqual(4, "4"), "deepEqual == check"); + Assert.ok(deepEqual(true, 1), "deepEqual == check"); + Assert.ok(!deepEqual(4, "5"), "deepEqual == check"); + + // 7.5 + // having the same number of owned properties && the same set of keys + Assert.ok(deepEqual({a: 4}, {a: 4})); + Assert.ok(deepEqual({a: 4, b: "2"}, {a: 4, b: "2"})); + Assert.ok(deepEqual([4], ["4"])); + Assert.ok(!deepEqual({a: 4}, {a: 4, b: true})); + Assert.ok(deepEqual(["a"], {0: "a"})); + + let a1 = [1, 2, 3]; + let a2 = [1, 2, 3]; + a1.a = "test"; + a1.b = true; + a2.b = true; + a2.a = "test"; + Assert.ok(!deepEqual(Object.keys(a1), Object.keys(a2))); + Assert.ok(deepEqual(a1, a2)); + + let nbRoot = { + toString: function() { return this.first + " " + this.last; } + }; + + function nameBuilder(first, last) { + this.first = first; + this.last = last; + return this; + } + nameBuilder.prototype = nbRoot; + + function nameBuilder2(first, last) { + this.first = first; + this.last = last; + return this; + } + nameBuilder2.prototype = nbRoot; + + let nb1 = new nameBuilder("Ryan", "Dahl"); + let nb2 = new nameBuilder2("Ryan", "Dahl"); + + Assert.ok(deepEqual(nb1, nb2)); + + nameBuilder2.prototype = Object; + nb2 = new nameBuilder2("Ryan", "Dahl"); + Assert.ok(!deepEqual(nb1, nb2)); + + // String literal + object + Assert.ok(!deepEqual("a", {})); + + // Make sure deepEqual doesn't loop forever on circular refs + + let b = {}; + b.b = b; + + let c = {}; + c.b = c; + + Assert.ok(!deepEqual(b, c)); +});
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini +++ b/toolkit/modules/tests/xpcshell/xpcshell.ini @@ -11,16 +11,17 @@ support-files = [test_BinarySearch.js] [test_DeferredTask.js] [test_dict.js] [test_FileUtils.js] [test_GMPInstallManager.js] [test_Http.js] [test_Log.js] [test_NewTabUtils.js] +[test_ObjectUtils.js] [test_PermissionsUtils.js] [test_Preferences.js] [test_Promise.js] [test_PromiseUtils.js] [test_propertyListsUtils.js] [test_readCertPrefs.js] [test_Services.js] [test_sqlite.js]