Bug 1106913 - Add assert.acyclic for testing for cyclic objects. r?whimboo
Introduces a new assert.acyclic assertion helper function that uses
JSON.stringify to test if the input contains cyclic object references.
A JavaScriptError will be thrown if the object is not acyclic.
MozReview-Commit-ID: 3y8pnkPgf2k
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -8,16 +8,17 @@ const {utils: Cu} = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const {
InvalidArgumentError,
InvalidSessionIDError,
+ JavaScriptError,
NoSuchWindowError,
UnexpectedAlertOpenError,
UnsupportedOperationError,
} = Cu.import("chrome://marionette/content/error.js", {});
const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
this.EXPORTED_SYMBOLS = ["assert"];
@@ -28,16 +29,40 @@ const isFirefox = () =>
/**
* Shorthands for common assertions made in Marionette.
*
* @namespace
*/
this.assert = {};
/**
+ * Asserts that an arbitrary object, <var>obj</var> is not acyclic.
+ *
+ * @param {*} obj
+ * Object 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>.
+ *
+ * @throws {JavaScriptError}
+ * If <var>obj</var> is cyclic.
+ */
+assert.acyclic = function(obj, msg = "", error = JavaScriptError) {
+ try {
+ JSON.stringify(obj);
+ } catch (e) {
+ throw new error(msg || e);
+ }
+};
+
+/**
* Asserts that Marionette has a session.
*
* @param {GeckoDriver} driver
* Marionette driver instance.
* @param {string=} msg
* Custom error message.
*
* @return {string}
--- a/testing/marionette/test_assert.js
+++ b/testing/marionette/test_assert.js
@@ -4,16 +4,60 @@
"use strict";
const {utils: Cu} = Components;
Cu.import("chrome://marionette/content/assert.js");
Cu.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();
+});
+
add_test(function test_session() {
assert.session({sessionID: "foo"});
for (let typ of [null, undefined, ""]) {
Assert.throws(() => assert.session({sessionId: typ}), InvalidSessionIDError);
}
Assert.throws(() => assert.session({sessionId: null}, "custom"), /custom/);