summaryrefslogtreecommitdiffstats
path: root/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell')
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_arithmetic.js48
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_callbacks.js47
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_custom_types.js13
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js16
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js21
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js311
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js28
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js71
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js123
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.ini9
10 files changed, 687 insertions, 0 deletions
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_arithmetic.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_arithmetic.js
new file mode 100644
index 0000000000..a221abc39f
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_arithmetic.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Arithmetic = ChromeUtils.importESModule(
+ "resource://gre/modules/RustArithmetic.sys.mjs"
+);
+
+add_task(async function () {
+ Assert.ok(Arithmetic.IntegerOverflow);
+ Assert.equal(await Arithmetic.add(2, 4), 6);
+ Assert.equal(await Arithmetic.add(4, 8), 12);
+ // For other backends we would have this test:
+ // await Assert.rejects(
+ // Arithmetic.add(18446744073709551615, 1),
+ // Arithmetic.IntegerOverflow,
+ // "add() should throw IntegerOverflow")
+ //
+ // However, this doesn't work because JS number values are actually 64-bit
+ // floats, and that number is greater than the maximum "safe" integer.
+ //
+ // Instead, let's test that we reject numbers that are that big
+ await Assert.rejects(
+ Arithmetic.add(Number.MAX_SAFE_INTEGER + 1, 0),
+ /TypeError/,
+ "add() should throw TypeError when an input is > MAX_SAFE_INTEGER"
+ );
+
+ Assert.equal(await Arithmetic.sub(4, 2), 2);
+ Assert.equal(await Arithmetic.sub(8, 4), 4);
+ await Assert.rejects(
+ Arithmetic.sub(0, 1),
+ Arithmetic.IntegerOverflow,
+ "sub() should throw IntegerOverflow"
+ );
+
+ Assert.equal(await Arithmetic.div(8, 4), 2);
+ // Can't test this, because we don't allow Rust panics in FF
+ // Assert.rejects(
+ // Arithmetic.div(8, 0),
+ // (e) => Assert.equal(e, Arithmetic.UniFFIInternalError),
+ // "Divide by 0 should throw UniFFIInternalError")
+ //
+ Assert.ok(await Arithmetic.equal(2, 2));
+ Assert.ok(await Arithmetic.equal(4, 4));
+
+ Assert.ok(!(await Arithmetic.equal(2, 4)));
+ Assert.ok(!(await Arithmetic.equal(4, 8)));
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_callbacks.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_callbacks.js
new file mode 100644
index 0000000000..67f0a167a8
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_callbacks.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { logEvenNumbers, logEvenNumbersMainThread } = ChromeUtils.importESModule(
+ "resource://gre/modules/RustFixtureCallbacks.sys.mjs"
+);
+
+class Logger {
+ constructor() {
+ this.messages = [];
+ this.finishedPromise = new Promise((resolve, reject) => {
+ this.finishedResolve = resolve;
+ this.finishedReject = reject;
+ });
+ }
+
+ log(message) {
+ this.messages.push(message);
+ }
+
+ finished() {
+ this.finishedResolve(true);
+ }
+
+ async waitForFinish() {
+ // Set a timeout to avoid hanging the tests if the Rust code fails to call finished().
+ do_timeout(2000, () =>
+ this.finishedReject("Timeout waiting for finished()")
+ );
+ return this.finishedPromise;
+ }
+}
+
+add_task(async function testLogEvenNumbers() {
+ async function runTest(logEvenNumbersFunc) {
+ const logger = new Logger();
+ logEvenNumbersFunc(logger, [1, 1, 2, 3, 5, 8, 13]);
+ await logger.waitForFinish();
+ Assert.deepEqual(logger.messages, [
+ "Saw even number: 2",
+ "Saw even number: 8",
+ ]);
+ }
+
+ await runTest(logEvenNumbers);
+ await runTest(logEvenNumbersMainThread);
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_custom_types.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_custom_types.js
new file mode 100644
index 0000000000..cd26467494
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_custom_types.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const CustomTypes = ChromeUtils.importESModule(
+ "resource://gre/modules/RustCustomTypes.sys.mjs"
+);
+
+add_task(async function () {
+ // JS right now doesn't treat custom types as anything but it's native counterparts
+ let demo = await CustomTypes.getCustomTypesDemo();
+ Assert.equal(demo.url, "http://example.com/");
+ Assert.equal(demo.handle, 123);
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js
new file mode 100644
index 0000000000..e2f985cde7
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_external_types.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const ExternalTypes = ChromeUtils.importESModule(
+ "resource://gre/modules/RustExternalTypes.sys.mjs"
+);
+
+add_task(async function () {
+ const line = new ExternalTypes.Line(
+ new ExternalTypes.Point(0, 0, "p1"),
+ new ExternalTypes.Point(2, 1, "p2")
+ );
+ Assert.equal(await ExternalTypes.gradient(line), 0.5);
+
+ Assert.equal(await ExternalTypes.gradient(null), 0.0);
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js
new file mode 100644
index 0000000000..f06ecd46aa
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_geometry.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Geometry = ChromeUtils.importESModule(
+ "resource://gre/modules/RustGeometry.sys.mjs"
+);
+
+add_task(async function () {
+ const ln1 = new Geometry.Line(
+ new Geometry.Point(0, 0, "p1"),
+ new Geometry.Point(1, 2, "p2")
+ );
+ const ln2 = new Geometry.Line(
+ new Geometry.Point(1, 1, "p3"),
+ new Geometry.Point(2, 2, "p4")
+ );
+ const origin = new Geometry.Point(0, 0);
+ Assert.ok((await Geometry.intersection(ln1, ln2)).equals(origin));
+ Assert.deepEqual(await Geometry.intersection(ln1, ln2), origin);
+ Assert.strictEqual(await Geometry.intersection(ln1, ln1), null);
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js
new file mode 100644
index 0000000000..8c673b2e92
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_rondpoint.js
@@ -0,0 +1,311 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Rondpoint = ChromeUtils.importESModule(
+ "resource://gre/modules/RustRondpoint.sys.mjs"
+);
+
+const {
+ Dictionnaire,
+ Enumeration,
+ copieDictionnaire,
+ copieEnumeration,
+ copieEnumerations,
+ copieCarte,
+ EnumerationAvecDonnees,
+ switcheroo,
+ Retourneur,
+ DictionnaireNombresSignes,
+ DictionnaireNombres,
+ Stringifier,
+ Optionneur,
+ OptionneurDictionnaire,
+} = Rondpoint;
+add_task(async function () {
+ const dico = new Dictionnaire(Enumeration.DEUX, true, 0, 1235);
+ const copyDico = await copieDictionnaire(dico);
+ Assert.deepEqual(dico, copyDico);
+
+ Assert.equal(await copieEnumeration(Enumeration.DEUX), Enumeration.DEUX);
+ Assert.deepEqual(
+ await copieEnumerations([Enumeration.UN, Enumeration.DEUX]),
+ [Enumeration.UN, Enumeration.DEUX]
+ );
+ const obj = {
+ 0: new EnumerationAvecDonnees.Zero(),
+ 1: new EnumerationAvecDonnees.Un(1),
+ 2: new EnumerationAvecDonnees.Deux(2, "deux"),
+ };
+
+ Assert.deepEqual(await copieCarte(obj), obj);
+
+ const zero = new EnumerationAvecDonnees.Zero();
+ const one = new EnumerationAvecDonnees.Un(1);
+ const two = new EnumerationAvecDonnees.Deux(2);
+ Assert.notEqual(zero, one);
+ Assert.notEqual(one, two);
+
+ Assert.deepEqual(zero, new EnumerationAvecDonnees.Zero());
+ Assert.deepEqual(one, new EnumerationAvecDonnees.Un(1));
+ Assert.notDeepEqual(one, new EnumerationAvecDonnees.Un(4));
+
+ Assert.ok(await switcheroo(false));
+ // Test the roundtrip across the FFI.
+ // This shows that the values we send come back in exactly the same state as we sent them.
+ // i.e. it shows that lowering from JS and lifting into rust is symmetrical with
+ // lowering from rust and lifting into JS.
+
+ const rt = await Retourneur.init();
+
+ const affirmAllerRetour = async (arr, fn, equalFn) => {
+ for (const member of arr) {
+ if (equalFn) {
+ equalFn(await fn(member), member);
+ } else {
+ Assert.equal(await fn(member), member);
+ }
+ }
+ };
+
+ // Booleans
+ await affirmAllerRetour([true, false], rt.identiqueBoolean.bind(rt));
+
+ // Bytes
+ await affirmAllerRetour([-128, 127], rt.identiqueI8.bind(rt));
+ await affirmAllerRetour([0, 0xff], rt.identiqueU8.bind(rt));
+
+ // Shorts
+ await affirmAllerRetour([-32768, 32767], rt.identiqueI16.bind(rt));
+ await affirmAllerRetour([0, 0xffff], rt.identiqueU16.bind(rt));
+
+ // Ints
+ await affirmAllerRetour(
+ [0, 1, -1, -2147483648, 2147483647],
+ rt.identiqueI32.bind(rt)
+ );
+ await affirmAllerRetour([0, 0xffffffff], rt.identiqueU32.bind(rt));
+
+ // Longs
+ // NOTE: we cannot represent greater than `Number.MAX_SAFE_INTEGER`
+ await affirmAllerRetour(
+ [0, 1, -1, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER],
+ rt.identiqueI64.bind(rt)
+ );
+ await affirmAllerRetour(
+ [0, Number.MAX_SAFE_INTEGER],
+ rt.identiqueU64.bind(rt)
+ );
+
+ // Floats
+ const equalFloats = (a, b) => Assert.ok(Math.abs(a - b) <= Number.EPSILON);
+ await affirmAllerRetour(
+ [0.0, 0.5, 0.25, 1.5],
+ rt.identiqueFloat.bind(rt),
+ equalFloats
+ );
+ // Some float value's precision gets messed up, an example is 3.22, 100.223, etc
+ // await affirmAllerRetour([0.0, 0.5, 0.25, 1.5, 100.223], rt.identiqueFloat.bind(rt), equalFloats);
+
+ // Double (although on the JS side doubles are limited since they are also represented by Number)
+ await affirmAllerRetour(
+ [0.0, 0.5, 0.25, 1.5],
+ rt.identiqueDouble.bind(rt),
+ equalFloats
+ );
+
+ // Strings
+ await affirmAllerRetour(
+ [
+ "",
+ "abc",
+ "null\u0000byte",
+ "été",
+ "ښي لاس ته لوستلو لوستل",
+ "😻emoji 👨‍👧‍👦multi-emoji, 🇨🇭a flag, a canal, panama",
+ ],
+ rt.identiqueString.bind(rt)
+ );
+
+ await affirmAllerRetour(
+ [-1, 0, 1].map(n => new DictionnaireNombresSignes(n, n, n, n)),
+ rt.identiqueNombresSignes.bind(rt),
+ (a, b) => Assert.deepEqual(a, b)
+ );
+
+ await affirmAllerRetour(
+ [0, 1].map(n => new DictionnaireNombres(n, n, n, n)),
+ rt.identiqueNombres.bind(rt),
+ (a, b) => Assert.deepEqual(a, b)
+ );
+
+ // Test one way across the FFI.
+ //
+ // We send one representation of a value to lib.rs, and it transforms it into another, a string.
+ // lib.rs sends the string back, and then we compare here in js.
+ //
+ // This shows that the values are transformed into strings the same way in both js and rust.
+ // i.e. if we assume that the string return works (we test this assumption elsewhere)
+ // we show that lowering from js and lifting into rust has values that both js and rust
+ // both stringify in the same way. i.e. the same values.
+ //
+ // If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
+ // and this convinces us that lowering/lifting from here to rust is correct, then
+ // together, we've shown the correctness of the return leg.
+ const st = await Stringifier.init();
+
+ const affirmEnchaine = async (arr, fn) => {
+ for (const member of arr) {
+ Assert.equal(await fn(member), String(member));
+ }
+ };
+
+ // Booleans
+ await affirmEnchaine([true, false], st.toStringBoolean.bind(st));
+
+ // Bytes
+ await affirmEnchaine([-128, 127], st.toStringI8.bind(st));
+ await affirmEnchaine([0, 0xff], st.toStringU8.bind(st));
+
+ // Shorts
+ await affirmEnchaine([-32768, 32767], st.toStringI16.bind(st));
+ await affirmEnchaine([0, 0xffff], st.toStringU16.bind(st));
+
+ // Ints
+ await affirmEnchaine(
+ [0, 1, -1, -2147483648, 2147483647],
+ st.toStringI32.bind(st)
+ );
+ await affirmEnchaine([0, 0xffffffff], st.toStringU32.bind(st));
+
+ // Longs
+ // NOTE: we cannot represent greater than `Number.MAX_SAFE_INTEGER`
+ await affirmEnchaine(
+ [0, 1, -1, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER],
+ st.toStringI64.bind(st)
+ );
+ await affirmEnchaine([0, Number.MAX_SAFE_INTEGER], st.toStringU64.bind(st));
+
+ // Floats
+ await affirmEnchaine([0.0, 0.5, 0.25, 1.5], st.toStringFloat.bind(st));
+
+ // Doubles
+ await affirmEnchaine([0.0, 0.5, 0.25, 1.5], st.toStringDouble.bind(st));
+
+ // Prove to ourselves that default arguments are being used.
+ // Step 1: call the methods without arguments, and check against the UDL.
+ const op = await Optionneur.init();
+
+ Assert.equal(await op.sinonString(), "default");
+
+ Assert.ok(!(await op.sinonBoolean()));
+
+ Assert.deepEqual(await op.sinonSequence(), []);
+
+ Assert.equal(await op.sinonNull(), null);
+ Assert.equal(await op.sinonZero(), 0);
+
+ // decimal integers
+ Assert.equal(await op.sinonI8Dec(), -42);
+ Assert.equal(await op.sinonU8Dec(), 42);
+ Assert.equal(await op.sinonI16Dec(), 42);
+ Assert.equal(await op.sinonU16Dec(), 42);
+ Assert.equal(await op.sinonI32Dec(), 42);
+ Assert.equal(await op.sinonU32Dec(), 42);
+ Assert.equal(await op.sinonI64Dec(), 42);
+ Assert.equal(await op.sinonU64Dec(), 42);
+
+ // hexadecimal integers
+ Assert.equal(await op.sinonI8Hex(), -0x7f);
+ Assert.equal(await op.sinonU8Hex(), 0xff);
+ Assert.equal(await op.sinonI16Hex(), 0x7f);
+ Assert.equal(await op.sinonU16Hex(), 0xffff);
+ Assert.equal(await op.sinonI32Hex(), 0x7fffffff);
+ Assert.equal(await op.sinonU32Hex(), 0xffffffff);
+ // The following are too big to be represented by js `Number`
+ // Assert.equal(await op.sinonI64Hex(), 0x7fffffffffffffff);
+ // Assert.equal(await op.sinonU64Hex(), 0xffffffffffffffff);
+
+ // octal integers
+ Assert.equal(await op.sinonU32Oct(), 0o755);
+
+ // floats
+ Assert.equal(await op.sinonF32(), 42.0);
+ Assert.equal(await op.sinonF64(), 42.1);
+
+ // enums
+ Assert.equal(await op.sinonEnum(), Enumeration.TROIS);
+
+ // Step 2. Convince ourselves that if we pass something else, then that changes the output.
+ // We have shown something coming out of the sinon methods, but without eyeballing the Rust
+ // we can't be sure that the arguments will change the return value.
+
+ await affirmAllerRetour(["foo", "bar"], op.sinonString.bind(op));
+ await affirmAllerRetour([true, false], op.sinonBoolean.bind(op));
+ await affirmAllerRetour([["a", "b"], []], op.sinonSequence.bind(op), (a, b) =>
+ Assert.deepEqual(a, b)
+ );
+
+ // Optionals
+ await affirmAllerRetour(["0", "1"], op.sinonNull.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonZero.bind(op));
+
+ // integers
+ await affirmAllerRetour([0, 1], op.sinonU8Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI8Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU16Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI16Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU32Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI32Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU64Dec.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI64Dec.bind(op));
+
+ await affirmAllerRetour([0, 1], op.sinonU8Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI8Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU16Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI16Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU32Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI32Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU64Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonI64Hex.bind(op));
+ await affirmAllerRetour([0, 1], op.sinonU32Oct.bind(op));
+
+ // Floats
+ await affirmAllerRetour([0.0, 1.0], op.sinonF32.bind(op));
+ await affirmAllerRetour([0.0, 1.0], op.sinonF64.bind(op));
+
+ // enums
+ await affirmAllerRetour(
+ [Enumeration.UN, Enumeration.DEUX, Enumeration.TROIS],
+ op.sinonEnum.bind(op)
+ );
+
+ // Testing defaulting properties in record types.
+ const defaultes = new OptionneurDictionnaire();
+ const explicite = new OptionneurDictionnaire(
+ -8,
+ 8,
+ -16,
+ 0x10,
+ -32,
+ 32,
+ -64,
+ 64,
+ 4.0,
+ 8.0,
+ true,
+ "default",
+ [],
+ Enumeration.DEUX,
+ null
+ );
+
+ Assert.deepEqual(defaultes, explicite);
+
+ // …and makes sure they travel across and back the FFI.
+
+ await affirmAllerRetour(
+ [defaultes, explicite],
+ rt.identiqueOptionneurDictionnaire.bind(rt),
+ (a, b) => Assert.deepEqual(a, b)
+ );
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js
new file mode 100644
index 0000000000..3feb2fd34d
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_sprites.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Sprites = ChromeUtils.importESModule(
+ "resource://gre/modules/RustSprites.sys.mjs"
+);
+
+add_task(async function () {
+ Assert.ok(Sprites.Sprite);
+
+ const sempty = await Sprites.Sprite.init(null);
+ Assert.deepEqual(await sempty.getPosition(), new Sprites.Point(0, 0));
+
+ const s = await Sprites.Sprite.init(new Sprites.Point(0, 1));
+ Assert.deepEqual(await s.getPosition(), new Sprites.Point(0, 1));
+
+ s.moveTo(new Sprites.Point(1, 2));
+ Assert.deepEqual(await s.getPosition(), new Sprites.Point(1, 2));
+
+ s.moveBy(new Sprites.Vector(-4, 2));
+ Assert.deepEqual(await s.getPosition(), new Sprites.Point(-3, 4));
+
+ const srel = await Sprites.Sprite.newRelativeTo(
+ new Sprites.Point(0, 1),
+ new Sprites.Vector(1, 1.5)
+ );
+ Assert.deepEqual(await srel.getPosition(), new Sprites.Point(1, 2.5));
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js
new file mode 100644
index 0000000000..dac26d2be1
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_todolist.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { TodoList, TodoEntry, getDefaultList, setDefaultList } =
+ ChromeUtils.importESModule("resource://gre/modules/RustTodolist.sys.mjs");
+
+add_task(async function () {
+ const todo = await TodoList.init();
+ const entry = new TodoEntry("Write bindings for strings in records");
+
+ await todo.addItem("Write JS bindings");
+ Assert.equal(await todo.getLast(), "Write JS bindings");
+
+ await todo.addItem("Write tests for bindings");
+ Assert.equal(await todo.getLast(), "Write tests for bindings");
+
+ await todo.addEntry(entry);
+ Assert.equal(await todo.getLast(), "Write bindings for strings in records");
+ Assert.equal(
+ (await todo.getLastEntry()).text,
+ "Write bindings for strings in records"
+ );
+ Assert.ok((await todo.getLastEntry()).equals(entry));
+
+ await todo.addItem(
+ "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
+ );
+ Assert.equal(
+ await todo.getLast(),
+ "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
+ );
+
+ const entry2 = new TodoEntry(
+ "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
+ );
+ await todo.addEntry(entry2);
+ Assert.equal(
+ (await todo.getLastEntry()).text,
+ "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
+ );
+
+ const todo2 = await TodoList.init();
+ Assert.notEqual(todo, todo2);
+ Assert.notStrictEqual(todo, todo2);
+
+ Assert.strictEqual(await getDefaultList(), null);
+
+ await setDefaultList(todo);
+ Assert.deepEqual(
+ await todo.getItems(),
+ await (await getDefaultList()).getItems()
+ );
+
+ todo2.makeDefault();
+ Assert.deepEqual(
+ await todo2.getItems(),
+ await (await getDefaultList()).getItems()
+ );
+
+ await todo.addItem("Test liveness after being demoted from default");
+ Assert.equal(
+ await todo.getLast(),
+ "Test liveness after being demoted from default"
+ );
+
+ todo2.addItem("Test shared state through local vs default reference");
+ Assert.equal(
+ await (await getDefaultList()).getLast(),
+ "Test shared state through local vs default reference"
+ );
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js
new file mode 100644
index 0000000000..bfeb07c82b
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/test_type_checking.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Arithmetic = ChromeUtils.importESModule(
+ "resource://gre/modules/RustArithmetic.sys.mjs"
+);
+const Geometry = ChromeUtils.importESModule(
+ "resource://gre/modules/RustGeometry.sys.mjs"
+);
+const TodoList = ChromeUtils.importESModule(
+ "resource://gre/modules/RustTodolist.sys.mjs"
+);
+const Rondpoint = ChromeUtils.importESModule(
+ "resource://gre/modules/RustRondpoint.sys.mjs"
+);
+const { UniFFITypeError } = ChromeUtils.importESModule(
+ "resource://gre/modules/UniFFI.sys.mjs"
+);
+
+add_task(async function testFunctionArguments() {
+ await Assert.rejects(
+ Arithmetic.add(2),
+ UniFFITypeError,
+ "add() call missing argument"
+ );
+ Assert.throws(
+ () => new Geometry.Point(0.0),
+ UniFFITypeError,
+ "Point constructor missing argument"
+ );
+});
+
+add_task(async function testObjectPointers() {
+ const todo = await TodoList.TodoList.init();
+ const stringifier = await Rondpoint.Stringifier.init();
+ await todo.getEntries(); // OK
+ todo[TodoList.UnitTestObjs.uniffiObjectPtr] =
+ stringifier[Rondpoint.UnitTestObjs.uniffiObjectPtr];
+
+ await Assert.rejects(
+ todo.getEntries(), // the pointer is incorrect, should throw
+ /Bad pointer type/,
+ "getEntries() with wrong pointer type"
+ );
+});
+
+add_task(async function testEnumTypeCheck() {
+ await Assert.rejects(
+ Rondpoint.copieEnumeration("invalid"), // Not an integer value
+ /e:/, // Ensure exception message includes the argument name
+ "copieEnumeration() with non-Enumeration value should throw"
+ );
+ await Assert.rejects(
+ Rondpoint.copieEnumeration(0), // Integer, but doesn't map to a variant
+ /e:/, // Ensure exception message includes the argument name
+ "copieEnumeration() with non-Enumeration value should throw"
+ );
+ await Assert.rejects(
+ Rondpoint.copieEnumeration(4), // Integer, but doesn't map to a variant
+ /e:/, // Ensure exception message includes the argument name
+ "copieEnumeration() with non-Enumeration value should throw"
+ );
+});
+
+add_task(async function testRecordTypeCheck() {
+ await Assert.rejects(
+ Geometry.gradient(123), // Not a Line object
+ UniFFITypeError,
+ "gradient with non-Line object should throw"
+ );
+
+ await Assert.rejects(
+ Geometry.gradient({
+ start: {
+ coordX: 0.0,
+ coordY: 0.0,
+ },
+ // missing the end field
+ }),
+ /ln.end/, // Ensure exception message includes the argument name
+ "gradient with Line object with missing end field should throw"
+ );
+});
+
+add_task(async function testOptionTypeCheck() {
+ const optionneur = await Rondpoint.Optionneur.init();
+ await Assert.rejects(
+ optionneur.sinonNull(0),
+ UniFFITypeError,
+ "sinonNull with non-string should throw"
+ );
+});
+
+add_task(async function testSequenceTypeCheck() {
+ const todo = await TodoList.TodoList.init();
+ await Assert.rejects(
+ todo.addEntries("not a list"),
+ UniFFITypeError,
+ "addEntries with non-list should throw"
+ );
+
+ await Assert.rejects(
+ todo.addEntries(["not TodoEntry"]),
+ /entries\[0]/,
+ "addEntries with non TodoEntry item should throw"
+ );
+});
+
+add_task(async function testMapTypeCheck() {
+ await Assert.rejects(
+ Rondpoint.copieCarte("not a map"),
+ UniFFITypeError,
+ "copieCarte with a non-map should throw"
+ );
+
+ await Assert.rejects(
+ Rondpoint.copieCarte({ x: 1 }),
+ /c\[x]/,
+ "copieCarte with a wrong value type should throw"
+ );
+
+ // TODO: test key types once we implement https://bugzilla.mozilla.org/show_bug.cgi?id=1809459
+});
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.ini b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.ini
new file mode 100644
index 0000000000..87d2b43489
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/fixtures/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,9 @@
+[test_arithmetic.js]
+[test_callbacks.js]
+[test_geometry.js]
+[test_rondpoint.js]
+[test_sprites.js]
+[test_todolist.js]
+[test_type_checking.js]
+[test_custom_types.js]
+[test_external_types.js]