/* 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 = Promise.withResolvers();
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"));
});