Bug 1447977 - Move cyclic object test function to evaluate. r?automatedtester draft
authorAndreas Tolfsen <ato@sny.no>
Fri, 06 Jul 2018 18:35:21 +0100
changeset 816077 4edf3ef4c78c6bb2ead534760a333d5cdd1e5b20
parent 816076 427cf313322203975deeebabb1c7e775d6a7efb8
child 816078 b42f3d93e0375205df2c0bae39fdccbc86ecabc2
child 816079 038aa18eb4096e93148ce00baef477e4d3ed4a44
push id115749
push userbmo:ato@sny.no
push dateTue, 10 Jul 2018 15:56:00 +0000
reviewersautomatedtester
bugs1447977
milestone63.0a1
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();
+});