/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { AsyncShutdown } = ChromeUtils.importESModule( "resource://gre/modules/AsyncShutdown.sys.mjs" ); function getConnection(dbName, extraOptions = {}) { let path = dbName + ".sqlite"; let options = { path }; for (let [k, v] of Object.entries(extraOptions)) { options[k] = v; } return Sqlite.openConnection(options); } async function getDummyDatabase(name, extraOptions = {}) { const TABLES = { dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT", files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT", }; let c = await getConnection(name, extraOptions); c._initialStatementCount = 0; for (let [k, v] of Object.entries(TABLES)) { await c.execute("CREATE TABLE " + k + "(" + v + ")"); c._initialStatementCount++; } return c; } function sleep(ms) { return new Promise(resolve => { let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); timer.initWithCallback( { notify() { resolve(); }, }, ms, timer.TYPE_ONE_SHOT ); }); } // // ----------- Don't add a test after this one, as it shuts down Sqlite.sys.mjs // add_task(async function test_shutdown_clients() { info("Ensuring that Sqlite.sys.mjs doesn't shutdown before its clients"); let assertions = []; let sleepStarted = false; let sleepComplete = false; Sqlite.shutdown.addBlocker( "test_sqlite.js shutdown blocker (sleep)", async function () { sleepStarted = true; await sleep(100); sleepComplete = true; } ); assertions.push({ name: "sleepStarted", value: () => sleepStarted }); assertions.push({ name: "sleepComplete", value: () => sleepComplete }); Sqlite.shutdown.addBlocker( "test_sqlite.js shutdown blocker (immediate)", true ); let dbOpened = false; let dbClosed = false; Sqlite.shutdown.addBlocker( "test_sqlite.js shutdown blocker (open a connection during shutdown)", async function () { let db = await getDummyDatabase("opened during shutdown"); dbOpened = true; db.close().then(() => (dbClosed = true)); // Don't wait for this task to complete, Sqlite.sys.mjs must wait automatically } ); assertions.push({ name: "dbOpened", value: () => dbOpened }); assertions.push({ name: "dbClosed", value: () => dbClosed }); info("Now shutdown Sqlite.sys.mjs synchronously"); Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); // Check opening a connection during shutdown fails. let deferred = PromiseUtils.defer(); let conn = Sqlite.openConnection({ path: PathUtils.join(PathUtils.profileDir, "test_shutdown.sqlite"), testDelayedOpenPromise: deferred.promise, }); await AsyncShutdown.profileBeforeChange._trigger(); deferred.resolve(); await Assert.rejects( conn, /has been shutdown/, "Should close the connection and not block" ); Services.prefs.clearUserPref("toolkit.asyncshutdown.testing"); for (let { name, value } of assertions) { Assert.ok(value(), "Checking: " + name); } info("Ensure that we cannot open databases anymore"); let exn; try { await getDummyDatabase("opened after shutdown"); } catch (ex) { exn = ex; } Assert.ok(!!exn, `exception: ${exn.message}`); Assert.ok(exn.message.includes("Sqlite.sys.mjs has been shutdown")); });