diff options
Diffstat (limited to 'toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js')
-rw-r--r-- | toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js b/toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js new file mode 100644 index 0000000000..a4b0f4834f --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_sqlite_shutdown.js @@ -0,0 +1,124 @@ +/* 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")); +}); |