// Enable the collection (during test) for all products so even products // that don't collect the data will be able to run the test without failure.
Services.prefs.setBoolPref( "toolkit.telemetry.testing.overrideProductsCheck", true
);
function sleep(ms) { returnnew Promise(resolve => {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
// When testing finalization, use this to tell Sqlite.sys.mjs to not throw // an uncatchable `Promise.reject` function failTestsOnAutoClose(enabled) {
Sqlite.failTestsOnAutoClose(enabled);
}
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 = {}) {
let c = await getConnection(name, extraOptions);
c._initialStatementCount = 0;
if (!extraOptions.readOnly) { const TABLES = new Map([
["dirs", "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT"],
[ "files", "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
],
]); for (let [k, v] of TABLES) {
await c.execute("CREATE TABLE " + k + "(" + v + ")");
c._initialStatementCount++;
}
}
add_task(async function test_schema_version() {
let db = await getDummyDatabase("schema_version");
let version = await db.getSchemaVersion(); Assert.strictEqual(version, 0);
db.setSchemaVersion(14);
version = await db.getSchemaVersion(); Assert.strictEqual(version, 14);
for (let v of [0.5, "foobar", NaN]) {
let success; try {
await db.setSchemaVersion(v);
info("Schema version " + v + " should have been rejected");
success = false;
} catch (ex) { if (!ex.message.startsWith("Schema version must be an integer.")) { throw ex;
}
success = true;
} Assert.ok(success);
version = await db.getSchemaVersion(); Assert.strictEqual(version, 14);
}
await db.execute("ATTACH :memory AS attached");
let attachedVersion = await db.getSchemaVersion("attached"); Assert.equal(
attachedVersion,
0, "Should return 0 for initial attached schema version"
);
version = await db.getSchemaVersion(); Assert.equal(
version,
14, "Setting attached schema version should not change main schema version"
);
await db.setSchemaVersion(15);
attachedVersion = await db.getSchemaVersion("attached"); Assert.equal(
attachedVersion,
3, "Setting main schema version should not change attached schema version"
);
await db.close();
});
add_task(async function test_simple_insert() {
let c = await getDummyDatabase("simple_insert");
let result = await c.execute("INSERT INTO dirs VALUES (NULL, 'foo')"); Assert.ok(Array.isArray(result)); Assert.equal(result.length, 0);
await c.close();
});
add_task(async function test_simple_bound_array() {
let c = await getDummyDatabase("simple_bound_array");
let result = await c.execute("INSERT INTO dirs VALUES (?, ?)", [1, "foo"]); Assert.equal(result.length, 0);
await c.close();
});
add_task(async function test_simple_bound_object() {
let c = await getDummyDatabase("simple_bound_object");
let result = await c.execute("INSERT INTO dirs VALUES (:id, :path)", {
id: 1,
path: "foo",
}); Assert.equal(result.length, 0);
result = await c.execute("SELECT id, path FROM dirs"); Assert.equal(result.length, 1); Assert.equal(result[0].getResultByName("id"), 1); Assert.equal(result[0].getResultByName("path"), "foo");
await c.close();
});
// This is mostly a sanity test to ensure simple executions work.
add_task(async function test_simple_insert_then_select() {
let c = await getDummyDatabase("simple_insert_then_select");
await c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
await c.execute("INSERT INTO dirs (path) VALUES (?)", ["bar"]);
let result = await c.execute("SELECT * FROM dirs"); Assert.equal(result.length, 2);
await c.execute("CREATE INDEX my_index ON dirs (path)"); Assert.ok(await c.indexExists("my_index"));
await c.close();
});
add_task(async function test_close_cached() {
let c = await getDummyDatabase("close_cached");
await c.executeCached("SELECT * FROM dirs");
await c.executeCached("SELECT * FROM files");
await c.close();
});
add_task(async function test_execute_invalid_statement() {
let c = await getDummyDatabase("invalid_statement");
await new Promise(resolve => { Assert.equal(c._connectionData._anonymousStatements.size, 0);
c.execute("SELECT invalid FROM unknown").then(do_throw, function onError() {
resolve();
});
});
// Ensure we don't leak the statement instance. Assert.equal(c._connectionData._anonymousStatements.size, 0);
await c.close();
});
add_task(async function test_incorrect_like_bindings() {
let c = await getDummyDatabase("incorrect_like_bindings");
let sql = "select * from dirs where path LIKE 'non%'"; Assert.throws(() => c.execute(sql), /Please enter a LIKE clause/); Assert.throws(() => c.executeCached(sql), /Please enter a LIKE clause/);
await c.close();
});
add_task(async function test_on_row_exception_ignored() {
let c = await getDummyDatabase("on_row_exception_ignored");
let sql = "INSERT INTO dirs (path) VALUES (?)"; for (let i = 0; i < 10; i++) {
await c.executeCached(sql, ["dir" + i]);
}
let i = 0;
let hasResult = await c.execute("SELECT * FROM DIRS", null, function onRow() {
i++;
// Ensure StopIteration during onRow causes processing to stop.
add_task(async function test_on_row_stop_iteration() {
let c = await getDummyDatabase("on_row_stop_iteration");
let sql = "INSERT INTO dirs (path) VALUES (?)"; for (let i = 0; i < 10; i++) {
await c.executeCached(sql, ["dir" + i]);
}
let i = 0;
let hasResult = await c.execute( "SELECT * FROM dirs", null, function onRow(row, cancel) {
i++;
// Ensure execute resolves to false when no rows are selected.
add_task(async function test_on_row_stop_iteration() {
let c = await getDummyDatabase("no_on_row");
let i = 0;
let hasResult = await c.execute(
`SELECT * FROM dirs WHERE path="nonexistent"`, null, function onRow() {
i++;
}
);
add_task(async function test_invalid_transaction_type() {
let c = await getDummyDatabase("invalid_transaction_type");
Assert.throws(
() => c.executeTransaction(function () {}, "foobar"),
/Unknown transaction type/, "Unknown transaction type should throw"
);
await c.close();
});
add_task(async function test_execute_transaction_success() {
let c = await getDummyDatabase("execute_transaction_success");
Assert.ok(!c.transactionInProgress);
await c.executeTransaction(async function transaction(conn) { Assert.equal(c, conn); Assert.ok(conn.transactionInProgress);
await conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
});
Assert.ok(!c.transactionInProgress);
let rows = await c.execute("SELECT * FROM dirs"); Assert.ok(Array.isArray(rows)); Assert.equal(rows.length, 1);
await c.close();
});
add_task(async function test_execute_transaction_rollback() {
let c = await getDummyDatabase("execute_transaction_rollback");
let deferred = Promise.withResolvers();
c.executeTransaction(async function transaction(conn) {
await conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
print("Expecting error with next statement.");
await conn.execute("INSERT INTO invalid VALUES ('foo')");
// We should never get here.
do_throw();
}).then(do_throw, function onError() {
deferred.resolve();
});
await deferred.promise;
let rows = await c.execute("SELECT * FROM dirs"); Assert.equal(rows.length, 0);
await c.close();
});
add_task(async function test_close_during_transaction() {
let c = await getDummyDatabase("close_during_transaction");
await c.execute("INSERT INTO dirs (path) VALUES ('foo')");
let promise = c.executeTransaction(async function transaction() {
await c.execute("INSERT INTO dirs (path) VALUES ('bar')");
});
await c.close();
await Assert.rejects(
promise,
/Transaction canceled due to a closed connection/, "closing a connection in the middle of a transaction should reject it"
);
let c2 = await getConnection("close_during_transaction");
let rows = await c2.execute("SELECT * FROM dirs"); Assert.equal(rows.length, 1);
await c2.close();
});
// Verify that we support concurrent transactions.
add_task(async function test_multiple_transactions() {
let c = await getDummyDatabase("detect_multiple_transactions");
for (let i = 0; i < 10; ++i) { // We don't wait for these transactions.
c.executeTransaction(async function () {
await c.execute("INSERT INTO dirs (path) VALUES (:path)", {
path: `foo${i}`,
});
await c.execute("SELECT * FROM dirs");
});
} for (let i = 0; i < 10; ++i) {
await c.executeTransaction(async function () {
await c.execute("INSERT INTO dirs (path) VALUES (:path)", {
path: `bar${i}`,
});
await c.execute("SELECT * FROM dirs");
});
}
let rows = await c.execute("SELECT * FROM dirs"); Assert.equal(rows.length, 20);
await c.close();
});
// Verify that wrapped transactions ignore a BEGIN TRANSACTION failure, when // an externally opened transaction exists.
add_task(async function test_wrapped_connection_transaction() {
let file = new FileUtils.File(
PathUtils.join(PROFILE_DIR, "test_wrapStorageConnection.sqlite")
);
let c = await new Promise((resolve, reject) => {
Services.storage.openAsyncDatabase(
file, /* openFlags */ Ci.mozIStorageService.OPEN_DEFAULT, /* connectionFlags */ Ci.mozIStorageService.CONNECTION_DEFAULT,
(status, db) => { if (Components.isSuccessCode(status)) {
resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
} else {
reject(new Error(status));
}
}
);
});
let wrapper = await Sqlite.wrapStorageConnection({ connection: c }); // Start a transaction on the raw connection.
await c.executeSimpleSQLAsync("BEGIN"); // Now use executeTransaction, it will be executed, but not in a transaction.
await wrapper.executeTransaction(async function () {
await wrapper.execute( "CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)"
);
}); // This should not fail cause the internal transaction has not been created.
await c.executeSimpleSQLAsync("COMMIT");
await wrapper.execute("SELECT * FROM test");
// Closing the wrapper should just finalize statements but not close the // database.
await wrapper.close();
await c.asyncClose();
});
add_task(async function test_transaction_timeout() { // Lower the transactions timeout for the test.
let defaultTimeout = Sqlite.TRANSACTIONS_TIMEOUT_MS;
Sqlite.TRANSACTIONS_TIMEOUT_MS = 500;
Services.telemetry.clearScalars();
let myResolve = () => {}; try {
let c = await getDummyDatabase("transaction_timeout"); Assert.ok(!c.transactionInProgress, "Should not be in a transaction");
let promise = c.executeTransaction(async function transaction(conn) { // Make a change, we'll later check it is undone by ROLLBACK.
await conn.execute( "CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)"
); Assert.ok(c.transactionInProgress, "Should be in a transaction"); // Return a never fulfilled promise.
await new Promise(resolve => { // Keep this alive regardless GC, and clean it up in finally.
myResolve = resolve;
});
});
await Assert.rejects(
promise,
/Transaction timeout, most likely caused by unresolved pending work./, "A transaction timeout should reject it"
);
let rows = await c.execute("SELECT * FROM dirs"); Assert.equal(rows.length, 0, "Changes should have been rolled back");
await c.close();
add_task(async function test_idle_shrink_fires() {
let c = await getDummyDatabase("idle_shrink_fires", {
shrinkMemoryOnConnectionIdleMS: 200,
});
c._connectionData._clearIdleShrinkTimer();
let oldShrink = c._connectionData.shrinkMemory;
let shrinkPromises = [];
let count = 0;
Object.defineProperty(c._connectionData, "shrinkMemory", {
value() {
count++;
let promise = oldShrink.call(c._connectionData);
shrinkPromises.push(promise); return promise;
},
});
// We reset the idle shrink timer after monkeypatching because otherwise the // installed timer callback will reference the non-monkeypatched function.
c._connectionData._startIdleShrinkTimer();
let now = new Date();
c._connectionData._startIdleShrinkTimer();
let initialIdle = new Date(now.getTime() + INTERVAL);
// Perform database operations until initial scheduled time has been passed.
let i = 0; while (new Date() < initialIdle) {
await c.execute("INSERT INTO dirs (path) VALUES (?)", ["" + i]);
i++;
}
Assert.ok(i > 0);
// We should not have performed an idle while doing operations. Assert.equal(count, 0);
// Wait for idle timer.
await sleep(INTERVAL);
// Ensure we fired. Assert.equal(count, 1); Assert.equal(shrinkPromises.length, 1);
await shrinkPromises[0];
await c.close();
});
add_task(async function test_in_progress_counts() {
let c = await getDummyDatabase("in_progress_counts"); Assert.equal(c._connectionData._statementCounter, c._initialStatementCount); Assert.equal(c._connectionData._pendingStatements.size, 0);
await c.executeCached("INSERT INTO dirs (path) VALUES ('foo')"); Assert.equal(
c._connectionData._statementCounter,
c._initialStatementCount + 1
); Assert.equal(c._connectionData._pendingStatements.size, 0);
let expectOne;
let expectTwo;
// We want to make sure that two queries executing simultaneously // result in `_pendingStatements.size` reaching 2, then dropping back to 0. // // To do so, we kick off a second statement within the row handler // of the first, then wait for both to finish.
let inner = Promise.withResolvers();
await c.executeCached("SELECT * from dirs", null, function onRow() { // In the onRow handler, we're still an outstanding query. // Expect a single in-progress entry.
expectOne = c._connectionData._pendingStatements.size;
// Start another query, checking that after its statement has been created // there are two statements in progress.
c.executeCached("SELECT 10, path from dirs").then(inner.resolve);
expectTwo = c._connectionData._pendingStatements.size;
});
add_task(async function test_discard_while_active() {
let c = await getDummyDatabase("discard_while_active");
await c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
await c.executeCached("INSERT INTO dirs (path) VALUES ('bar')");
let discarded = -1;
let first = true;
let sql = "SELECT * FROM dirs";
await c.executeCached(sql, null, function onRow() { if (!first) { return;
}
first = false;
discarded = c.discardCachedStatements();
});
// We discarded everything, because the SELECT had already started to run. Assert.equal(3, discarded);
// And again is safe. Assert.equal(0, c.discardCachedStatements());
await c.close();
});
add_task(async function test_discard_cached() {
let c = await getDummyDatabase("discard_cached");
await c.executeCached("SELECT * from dirs"); Assert.equal(1, c._connectionData._cachedStatements.size);
await c.executeCached("SELECT * from files"); Assert.equal(2, c._connectionData._cachedStatements.size);
await c.executeCached("SELECT * from dirs"); Assert.equal(2, c._connectionData._cachedStatements.size);
let sql = "INSERT INTO dirs VALUES (:id, :path)";
await c.executeTransaction(async function transaction() {
let result = await c.execute(sql, bindings); Assert.equal(result.length, 0);
let rows = await c.executeCached("SELECT * from dirs"); Assert.equal(rows.length, 3);
});
// Transaction committed.
let rows = await c.executeCached("SELECT * from dirs"); Assert.equal(rows.length, 3);
await c.close();
});
add_task(
async function test_programmatic_binding_transaction_partial_rollback() {
let c = await getDummyDatabase( "programmatic_binding_transaction_partial_rollback"
);
// Add some data in an implicit transaction before beginning the batch insert.
await c.execute(sql, { id: 1, path: "works" });
let secondSucceeded = false; try {
await c.executeTransaction(async function transaction() { // Insert one row. This won't implicitly start a transaction.
await c.execute(sql, bindings[0]);
// Insert multiple rows. mozStorage will want to start a transaction. // One of the inserts will fail, so the transaction should be rolled back.
await c.execute(sql, bindings);
secondSucceeded = true;
});
} catch (ex) {
print("Caught expected exception: " + ex); Assert.greater(
ex.result,
0x80000000, "The ex.result value should be forwarded."
);
}
// We did not get to the end of our in-transaction block. Assert.ok(!secondSucceeded);
// Everything that happened in *our* transaction, not mozStorage's, got // rolled back, but the first row still exists.
let rows = await c.executeCached("SELECT * from dirs"); Assert.equal(rows.length, 1); Assert.equal(rows[0].getResultByName("path"), "works");
await c.close();
}
);
// Just like the previous test, but relying on the implicit // transaction established by mozStorage.
add_task(async function test_programmatic_binding_implicit_transaction() {
let c = await getDummyDatabase("programmatic_binding_implicit_transaction");
let sql = "INSERT INTO dirs VALUES (:id, :path)";
let secondSucceeded = false;
await c.execute(sql, { id: 1, path: "works" }); try {
await c.execute(sql, bindings);
secondSucceeded = true;
} catch (ex) {
print("Caught expected exception: " + ex); Assert.greater(
ex.result,
0x80000000, "The ex.result value should be forwarded."
);
}
Assert.ok(!secondSucceeded);
// The entire batch failed.
let rows = await c.executeCached("SELECT * from dirs"); Assert.equal(rows.length, 1); Assert.equal(rows[0].getResultByName("path"), "works");
await c.close();
});
// Test that direct binding of params and execution through mozStorage doesn't // error when we manually create a transaction. See Bug 856925.
add_task(async function test_direct() {
let file = new FileUtils.File(
PathUtils.join(PathUtils.tempDir, "test_direct.sqlite")
);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
print("Opening " + file.path);
let db = Services.storage.openDatabase(file);
print("Opened " + db);
db.executeSimpleSQL( "CREATE TABLE types (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, UNIQUE (name))"
);
print("Executed setup.");
let statement = db.createAsyncStatement( "INSERT INTO types (name) VALUES (:name)"
);
let params = statement.newBindingParamsArray();
let one = params.newBindingParams();
one.bindByName("name", null);
params.addParams(one);
let two = params.newBindingParams();
two.bindByName("name", "bar");
params.addParams(two);
print("Beginning transaction.");
let begin = db.createAsyncStatement("BEGIN DEFERRED TRANSACTION");
let end = db.createAsyncStatement("COMMIT TRANSACTION");
// Test Sqlite.cloneStorageConnection.
add_task(async function test_cloneStorageConnection() {
let file = new FileUtils.File(
PathUtils.join(PROFILE_DIR, "test_cloneStorageConnection.sqlite")
);
let c = await new Promise((resolve, reject) => {
Services.storage.openAsyncDatabase(
file, /* openFlags */ Ci.mozIStorageService.OPEN_DEFAULT, /* connectionFlags */ Ci.mozIStorageService.CONNECTION_DEFAULT,
(status, db) => { if (Components.isSuccessCode(status)) {
resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
} else {
reject(new Error(status));
}
}
);
});
let clone = await Sqlite.cloneStorageConnection({
connection: c,
readOnly: true,
}); Assert.equal(clone.defaultTransactionType, "DEFERRED"); // Just check that it works.
await clone.execute("SELECT 1");
info("Set default transaction type on storage connection");
c.defaultTransactionType = Ci.mozIStorageConnection.TRANSACTION_IMMEDIATE;
let clone2 = await Sqlite.cloneStorageConnection({
connection: c,
readOnly: false,
}); Assert.equal(clone2.defaultTransactionType, "IMMEDIATE"); // Just check that it works.
await clone2.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)");
// Closing order should not matter.
await c.asyncClose();
await clone2.close();
await clone.close();
});
// Test Sqlite.cloneStorageConnection invalid argument.
add_task(async function test_cloneStorageConnection() { try {
await Sqlite.cloneStorageConnection({ connection: null });
do_throw(new Error("Should throw on invalid connection"));
} catch (ex) { if (ex.name != "TypeError") { throw ex;
}
}
});
// Test clone() method.
add_task(async function test_clone() {
let c = await getDummyDatabase("clone");
let clone = await c.clone(); // Just check that it works.
await clone.execute("SELECT 1"); // Closing order should not matter.
await c.close();
await clone.close();
});
// Test clone(readOnly) method.
add_task(async function test_readOnly_clone() {
let path = PathUtils.join(PROFILE_DIR, "test_readOnly_clone.sqlite");
let c = await Sqlite.openConnection({ path, sharedMemoryCache: false });
let clone = await c.clone(true); // Just check that it works.
await clone.execute("SELECT 1"); // But should not be able to write.
await Assert.rejects(
clone.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
/readonly/
); // Closing order should not matter.
await c.close();
await clone.close();
});
// Test Sqlite.wrapStorageConnection.
add_task(async function test_wrapStorageConnection() {
let file = new FileUtils.File(
PathUtils.join(PROFILE_DIR, "test_wrapStorageConnection.sqlite")
);
let c = await new Promise((resolve, reject) => {
Services.storage.openAsyncDatabase(
file, /* openFlags */ Ci.mozIStorageService.OPEN_DEFAULT, /* connectionFlags */ Ci.mozIStorageService.CONNECTION_DEFAULT,
(status, db) => { if (Components.isSuccessCode(status)) {
resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
} else {
reject(new Error(status));
}
}
);
});
let wrapper = await Sqlite.wrapStorageConnection({ connection: c }); Assert.equal(wrapper.defaultTransactionType, "DEFERRED"); // Just check that it works.
await wrapper.execute("SELECT 1");
await wrapper.executeCached("SELECT 1");
// Closing the wrapper should just finalize statements but not close the // database.
await wrapper.close();
info("Set default transaction type on storage connection");
c.defaultTransactionType = Ci.mozIStorageConnection.TRANSACTION_EXCLUSIVE;
let wrapper2 = await Sqlite.wrapStorageConnection({ connection: c }); Assert.equal(wrapper2.defaultTransactionType, "EXCLUSIVE"); // Just check that it works.
await wrapper2.execute("SELECT 1");
await wrapper2.close();
await c.asyncClose();
});
// Test finalization
add_task(async function test_closed_by_witness() {
failTestsOnAutoClose(false);
let c = await getDummyDatabase("closed_by_witness");
Services.obs.notifyObservers( null, "sqlite-finalization-witness",
c._connectionData._identifier
); // Since we triggered finalization ourselves, tell the witness to // forget the connection so it does not trigger a finalization again
c._witness.forget();
await c._connectionData._deferredClose.promise; Assert.ok(!c._connectionData._open);
failTestsOnAutoClose(true);
});
add_task(async function test_warning_message_on_finalization() {
failTestsOnAutoClose(false);
let c = await getDummyDatabase("warning_message_on_finalization");
let identifier = c._connectionData._identifier;
let deferred = Promise.withResolvers();
let listener = {
observe(msg) {
let messageText = msg.message; // Make sure the message starts with a warning containing the // connection identifier if (
messageText.includes("Warning: Sqlite connection '" + identifier + "'")
) {
deferred.resolve();
}
},
};
Services.console.registerListener(listener);
Services.obs.notifyObservers(null, "sqlite-finalization-witness", identifier); // Since we triggered finalization ourselves, tell the witness to // forget the connection so it does not trigger a finalization again
c._witness.forget();
add_task(async function test_forget_witness_on_close() {
let c = await getDummyDatabase("forget_witness_on_close");
let forgetCalled = false;
let oldWitness = c._witness;
c._witness = {
forget() {
forgetCalled = true;
oldWitness.forget();
},
};
await c.close(); // After close, witness should have forgotten the connection Assert.ok(forgetCalled);
});
add_task(async function test_close_database_on_gc() {
failTestsOnAutoClose(false);
let finalPromise;
{
let collectedPromises = []; for (let i = 0; i < 100; ++i) {
let deferred = Promise.withResolvers();
let c = await getDummyDatabase("gc_" + i);
c._connectionData._deferredClose.promise.then(deferred.resolve);
collectedPromises.push(deferred.promise);
}
finalPromise = Promise.all(collectedPromises);
}
// Call getDummyDatabase once more to clear any remaining // references. This is needed at the moment, otherwise // garbage-collection takes place after the shutdown barrier and the // test will timeout. Once that is fixed, we can remove this line // and be fine as long as the connections are garbage-collected.
let last = await getDummyDatabase("gc_last");
await last.close();
let rows = await c.execute("SELECT * FROM datatypes"); Assert.ok(Array.isArray(rows)); Assert.equal(rows.length, bindings.length); for (let i = 0; i < bindings.length; ++i) {
let binding = bindings[i];
let row = rows[i]; for (let colName in binding) { // In Sqlite bool is stored and then retrieved as numeric.
let val = typeof binding[colName] == "boolean"
? +binding[colName]
: binding[colName]; Assert.deepEqual(val, row.getResultByName(colName));
}
}
await c.close();
});
add_task(async function test_interrupt() { // Testing the interrupt functionality is left to mozStorage unit tests, here // we'll just test error conditions.
let c = await getDummyDatabase("interrupt");
await c.interrupt();
ok(true, "Sqlite.interrupt() should not throw on a writable connection");
await c.close(); Assert.throws(
() => c.interrupt(),
/Connection is not open/, "Sqlite.interrupt() should throw on a closed connection"
);
});
add_task(async function test_pageSize() { // Testing the possibility to set the page size on database creation.
await Assert.rejects(
getDummyDatabase("pagesize", { pageSize: 1234 }),
/Invalid pageSize/, "Check invalid pageSize value"
);
let c = await getDummyDatabase("pagesize", { pageSize: 8192 }); Assert.equal(
(await c.execute("PRAGMA page_size"))[0].getResultByIndex(0),
8192, "Check page size was set"
);
await c.close();
});
add_task(async function test_loadExtension() {
await Assert.rejects(
getDummyDatabase("dummy", { extensions: ["dummy"] }),
/Could not load extension/, "Check unsupported extension"
);
let c = await getDummyDatabase("fts", { extensions: ["fts5"] });
await c.execute("CREATE VIRTUAL TABLE test_fts USING fts5(body)");
await c.close();
});
Messung V0.5
¤ Dauer der Verarbeitung: 0.39 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.