summaryrefslogtreecommitdiffstats
path: root/remote/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/unit')
-rw-r--r--remote/test/unit/test_Connection.js31
-rw-r--r--remote/test/unit/test_DomainCache.js127
-rw-r--r--remote/test/unit/test_Error.js119
-rw-r--r--remote/test/unit/test_Format.js119
-rw-r--r--remote/test/unit/test_Session.js42
-rw-r--r--remote/test/unit/test_StreamRegistry.js172
-rw-r--r--remote/test/unit/test_Sync.js230
-rw-r--r--remote/test/unit/xpcshell.ini14
8 files changed, 854 insertions, 0 deletions
diff --git a/remote/test/unit/test_Connection.js b/remote/test/unit/test_Connection.js
new file mode 100644
index 0000000000..093daea338
--- /dev/null
+++ b/remote/test/unit/test_Connection.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Connection } = ChromeUtils.import(
+ "chrome://remote/content/Connection.jsm"
+);
+
+add_test(function test_Connection_splitMethod() {
+ for (const t of [42, null, true, {}, [], undefined]) {
+ Assert.throws(
+ () => Connection.splitMethod(t),
+ /TypeError/,
+ `${typeof t} throws`
+ );
+ }
+ for (const s of ["", ".", "foo.", ".bar", "foo.bar.baz"]) {
+ Assert.throws(
+ () => Connection.splitMethod(s),
+ /Invalid method format: ".*"/,
+ `"${s}" throws`
+ );
+ }
+ deepEqual(Connection.splitMethod("foo.bar"), {
+ domain: "foo",
+ command: "bar",
+ });
+
+ run_next_test();
+});
diff --git a/remote/test/unit/test_DomainCache.js b/remote/test/unit/test_DomainCache.js
new file mode 100644
index 0000000000..e54ebbc314
--- /dev/null
+++ b/remote/test/unit/test_DomainCache.js
@@ -0,0 +1,127 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Domain } = ChromeUtils.import(
+ "chrome://remote/content/domains/Domain.jsm"
+);
+const { DomainCache } = ChromeUtils.import(
+ "chrome://remote/content/domains/DomainCache.jsm"
+);
+
+class MockSession {
+ onEvent() {}
+}
+
+const noopSession = new MockSession();
+
+add_test(function test_DomainCache_constructor() {
+ new DomainCache(noopSession, {});
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_domainSupportsMethod() {
+ const modules = {
+ Foo: class extends Domain {
+ bar() {}
+ },
+ };
+ const domains = new DomainCache(noopSession, modules);
+
+ ok(domains.domainSupportsMethod("Foo", "bar"));
+ ok(!domains.domainSupportsMethod("Foo", "baz"));
+ ok(!domains.domainSupportsMethod("foo", "bar"));
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_get_invalidModule() {
+ Assert.throws(() => {
+ const domains = new DomainCache(noopSession, { Foo: undefined });
+ domains.get("Foo");
+ }, /UnknownMethodError/);
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_get_missingConstructor() {
+ Assert.throws(() => {
+ const domains = new DomainCache(noopSession, { Foo: {} });
+ domains.get("Foo");
+ }, /TypeError/);
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_get_superClassNotDomain() {
+ Assert.throws(() => {
+ const domains = new DomainCache(noopSession, { Foo: class {} });
+ domains.get("Foo");
+ }, /TypeError/);
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_get_constructs() {
+ let eventFired;
+ class Session {
+ onEvent(event) {
+ eventFired = event;
+ }
+ }
+
+ let constructed = false;
+ class Foo extends Domain {
+ constructor() {
+ super();
+ constructed = true;
+ }
+ }
+
+ const session = new Session();
+ const domains = new DomainCache(session, { Foo });
+
+ const foo = domains.get("Foo");
+ ok(constructed);
+ ok(foo instanceof Foo);
+
+ const event = {};
+ foo.emit(event);
+ equal(event, eventFired);
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_size() {
+ class Foo extends Domain {}
+ const domains = new DomainCache(noopSession, { Foo });
+
+ equal(domains.size, 0);
+ domains.get("Foo");
+ equal(domains.size, 1);
+
+ run_next_test();
+});
+
+add_test(function test_DomainCache_clear() {
+ let dtorCalled = false;
+ class Foo extends Domain {
+ destructor() {
+ dtorCalled = true;
+ }
+ }
+
+ const domains = new DomainCache(noopSession, { Foo });
+
+ equal(domains.size, 0);
+ domains.get("Foo");
+ equal(domains.size, 1);
+
+ domains.clear();
+ equal(domains.size, 0);
+ ok(dtorCalled);
+
+ run_next_test();
+});
diff --git a/remote/test/unit/test_Error.js b/remote/test/unit/test_Error.js
new file mode 100644
index 0000000000..a4d81483d2
--- /dev/null
+++ b/remote/test/unit/test_Error.js
@@ -0,0 +1,119 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+/* eslint-disable no-tabs */
+
+const {
+ RemoteAgentError,
+ UnknownMethodError,
+ UnsupportedError,
+} = ChromeUtils.import("chrome://remote/content/Error.jsm");
+
+add_test(function test_RemoteAgentError_ctor() {
+ const e1 = new RemoteAgentError();
+ equal(e1.name, "RemoteAgentError");
+ equal(e1.message, "");
+ equal(e1.cause, e1.message);
+
+ const e2 = new RemoteAgentError("message");
+ equal(e2.message, "message");
+ equal(e2.cause, e2.message);
+
+ const e3 = new RemoteAgentError("message", "cause");
+ equal(e3.message, "message");
+ equal(e3.cause, "cause");
+
+ run_next_test();
+});
+
+add_test(function test_RemoteAgentError_notify() {
+ // nothing much we can test, except test that it doesn't throw
+ new RemoteAgentError().notify();
+
+ run_next_test();
+});
+
+add_test(function test_RemoteAgentError_toString() {
+ const e = new RemoteAgentError("message");
+ equal(e.toString(), RemoteAgentError.format(e));
+ equal(
+ e.toString({ stack: true }),
+ RemoteAgentError.format(e, { stack: true })
+ );
+
+ run_next_test();
+});
+
+add_test(function test_RemoteAgentError_format() {
+ const { format } = RemoteAgentError;
+
+ equal(format({ name: "HippoError" }), "HippoError");
+ equal(format({ name: "HorseError", message: "neigh" }), "HorseError: neigh");
+
+ const dog = {
+ name: "DogError",
+ message: "woof",
+ stack: " one\ntwo\nthree ",
+ };
+ equal(format(dog), "DogError: woof");
+ equal(
+ format(dog, { stack: true }),
+ `DogError: woof:
+ one
+ two
+ three`
+ );
+
+ const cat = {
+ name: "CatError",
+ message: "meow",
+ stack: "four\nfive\nsix",
+ cause: dog,
+ };
+ equal(format(cat), "CatError: meow");
+ equal(
+ format(cat, { stack: true }),
+ `CatError: meow:
+ four
+ five
+ six
+caused by: DogError: woof:
+ one
+ two
+ three`
+ );
+
+ run_next_test();
+});
+
+add_test(function test_RemoteAgentError_fromJSON() {
+ const cdpErr = {
+ message: `TypeError: foo:
+ bar
+ baz`,
+ };
+ const err = RemoteAgentError.fromJSON(cdpErr);
+
+ equal(err.message, "TypeError: foo");
+ equal(err.stack, "bar\nbaz");
+ equal(err.cause, null);
+
+ run_next_test();
+});
+
+add_test(function test_UnsupportedError() {
+ ok(new UnsupportedError() instanceof RemoteAgentError);
+ run_next_test();
+});
+
+add_test(function test_UnknownMethodError() {
+ ok(new UnknownMethodError() instanceof RemoteAgentError);
+ ok(new UnknownMethodError("domain").message.endsWith("domain"));
+ ok(
+ new UnknownMethodError("domain", "command").message.endsWith(
+ "domain.command"
+ )
+ );
+ run_next_test();
+});
diff --git a/remote/test/unit/test_Format.js b/remote/test/unit/test_Format.js
new file mode 100644
index 0000000000..1787bdb491
--- /dev/null
+++ b/remote/test/unit/test_Format.js
@@ -0,0 +1,119 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { truncate, pprint } = ChromeUtils.import(
+ "chrome://remote/content/Format.jsm"
+);
+
+const MAX_STRING_LENGTH = 250;
+const HALF = "x".repeat(MAX_STRING_LENGTH / 2);
+
+add_test(function test_pprint() {
+ equal('[object Object] {"foo":"bar"}', pprint`${{ foo: "bar" }}`);
+
+ equal("[object Number] 42", pprint`${42}`);
+ equal("[object Boolean] true", pprint`${true}`);
+ equal("[object Undefined] undefined", pprint`${undefined}`);
+ equal("[object Null] null", pprint`${null}`);
+
+ let complexObj = { toJSON: () => "foo" };
+ equal('[object Object] "foo"', pprint`${complexObj}`);
+
+ let cyclic = {};
+ cyclic.me = cyclic;
+ equal("[object Object] <cyclic object value>", pprint`${cyclic}`);
+
+ let el = {
+ hasAttribute: attr => attr in el,
+ getAttribute: attr => (attr in el ? el[attr] : null),
+ nodeType: 1,
+ localName: "input",
+ id: "foo",
+ class: "a b",
+ href: "#",
+ name: "bar",
+ src: "s",
+ type: "t",
+ };
+ equal(
+ '<input id="foo" class="a b" href="#" name="bar" src="s" type="t">',
+ pprint`${el}`
+ );
+
+ run_next_test();
+});
+
+add_test(function test_truncate_empty() {
+ equal(truncate``, "");
+ run_next_test();
+});
+
+add_test(function test_truncate_noFields() {
+ equal(truncate`foo bar`, "foo bar");
+ run_next_test();
+});
+
+add_test(function test_truncate_multipleFields() {
+ equal(truncate`${0}`, "0");
+ equal(truncate`${1}${2}${3}`, "123");
+ equal(truncate`a${1}b${2}c${3}`, "a1b2c3");
+ run_next_test();
+});
+
+add_test(function test_truncate_primitiveFields() {
+ equal(truncate`${123}`, "123");
+ equal(truncate`${true}`, "true");
+ equal(truncate`${null}`, "");
+ equal(truncate`${undefined}`, "");
+ run_next_test();
+});
+
+add_test(function test_truncate_string() {
+ equal(truncate`${"foo"}`, "foo");
+ equal(truncate`${"x".repeat(250)}`, "x".repeat(250));
+ equal(truncate`${"x".repeat(260)}`, `${HALF} ... ${HALF}`);
+ run_next_test();
+});
+
+add_test(function test_truncate_array() {
+ equal(truncate`${["foo"]}`, JSON.stringify(["foo"]));
+ equal(truncate`${"foo"} ${["bar"]}`, `foo ${JSON.stringify(["bar"])}`);
+ equal(
+ truncate`${["x".repeat(260)]}`,
+ JSON.stringify([`${HALF} ... ${HALF}`])
+ );
+
+ run_next_test();
+});
+
+add_test(function test_truncate_object() {
+ equal(truncate`${{}}`, JSON.stringify({}));
+ equal(truncate`${{ foo: "bar" }}`, JSON.stringify({ foo: "bar" }));
+ equal(
+ truncate`${{ foo: "x".repeat(260) }}`,
+ JSON.stringify({ foo: `${HALF} ... ${HALF}` })
+ );
+ equal(truncate`${{ foo: ["bar"] }}`, JSON.stringify({ foo: ["bar"] }));
+ equal(
+ truncate`${{ foo: ["bar", { baz: 42 }] }}`,
+ JSON.stringify({ foo: ["bar", { baz: 42 }] })
+ );
+
+ let complex = {
+ toString() {
+ return "hello world";
+ },
+ };
+ equal(truncate`${complex}`, "hello world");
+
+ let longComplex = {
+ toString() {
+ return "x".repeat(260);
+ },
+ };
+ equal(truncate`${longComplex}`, `${HALF} ... ${HALF}`);
+
+ run_next_test();
+});
diff --git a/remote/test/unit/test_Session.js b/remote/test/unit/test_Session.js
new file mode 100644
index 0000000000..65d1b8bcf8
--- /dev/null
+++ b/remote/test/unit/test_Session.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Session } = ChromeUtils.import(
+ "chrome://remote/content/sessions/Session.jsm"
+);
+
+const connection = {
+ registerSession: () => {},
+ transport: {
+ on: () => {},
+ },
+};
+
+class MockTarget {
+ constructor() {}
+
+ get browsingContext() {
+ return { id: 42 };
+ }
+
+ get mm() {
+ return {
+ addMessageListener() {},
+ removeMessageListener() {},
+ loadFrameScript() {},
+ sendAsyncMessage() {},
+ };
+ }
+}
+
+add_test(function test_Session_destructor() {
+ const session = new Session(connection, new MockTarget());
+ session.domains.get("Browser");
+ equal(session.domains.size, 1);
+ session.destructor();
+ equal(session.domains.size, 0);
+
+ run_next_test();
+});
diff --git a/remote/test/unit/test_StreamRegistry.js b/remote/test/unit/test_StreamRegistry.js
new file mode 100644
index 0000000000..ed68577540
--- /dev/null
+++ b/remote/test/unit/test_StreamRegistry.js
@@ -0,0 +1,172 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { AsyncShutdown } = ChromeUtils.import(
+ "resource://gre/modules/AsyncShutdown.jsm"
+);
+const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const { StreamRegistry } = ChromeUtils.import(
+ "chrome://remote/content/StreamRegistry.jsm"
+);
+
+add_test(function test_constructor() {
+ const registry = new StreamRegistry();
+ equal(registry.streams.size, 0);
+
+ run_next_test();
+});
+
+add_test(async function test_destructor() {
+ const registry = new StreamRegistry();
+ const { file: file1, path: path1 } = await createFile("foo bar");
+ const { file: file2, path: path2 } = await createFile("foo bar");
+
+ registry.add(file1);
+ registry.add(file2);
+
+ equal(registry.streams.size, 2);
+
+ await registry.destructor();
+ equal(registry.streams.size, 0);
+ ok(!(await OS.File.exists(path1)), "temporary file has been removed");
+ ok(!(await OS.File.exists(path2)), "temporary file has been removed");
+
+ run_next_test();
+});
+
+add_test(async function test_addValidStreamType() {
+ const registry = new StreamRegistry();
+ const { file } = await createFile("foo bar");
+
+ const handle = registry.add(file);
+ equal(registry.streams.size, 1, "A single stream has been added");
+ equal(typeof handle, "string", "Handle is of type string");
+ ok(registry.streams.has(handle), "Handle has been found");
+ equal(registry.streams.get(handle), file, "Expected OS.File stream found");
+
+ run_next_test();
+});
+
+add_test(async function test_addCreatesDifferentHandles() {
+ const registry = new StreamRegistry();
+ const { file } = await createFile("foo bar");
+
+ const handle1 = registry.add(file);
+ equal(registry.streams.size, 1, "A single stream has been added");
+ equal(typeof handle1, "string", "Handle is of type string");
+ ok(registry.streams.has(handle1), "Handle has been found");
+ equal(registry.streams.get(handle1), file, "Expected OS.File stream found");
+
+ const handle2 = registry.add(file);
+ equal(registry.streams.size, 2, "A single stream has been added");
+ equal(typeof handle2, "string", "Handle is of type string");
+ ok(registry.streams.has(handle2), "Handle has been found");
+ equal(registry.streams.get(handle2), file, "Expected OS.File stream found");
+
+ notEqual(handle1, handle2, "Different handles have been generated");
+
+ run_next_test();
+});
+
+add_test(async function test_addInvalidStreamType() {
+ const registry = new StreamRegistry();
+ Assert.throws(() => registry.add(new Blob([])), /UnsupportedError/);
+
+ run_next_test();
+});
+
+add_test(async function test_getForValidHandle() {
+ const registry = new StreamRegistry();
+ const { file } = await createFile("foo bar");
+ const handle = registry.add(file);
+
+ equal(registry.streams.size, 1, "A single stream has been added");
+ equal(registry.get(handle), file, "Expected OS.File stream found");
+
+ run_next_test();
+});
+
+add_test(async function test_getForInvalidHandle() {
+ const registry = new StreamRegistry();
+ const { file } = await createFile("foo bar");
+ registry.add(file);
+
+ equal(registry.streams.size, 1, "A single stream has been added");
+ Assert.throws(() => registry.get("foo"), /TypeError/);
+
+ run_next_test();
+});
+
+add_test(async function test_removeForValidHandle() {
+ const registry = new StreamRegistry();
+ const { file: file1, path: path1 } = await createFile("foo bar");
+ const { file: file2, path: path2 } = await createFile("foo bar");
+
+ const handle1 = registry.add(file1);
+ const handle2 = registry.add(file2);
+
+ equal(registry.streams.size, 2);
+
+ await registry.remove(handle1);
+ equal(registry.streams.size, 1);
+ ok(
+ !(await OS.File.exists(path1)),
+ "temporary file for first stream has been removed"
+ );
+ equal(registry.get(handle2), file2, "Second stream has not been closed");
+ ok(
+ await OS.File.exists(path2),
+ "temporary file for second stream hasn't been removed"
+ );
+
+ run_next_test();
+});
+
+add_test(async function test_removeForInvalidHandle() {
+ const registry = new StreamRegistry();
+ const { file } = await createFile("foo bar");
+ registry.add(file);
+
+ equal(registry.streams.size, 1, "A single stream has been added");
+ await Assert.rejects(registry.remove("foo"), /TypeError/);
+
+ run_next_test();
+});
+
+async function createFile(contents, options = {}) {
+ let { path = null, remove = true } = options;
+
+ if (!path) {
+ const basePath = OS.Path.join(OS.Constants.Path.tmpDir, "remote-agent.txt");
+ const { file, path: tmpPath } = await OS.File.openUnique(basePath, {
+ humanReadable: true,
+ });
+ await file.close();
+ path = tmpPath;
+ }
+
+ let encoder = new TextEncoder();
+ let array = encoder.encode(contents);
+
+ const count = await OS.File.writeAtomic(path, array, {
+ encoding: "utf-8",
+ tmpPath: path + ".tmp",
+ });
+ equal(count, contents.length, "All data has been written to file");
+
+ const file = await OS.File.open(path);
+
+ // Automatically remove the file once the test has finished
+ if (remove) {
+ registerCleanupFunction(async () => {
+ await file.close();
+ await OS.File.remove(path, { ignoreAbsent: true });
+ });
+ }
+
+ return { file, path };
+}
diff --git a/remote/test/unit/test_Sync.js b/remote/test/unit/test_Sync.js
new file mode 100644
index 0000000000..36150c4cc3
--- /dev/null
+++ b/remote/test/unit/test_Sync.js
@@ -0,0 +1,230 @@
+/* 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/. */
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const { PollPromise } = ChromeUtils.import("chrome://remote/content/Sync.jsm");
+
+/**
+ * Mimic a DOM node for listening for events.
+ */
+class MockElement {
+ constructor() {
+ this.capture = false;
+ this.func = null;
+ this.eventName = null;
+ this.untrusted = false;
+ }
+
+ addEventListener(name, func, capture, untrusted) {
+ this.eventName = name;
+ this.func = func;
+ if (capture != null) {
+ this.capture = capture;
+ }
+ if (untrusted != null) {
+ this.untrusted = untrusted;
+ }
+ }
+
+ click() {
+ if (this.func) {
+ let details = {
+ capture: this.capture,
+ target: this,
+ type: this.eventName,
+ untrusted: this.untrusted,
+ };
+ this.func(details);
+ }
+ }
+
+ removeEventListener(name, func) {
+ this.capture = false;
+ this.func = null;
+ this.eventName = null;
+ this.untrusted = false;
+ }
+}
+
+/**
+ * Mimic a message manager for sending messages.
+ */
+class MessageManager {
+ constructor() {
+ this.func = null;
+ this.message = null;
+ }
+
+ addMessageListener(message, func) {
+ this.func = func;
+ this.message = message;
+ }
+
+ removeMessageListener(message) {
+ this.func = null;
+ this.message = null;
+ }
+
+ send(message, data) {
+ if (this.func) {
+ this.func({
+ data,
+ message,
+ target: this,
+ });
+ }
+ }
+}
+
+/**
+ * Mimics nsITimer, but instead of using a system clock you can
+ * preprogram it to invoke the callback after a given number of ticks.
+ */
+class MockTimer {
+ constructor(ticksBeforeFiring) {
+ this.goal = ticksBeforeFiring;
+ this.ticks = 0;
+ this.cancelled = false;
+ }
+
+ initWithCallback(cb, timeout, type) {
+ this.ticks++;
+ if (this.ticks >= this.goal) {
+ cb();
+ }
+ }
+
+ cancel() {
+ this.cancelled = true;
+ }
+}
+
+add_test(function test_executeSoon_callback() {
+ // executeSoon() is already defined for xpcshell in head.js. As such import
+ // our implementation into a custom namespace.
+ let sync = {};
+ ChromeUtils.import("chrome://remote/content/Sync.jsm", sync);
+
+ for (let func of ["foo", null, true, [], {}]) {
+ Assert.throws(() => sync.executeSoon(func), /TypeError/);
+ }
+
+ let a;
+ sync.executeSoon(() => {
+ a = 1;
+ });
+ executeSoon(() => equal(1, a));
+
+ run_next_test();
+});
+
+add_test(function test_PollPromise_funcTypes() {
+ for (let type of ["foo", 42, null, undefined, true, [], {}]) {
+ Assert.throws(() => new PollPromise(type), /TypeError/);
+ }
+ new PollPromise(() => {});
+ new PollPromise(function() {});
+
+ run_next_test();
+});
+
+add_test(function test_PollPromise_timeoutTypes() {
+ for (let timeout of ["foo", true, [], {}]) {
+ Assert.throws(() => new PollPromise(() => {}, { timeout }), /TypeError/);
+ }
+ for (let timeout of [1.2, -1]) {
+ Assert.throws(() => new PollPromise(() => {}, { timeout }), /RangeError/);
+ }
+ for (let timeout of [null, undefined, 42]) {
+ new PollPromise(resolve => resolve(1), { timeout });
+ }
+
+ run_next_test();
+});
+
+add_test(function test_PollPromise_intervalTypes() {
+ for (let interval of ["foo", null, true, [], {}]) {
+ Assert.throws(() => new PollPromise(() => {}, { interval }), /TypeError/);
+ }
+ for (let interval of [1.2, -1]) {
+ Assert.throws(() => new PollPromise(() => {}, { interval }), /RangeError/);
+ }
+ new PollPromise(() => {}, { interval: 42 });
+
+ run_next_test();
+});
+
+add_task(async function test_PollPromise_retvalTypes() {
+ for (let typ of [true, false, "foo", 42, [], {}]) {
+ strictEqual(typ, await new PollPromise(resolve => resolve(typ)));
+ }
+});
+
+add_task(async function test_PollPromise_rethrowError() {
+ let nevals = 0;
+ let err;
+ try {
+ await PollPromise(() => {
+ ++nevals;
+ throw new Error();
+ });
+ } catch (e) {
+ err = e;
+ }
+ equal(1, nevals);
+ ok(err instanceof Error);
+});
+
+add_task(async function test_PollPromise_noTimeout() {
+ let nevals = 0;
+ await new PollPromise((resolve, reject) => {
+ ++nevals;
+ nevals < 100 ? reject() : resolve();
+ });
+ equal(100, nevals);
+});
+
+add_task(async function test_PollPromise_zeroTimeout() {
+ // run at least once when timeout is 0
+ let nevals = 0;
+ let start = new Date().getTime();
+ await new PollPromise(
+ (resolve, reject) => {
+ ++nevals;
+ reject();
+ },
+ { timeout: 0 }
+ );
+ let end = new Date().getTime();
+ equal(1, nevals);
+ less(end - start, 500);
+});
+
+add_task(async function test_PollPromise_timeoutElapse() {
+ let nevals = 0;
+ let start = new Date().getTime();
+ await new PollPromise(
+ (resolve, reject) => {
+ ++nevals;
+ reject();
+ },
+ { timeout: 100 }
+ );
+ let end = new Date().getTime();
+ lessOrEqual(nevals, 11);
+ greaterOrEqual(end - start, 100);
+});
+
+add_task(async function test_PollPromise_interval() {
+ let nevals = 0;
+ await new PollPromise(
+ (resolve, reject) => {
+ ++nevals;
+ reject();
+ },
+ { timeout: 100, interval: 100 }
+ );
+ equal(2, nevals);
+});
diff --git a/remote/test/unit/xpcshell.ini b/remote/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..8e004e8dd2
--- /dev/null
+++ b/remote/test/unit/xpcshell.ini
@@ -0,0 +1,14 @@
+# 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/.
+
+[DEFAULT]
+skip-if = appname == "thunderbird"
+
+[test_Connection.js]
+[test_DomainCache.js]
+[test_Error.js]
+[test_Format.js]
+[test_Session.js]
+[test_StreamRegistry.js]
+[test_Sync.js]