Bug 1447977 - Move cyclic object test function to evaluate. r=automatedtester a=test-only
authorAndreas Tolfsen <ato@sny.no>
Fri, 06 Jul 2018 18:35:21 +0100
changeset 478126 43f6112e957876670aa24461d6d751a8de5b3cfb
parent 478125 332084df66518b9065ce7f4eebbda8ae5db41338
child 478127 20c023fb254c10ad540825753b49ca75af96e0c6
push id9539
push userarchaeopteryx@coole-files.de
push dateThu, 26 Jul 2018 08:07:25 +0000
treeherdermozilla-beta@20c023fb254c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester, test-only
bugs1447977
milestone62.0
Bug 1447977 - Move cyclic object test function to evaluate. r=automatedtester a=test-only 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
@@ -290,16 +290,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();
+});