Bug 1447977 - Move cyclic object test function to evaluate. r=automatedtester
authorAndreas Tolfsen <ato@sny.no>
Fri, 06 Jul 2018 18:35:21 +0100
changeset 425738 cbb958819d32f25d41d0e6fe61105d57a882b3eb
parent 425737 dccb4ac6468a39d0266039d5c0ad0ca9cbe2c462
child 425739 b4cd5a640564cee2dda926724bf2faeb8934d702
push id34264
push usershindli@mozilla.com
push dateWed, 11 Jul 2018 09:41:50 +0000
treeherdermozilla-central@5a5c74e9ccc0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester
bugs1447977
milestone63.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
Bug 1447977 - Move cyclic object test function to evaluate. r=automatedtester Moves the innards of assert.acyclic to evaluate.isCyclic so it can be called externally without throwing. It makes more sense for this function to be exposed on the evaluate module, since other behaviour related to JSON marshaling is also defined there. MozReview-Commit-ID: 4WjEy8Sjqrm
testing/marionette/assert.js
testing/marionette/evaluate.js
testing/marionette/test/unit/test_assert.js
testing/marionette/test/unit/test_evaluate.js
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -14,55 +14,51 @@ const {
   InvalidSessionIDError,
   JavaScriptError,
   NoSuchWindowError,
   UnexpectedAlertOpenError,
   UnsupportedOperationError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 const {pprint} = ChromeUtils.import("chrome://marionette/content/format.js", {});
 
-XPCOMUtils.defineLazyGetter(this, "browser", () => {
-  const {browser} = ChromeUtils.import("chrome://marionette/content/browser.js", {});
-  return browser;
+XPCOMUtils.defineLazyModuleGetters(this, {
+  evaluate: "chrome://marionette/content/evaluate.js",
+  browser: "chrome://marionette/content/browser.js",
 });
 
 this.EXPORTED_SYMBOLS = ["assert"];
 
 const isFennec = () => AppConstants.platform == "android";
 const isFirefox = () =>
     Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
 
 /**
  * Shorthands for common assertions made in Marionette.
  *
  * @namespace
  */
 this.assert = {};
 
 /**
- * Asserts that an arbitrary object, <var>obj</var> is not acyclic.
+ * Asserts that an arbitrary object is not acyclic.
  *
  * @param {*} obj
- *     Object test.  This assertion is only meaningful if passed
+ *     Object to test.  This assertion is only meaningful if passed
  *     an actual object or array.
  * @param {Error=} [error=JavaScriptError] error
  *     Error to throw if assertion fails.
  * @param {string=} message
- *     Message to use for <var>error</var> if assertion fails.  By default
- *     it will use the error message provided by
- *     <code>JSON.stringify</code>.
+ *     Custom message to use for `error` if assertion fails.
  *
  * @throws {JavaScriptError}
- *     If <var>obj</var> is cyclic.
+ *     If the object is cyclic.
  */
 assert.acyclic = function(obj, msg = "", error = JavaScriptError) {
-  try {
-    JSON.stringify(obj);
-  } catch (e) {
-    throw new error(msg || e);
+  if (evaluate.isCyclic(obj)) {
+    throw new error(msg || "Cyclic object value");
   }
 };
 
 /**
  * Asserts that Marionette has a session.
  *
  * @param {GeckoDriver} driver
  *     Marionette driver instance.
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -279,16 +279,34 @@ evaluate.toJSON = function(obj, seenEls)
         throw e;
       }
     }
   }
   return rv;
 };
 
 /**
+ * Tests if an arbitrary object is cyclic.
+ *
+ * @param {*} obj
+ *     Object to test for cyclical references.
+ *
+ * @return {boolean}
+ *     True if object is cyclic, false otherwise.
+ */
+evaluate.isCyclic = function(obj) {
+  try {
+    JSON.stringify(obj);
+    return false;
+  } catch (e) {
+    return true;
+  }
+};
+
+/**
  * `Cu.isDeadWrapper` does not return true for a dead sandbox that
  * was assosciated with and extension popup.  This provides a way to
  * still test for a dead object.
  *
  * @param {Object} obj
  *     A potentially dead object.
  * @param {string} prop
  *     Name of a property on the object.
--- a/testing/marionette/test/unit/test_assert.js
+++ b/testing/marionette/test/unit/test_assert.js
@@ -13,48 +13,23 @@ const {
   NoSuchWindowError,
   SessionNotCreatedError,
   UnexpectedAlertOpenError,
   UnsupportedOperationError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 
 add_test(function test_acyclic() {
   assert.acyclic({});
-  assert.acyclic(new Object());
-  assert.acyclic([]);
-  assert.acyclic(new Array());
 
-  // object
   Assert.throws(() => {
     let obj = {};
     obj.reference = obj;
     assert.acyclic(obj);
   }, JavaScriptError);
 
-  // array
-  Assert.throws(() => {
-    let arr = [];
-    arr.push(arr);
-    assert.acyclic(arr);
-  }, JavaScriptError);
-
-  // array in object
-  Assert.throws(() => {
-    let arr = [];
-    arr.push(arr);
-    assert.acyclic({arr});
-  }, JavaScriptError);
-
-  // object in array
-  Assert.throws(() => {
-    let obj = {};
-    obj.reference = obj;
-    assert.acyclic([obj]);
-  }, JavaScriptError);
-
   // custom message
   let cyclic = {};
   cyclic.reference = cyclic;
   Assert.throws(() => assert.acyclic(cyclic, "", RangeError), RangeError);
   Assert.throws(() => assert.acyclic(cyclic, "foo"), /JavaScriptError: foo/);
   Assert.throws(() => assert.acyclic(cyclic, "bar", RangeError), /RangeError: bar/);
 
   run_next_test();
--- a/testing/marionette/test/unit/test_evaluate.js
+++ b/testing/marionette/test/unit/test_evaluate.js
@@ -113,8 +113,48 @@ add_test(function test_toJSON_objects() 
   equal(true, actual.boolean);
   deepEqual([], actual.array);
   ok(WebElement.isReference(actual.webElement));
   equal("foo", actual.toJSON);
   deepEqual({"bar": "baz"}, actual.object);
 
   run_next_test();
 });
+
+add_test(function test_isCyclic_noncyclic() {
+  for (let type of [true, 42, "foo", [], {}, null, undefined]) {
+    ok(!evaluate.isCyclic(type));
+  }
+
+  run_next_test();
+});
+
+add_test(function test_isCyclic_object() {
+  let obj = {};
+  obj.reference = obj;
+  ok(evaluate.isCyclic(obj));
+
+  run_next_test();
+});
+
+add_test(function test_isCyclic_array() {
+  let arr = [];
+  arr.push(arr);
+  ok(evaluate.isCyclic(arr));
+
+  run_next_test();
+});
+
+add_test(function test_isCyclic_arrayInObject() {
+  let arr = [];
+  arr.push(arr);
+  ok(evaluate.isCyclic({arr}));
+
+  run_next_test();
+});
+
+add_test(function test_isCyclic_objectInArray() {
+  let obj = {};
+  obj.reference = obj;
+  ok(evaluate.isCyclic([obj]));
+
+  run_next_test();
+});