summaryrefslogtreecommitdiffstats
path: root/toolkit/modules/tests/xpcshell/test_sqlite.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/modules/tests/xpcshell/test_sqlite.js')
-rw-r--r--toolkit/modules/tests/xpcshell/test_sqlite.js1094
1 files changed, 0 insertions, 1094 deletions
diff --git a/toolkit/modules/tests/xpcshell/test_sqlite.js b/toolkit/modules/tests/xpcshell/test_sqlite.js
deleted file mode 100644
index edd39d977..000000000
--- a/toolkit/modules/tests/xpcshell/test_sqlite.js
+++ /dev/null
@@ -1,1094 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-do_get_profile();
-
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/PromiseUtils.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Sqlite.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-// To spin the event loop in test.
-Cu.import("resource://services-common/async.js");
-
-function sleep(ms) {
- let deferred = Promise.defer();
-
- let timer = Cc["@mozilla.org/timer;1"]
- .createInstance(Ci.nsITimer);
-
- timer.initWithCallback({
- notify: function () {
- deferred.resolve();
- },
- }, ms, timer.TYPE_ONE_SHOT);
-
- return deferred.promise;
-}
-
-// When testing finalization, use this to tell Sqlite.jsm to not throw
-// an uncatchable `Promise.reject`
-function failTestsOnAutoClose(enabled) {
- Cu.getGlobalForObject(Sqlite).Debugging.failTestsOnAutoClose = enabled;
-}
-
-function getConnection(dbName, extraOptions={}) {
- let path = dbName + ".sqlite";
- let options = {path: path};
- for (let [k, v] of Object.entries(extraOptions)) {
- options[k] = v;
- }
-
- return Sqlite.openConnection(options);
-}
-
-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 = yield getConnection(name, extraOptions);
- c._initialStatementCount = 0;
-
- for (let [k, v] of Object.entries(TABLES)) {
- yield c.execute("CREATE TABLE " + k + "(" + v + ")");
- c._initialStatementCount++;
- }
-
- return c;
-}
-
-function* getDummyTempDatabase(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 = yield getConnection(name, extraOptions);
- c._initialStatementCount = 0;
-
- for (let [k, v] of Object.entries(TABLES)) {
- yield c.execute("CREATE TEMP TABLE " + k + "(" + v + ")");
- c._initialStatementCount++;
- }
-
- return c;
-}
-
-function run_test() {
- Cu.import("resource://testing-common/services/common/logging.js");
- initTestLogging("Trace");
-
- run_next_test();
-}
-
-add_task(function* test_open_normal() {
- let c = yield Sqlite.openConnection({path: "test_open_normal.sqlite"});
- yield c.close();
-});
-
-add_task(function* test_open_unshared() {
- let path = OS.Path.join(OS.Constants.Path.profileDir, "test_open_unshared.sqlite");
-
- let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
- yield c.close();
-});
-
-add_task(function* test_get_dummy_database() {
- let db = yield getDummyDatabase("get_dummy_database");
-
- do_check_eq(typeof(db), "object");
- yield db.close();
-});
-
-add_task(function* test_schema_version() {
- let db = yield getDummyDatabase("schema_version");
-
- let version = yield db.getSchemaVersion();
- do_check_eq(version, 0);
-
- db.setSchemaVersion(14);
- version = yield db.getSchemaVersion();
- do_check_eq(version, 14);
-
- for (let v of [0.5, "foobar", NaN]) {
- let success;
- try {
- yield db.setSchemaVersion(v);
- do_print("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;
- }
- do_check_true(success);
-
- version = yield db.getSchemaVersion();
- do_check_eq(version, 14);
- }
-
- yield db.close();
-});
-
-add_task(function* test_simple_insert() {
- let c = yield getDummyDatabase("simple_insert");
-
- let result = yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
- do_check_true(Array.isArray(result));
- do_check_eq(result.length, 0);
- yield c.close();
-});
-
-add_task(function* test_simple_bound_array() {
- let c = yield getDummyDatabase("simple_bound_array");
-
- let result = yield c.execute("INSERT INTO dirs VALUES (?, ?)", [1, "foo"]);
- do_check_eq(result.length, 0);
- yield c.close();
-});
-
-add_task(function* test_simple_bound_object() {
- let c = yield getDummyDatabase("simple_bound_object");
- let result = yield c.execute("INSERT INTO dirs VALUES (:id, :path)",
- {id: 1, path: "foo"});
- do_check_eq(result.length, 0);
- result = yield c.execute("SELECT id, path FROM dirs");
- do_check_eq(result.length, 1);
- do_check_eq(result[0].getResultByName("id"), 1);
- do_check_eq(result[0].getResultByName("path"), "foo");
- yield c.close();
-});
-
-// This is mostly a sanity test to ensure simple executions work.
-add_task(function* test_simple_insert_then_select() {
- let c = yield getDummyDatabase("simple_insert_then_select");
-
- yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
- yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["bar"]);
-
- let result = yield c.execute("SELECT * FROM dirs");
- do_check_eq(result.length, 2);
-
- let i = 0;
- for (let row of result) {
- i++;
-
- do_check_eq(row.numEntries, 2);
- do_check_eq(row.getResultByIndex(0), i);
-
- let expected = {1: "foo", 2: "bar"}[i];
- do_check_eq(row.getResultByName("path"), expected);
- }
-
- yield c.close();
-});
-
-add_task(function* test_repeat_execution() {
- let c = yield getDummyDatabase("repeat_execution");
-
- let sql = "INSERT INTO dirs (path) VALUES (:path)";
- yield c.executeCached(sql, {path: "foo"});
- yield c.executeCached(sql);
-
- let result = yield c.execute("SELECT * FROM dirs");
-
- do_check_eq(result.length, 2);
-
- yield c.close();
-});
-
-add_task(function* test_table_exists() {
- let c = yield getDummyDatabase("table_exists");
-
- do_check_false(yield c.tableExists("does_not_exist"));
- do_check_true(yield c.tableExists("dirs"));
- do_check_true(yield c.tableExists("files"));
-
- yield c.close();
-});
-
-add_task(function* test_index_exists() {
- let c = yield getDummyDatabase("index_exists");
-
- do_check_false(yield c.indexExists("does_not_exist"));
-
- yield c.execute("CREATE INDEX my_index ON dirs (path)");
- do_check_true(yield c.indexExists("my_index"));
-
- yield c.close();
-});
-
-add_task(function* test_temp_table_exists() {
- let c = yield getDummyTempDatabase("temp_table_exists");
-
- do_check_false(yield c.tableExists("temp_does_not_exist"));
- do_check_true(yield c.tableExists("dirs"));
- do_check_true(yield c.tableExists("files"));
-
- yield c.close();
-});
-
-add_task(function* test_temp_index_exists() {
- let c = yield getDummyTempDatabase("temp_index_exists");
-
- do_check_false(yield c.indexExists("temp_does_not_exist"));
-
- yield c.execute("CREATE INDEX my_index ON dirs (path)");
- do_check_true(yield c.indexExists("my_index"));
-
- yield c.close();
-});
-
-add_task(function* test_close_cached() {
- let c = yield getDummyDatabase("close_cached");
-
- yield c.executeCached("SELECT * FROM dirs");
- yield c.executeCached("SELECT * FROM files");
-
- yield c.close();
-});
-
-add_task(function* test_execute_invalid_statement() {
- let c = yield getDummyDatabase("invalid_statement");
-
- let deferred = Promise.defer();
-
- do_check_eq(c._connectionData._anonymousStatements.size, 0);
-
- c.execute("SELECT invalid FROM unknown").then(do_throw, function onError(error) {
- deferred.resolve();
- });
-
- yield deferred.promise;
-
- // Ensure we don't leak the statement instance.
- do_check_eq(c._connectionData._anonymousStatements.size, 0);
-
- yield c.close();
-});
-
-add_task(function* test_incorrect_like_bindings() {
- let c = yield 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/);
-
- yield c.close();
-});
-add_task(function* test_on_row_exception_ignored() {
- let c = yield getDummyDatabase("on_row_exception_ignored");
-
- let sql = "INSERT INTO dirs (path) VALUES (?)";
- for (let i = 0; i < 10; i++) {
- yield c.executeCached(sql, ["dir" + i]);
- }
-
- let i = 0;
- let hasResult = yield c.execute("SELECT * FROM DIRS", null, function onRow(row) {
- i++;
-
- throw new Error("Some silly error.");
- });
-
- do_check_eq(hasResult, true);
- do_check_eq(i, 10);
-
- yield c.close();
-});
-
-// Ensure StopIteration during onRow causes processing to stop.
-add_task(function* test_on_row_stop_iteration() {
- let c = yield getDummyDatabase("on_row_stop_iteration");
-
- let sql = "INSERT INTO dirs (path) VALUES (?)";
- for (let i = 0; i < 10; i++) {
- yield c.executeCached(sql, ["dir" + i]);
- }
-
- let i = 0;
- let hasResult = yield c.execute("SELECT * FROM dirs", null, function onRow(row) {
- i++;
-
- if (i == 5) {
- throw StopIteration;
- }
- });
-
- do_check_eq(hasResult, true);
- do_check_eq(i, 5);
-
- yield c.close();
-});
-
-// Ensure execute resolves to false when no rows are selected.
-add_task(function* test_on_row_stop_iteration() {
- let c = yield getDummyDatabase("no_on_row");
-
- let i = 0;
- let hasResult = yield c.execute(`SELECT * FROM dirs WHERE path="nonexistent"`, null, function onRow(row) {
- i++;
- });
-
- do_check_eq(hasResult, false);
- do_check_eq(i, 0);
-
- yield c.close();
-});
-
-add_task(function* test_invalid_transaction_type() {
- let c = yield getDummyDatabase("invalid_transaction_type");
-
- Assert.throws(() => c.executeTransaction(function* () {}, "foobar"),
- /Unknown transaction type/,
- "Unknown transaction type should throw");
-
- yield c.close();
-});
-
-add_task(function* test_execute_transaction_success() {
- let c = yield getDummyDatabase("execute_transaction_success");
-
- do_check_false(c.transactionInProgress);
-
- yield c.executeTransaction(function* transaction(conn) {
- do_check_eq(c, conn);
- do_check_true(conn.transactionInProgress);
-
- yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
- });
-
- do_check_false(c.transactionInProgress);
- let rows = yield c.execute("SELECT * FROM dirs");
- do_check_true(Array.isArray(rows));
- do_check_eq(rows.length, 1);
-
- yield c.close();
-});
-
-add_task(function* test_execute_transaction_rollback() {
- let c = yield getDummyDatabase("execute_transaction_rollback");
-
- let deferred = Promise.defer();
-
- c.executeTransaction(function* transaction(conn) {
- yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
- print("Expecting error with next statement.");
- yield conn.execute("INSERT INTO invalid VALUES ('foo')");
-
- // We should never get here.
- do_throw();
- }).then(do_throw, function onError(error) {
- deferred.resolve();
- });
-
- yield deferred.promise;
-
- let rows = yield c.execute("SELECT * FROM dirs");
- do_check_eq(rows.length, 0);
-
- yield c.close();
-});
-
-add_task(function* test_close_during_transaction() {
- let c = yield getDummyDatabase("close_during_transaction");
-
- yield c.execute("INSERT INTO dirs (path) VALUES ('foo')");
-
- let promise = c.executeTransaction(function* transaction(conn) {
- yield c.execute("INSERT INTO dirs (path) VALUES ('bar')");
- });
- yield c.close();
-
- yield Assert.rejects(promise,
- /Transaction canceled due to a closed connection/,
- "closing a connection in the middle of a transaction should reject it");
-
- let c2 = yield getConnection("close_during_transaction");
- let rows = yield c2.execute("SELECT * FROM dirs");
- do_check_eq(rows.length, 1);
-
- yield c2.close();
-});
-
-// Verify that we support concurrent transactions.
-add_task(function* test_multiple_transactions() {
- let c = yield getDummyDatabase("detect_multiple_transactions");
-
- for (let i = 0; i < 10; ++i) {
- // We don't wait for these transactions.
- c.executeTransaction(function* () {
- yield c.execute("INSERT INTO dirs (path) VALUES (:path)",
- { path: `foo${i}` });
- yield c.execute("SELECT * FROM dirs");
- });
- }
- for (let i = 0; i < 10; ++i) {
- yield c.executeTransaction(function* () {
- yield c.execute("INSERT INTO dirs (path) VALUES (:path)",
- { path: `bar${i}` });
- yield c.execute("SELECT * FROM dirs");
- });
- }
-
- let rows = yield c.execute("SELECT * FROM dirs");
- do_check_eq(rows.length, 20);
-
- yield c.close();
-});
-
-// Verify that wrapped transactions ignore a BEGIN TRANSACTION failure, when
-// an externally opened transaction exists.
-add_task(function* test_wrapped_connection_transaction() {
- let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
- "test_wrapStorageConnection.sqlite"));
- let c = yield new Promise((resolve, reject) => {
- Services.storage.openAsyncDatabase(file, null, (status, db) => {
- if (Components.isSuccessCode(status)) {
- resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
- } else {
- reject(new Error(status));
- }
- });
- });
-
- let wrapper = yield Sqlite.wrapStorageConnection({ connection: c });
- // Start a transaction on the raw connection.
- yield c.executeSimpleSQLAsync("BEGIN");
- // Now use executeTransaction, it will be executed, but not in a transaction.
- yield wrapper.executeTransaction(function* () {
- yield wrapper.execute("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)");
- });
- // This should not fail cause the internal transaction has not been created.
- yield c.executeSimpleSQLAsync("COMMIT");
-
- yield wrapper.execute("SELECT * FROM test");
-
- // Closing the wrapper should just finalize statements but not close the
- // database.
- yield wrapper.close();
- yield c.asyncClose();
-});
-
-add_task(function* test_shrink_memory() {
- let c = yield getDummyDatabase("shrink_memory");
-
- // It's just a simple sanity test. We have no way of measuring whether this
- // actually does anything.
-
- yield c.shrinkMemory();
- yield c.close();
-});
-
-add_task(function* test_no_shrink_on_init() {
- let c = yield getConnection("no_shrink_on_init",
- {shrinkMemoryOnConnectionIdleMS: 200});
-
- let oldShrink = c._connectionData.shrinkMemory;
- let count = 0;
- Object.defineProperty(c._connectionData, "shrinkMemory", {
- value: function () {
- count++;
- },
- });
-
- // We should not shrink until a statement has been executed.
- yield sleep(220);
- do_check_eq(count, 0);
-
- yield c.execute("SELECT 1");
- yield sleep(220);
- do_check_eq(count, 1);
-
- yield c.close();
-});
-
-add_task(function* test_idle_shrink_fires() {
- let c = yield 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: function () {
- 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();
-
- yield sleep(220);
- do_check_eq(count, 1);
- do_check_eq(shrinkPromises.length, 1);
- yield shrinkPromises[0];
- shrinkPromises.shift();
-
- // We shouldn't shrink again unless a statement was executed.
- yield sleep(300);
- do_check_eq(count, 1);
-
- yield c.execute("SELECT 1");
- yield sleep(300);
-
- do_check_eq(count, 2);
- do_check_eq(shrinkPromises.length, 1);
- yield shrinkPromises[0];
-
- yield c.close();
-});
-
-add_task(function* test_idle_shrink_reset_on_operation() {
- const INTERVAL = 500;
- let c = yield getDummyDatabase("idle_shrink_reset_on_operation",
- {shrinkMemoryOnConnectionIdleMS: INTERVAL});
-
- c._connectionData._clearIdleShrinkTimer();
-
- let oldShrink = c._connectionData.shrinkMemory;
- let shrinkPromises = [];
- let count = 0;
-
- Object.defineProperty(c._connectionData, "shrinkMemory", {
- value: function () {
- count++;
- let promise = oldShrink.call(c._connectionData);
- shrinkPromises.push(promise);
- return promise;
- },
- });
-
- 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) {
- yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["" + i]);
- i++;
- }
-
- do_check_true(i > 0);
-
- // We should not have performed an idle while doing operations.
- do_check_eq(count, 0);
-
- // Wait for idle timer.
- yield sleep(INTERVAL);
-
- // Ensure we fired.
- do_check_eq(count, 1);
- do_check_eq(shrinkPromises.length, 1);
- yield shrinkPromises[0];
-
- yield c.close();
-});
-
-add_task(function* test_in_progress_counts() {
- let c = yield getDummyDatabase("in_progress_counts");
- do_check_eq(c._connectionData._statementCounter, c._initialStatementCount);
- do_check_eq(c._connectionData._pendingStatements.size, 0);
- yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
- do_check_eq(c._connectionData._statementCounter, c._initialStatementCount + 1);
- do_check_eq(c._connectionData._pendingStatements.size, 0);
-
- let expectOne;
- let expectTwo;
-
- // Please forgive me.
- let inner = Async.makeSpinningCallback();
- let outer = Async.makeSpinningCallback();
-
- // 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.
-
- yield 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.
- let p = c.executeCached("SELECT 10, path from dirs");
- expectTwo = c._connectionData._pendingStatements.size;
-
- // Now wait for it to be done before we return from the row handler …
- p.then(function onInner() {
- inner();
- });
- }).then(function onOuter() {
- // … and wait for the inner to be done before we finish …
- inner.wait();
- outer();
- });
-
- // … and wait for both queries to have finished before we go on and
- // test postconditions.
- outer.wait();
-
- do_check_eq(expectOne, 1);
- do_check_eq(expectTwo, 2);
- do_check_eq(c._connectionData._statementCounter, c._initialStatementCount + 3);
- do_check_eq(c._connectionData._pendingStatements.size, 0);
-
- yield c.close();
-});
-
-add_task(function* test_discard_while_active() {
- let c = yield getDummyDatabase("discard_while_active");
-
- yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
- yield c.executeCached("INSERT INTO dirs (path) VALUES ('bar')");
-
- let discarded = -1;
- let first = true;
- let sql = "SELECT * FROM dirs";
- yield c.executeCached(sql, null, function onRow(row) {
- if (!first) {
- return;
- }
- first = false;
- discarded = c.discardCachedStatements();
- });
-
- // We discarded everything, because the SELECT had already started to run.
- do_check_eq(3, discarded);
-
- // And again is safe.
- do_check_eq(0, c.discardCachedStatements());
-
- yield c.close();
-});
-
-add_task(function* test_discard_cached() {
- let c = yield getDummyDatabase("discard_cached");
-
- yield c.executeCached("SELECT * from dirs");
- do_check_eq(1, c._connectionData._cachedStatements.size);
-
- yield c.executeCached("SELECT * from files");
- do_check_eq(2, c._connectionData._cachedStatements.size);
-
- yield c.executeCached("SELECT * from dirs");
- do_check_eq(2, c._connectionData._cachedStatements.size);
-
- c.discardCachedStatements();
- do_check_eq(0, c._connectionData._cachedStatements.size);
-
- yield c.close();
-});
-
-add_task(function* test_programmatic_binding() {
- let c = yield getDummyDatabase("programmatic_binding");
-
- let bindings = [
- {id: 1, path: "foobar"},
- {id: null, path: "baznoo"},
- {id: 5, path: "toofoo"},
- ];
-
- let sql = "INSERT INTO dirs VALUES (:id, :path)";
- let result = yield c.execute(sql, bindings);
- do_check_eq(result.length, 0);
-
- let rows = yield c.executeCached("SELECT * from dirs");
- do_check_eq(rows.length, 3);
- yield c.close();
-});
-
-add_task(function* test_programmatic_binding_transaction() {
- let c = yield getDummyDatabase("programmatic_binding_transaction");
-
- let bindings = [
- {id: 1, path: "foobar"},
- {id: null, path: "baznoo"},
- {id: 5, path: "toofoo"},
- ];
-
- let sql = "INSERT INTO dirs VALUES (:id, :path)";
- yield c.executeTransaction(function* transaction() {
- let result = yield c.execute(sql, bindings);
- do_check_eq(result.length, 0);
-
- let rows = yield c.executeCached("SELECT * from dirs");
- do_check_eq(rows.length, 3);
- });
-
- // Transaction committed.
- let rows = yield c.executeCached("SELECT * from dirs");
- do_check_eq(rows.length, 3);
- yield c.close();
-});
-
-add_task(function* test_programmatic_binding_transaction_partial_rollback() {
- let c = yield getDummyDatabase("programmatic_binding_transaction_partial_rollback");
-
- let bindings = [
- {id: 2, path: "foobar"},
- {id: 3, path: "toofoo"},
- ];
-
- let sql = "INSERT INTO dirs VALUES (:id, :path)";
-
- // Add some data in an implicit transaction before beginning the batch insert.
- yield c.execute(sql, {id: 1, path: "works"});
-
- let secondSucceeded = false;
- try {
- yield c.executeTransaction(function* transaction() {
- // Insert one row. This won't implicitly start a transaction.
- let result = yield 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.
- result = yield c.execute(sql, bindings);
- secondSucceeded = true;
- });
- } catch (ex) {
- print("Caught expected exception: " + ex);
- }
-
- // We did not get to the end of our in-transaction block.
- do_check_false(secondSucceeded);
-
- // Everything that happened in *our* transaction, not mozStorage's, got
- // rolled back, but the first row still exists.
- let rows = yield c.executeCached("SELECT * from dirs");
- do_check_eq(rows.length, 1);
- do_check_eq(rows[0].getResultByName("path"), "works");
- yield c.close();
-});
-
-// Just like the previous test, but relying on the implicit
-// transaction established by mozStorage.
-add_task(function* test_programmatic_binding_implicit_transaction() {
- let c = yield getDummyDatabase("programmatic_binding_implicit_transaction");
-
- let bindings = [
- {id: 2, path: "foobar"},
- {id: 1, path: "toofoo"},
- ];
-
- let sql = "INSERT INTO dirs VALUES (:id, :path)";
- let secondSucceeded = false;
- yield c.execute(sql, {id: 1, path: "works"});
- try {
- let result = yield c.execute(sql, bindings);
- secondSucceeded = true;
- } catch (ex) {
- print("Caught expected exception: " + ex);
- }
-
- do_check_false(secondSucceeded);
-
- // The entire batch failed.
- let rows = yield c.executeCached("SELECT * from dirs");
- do_check_eq(rows.length, 1);
- do_check_eq(rows[0].getResultByName("path"), "works");
- yield 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(function* test_direct() {
- let file = FileUtils.getFile("TmpD", ["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");
-
- let deferred = Promise.defer();
- begin.executeAsync({
- handleCompletion: function (reason) {
- deferred.resolve();
- }
- });
- yield deferred.promise;
-
- statement.bindParameters(params);
-
- deferred = Promise.defer();
- print("Executing async.");
- statement.executeAsync({
- handleResult: function (resultSet) {
- },
-
- handleError: function (error) {
- print("Error when executing SQL (" + error.result + "): " +
- error.message);
- print("Original error: " + error.error);
- errors.push(error);
- deferred.reject();
- },
-
- handleCompletion: function (reason) {
- print("Completed.");
- deferred.resolve();
- }
- });
-
- yield deferred.promise;
-
- deferred = Promise.defer();
- end.executeAsync({
- handleCompletion: function (reason) {
- deferred.resolve();
- }
- });
- yield deferred.promise;
-
- statement.finalize();
- begin.finalize();
- end.finalize();
-
- deferred = Promise.defer();
- db.asyncClose(function () {
- deferred.resolve()
- });
- yield deferred.promise;
-});
-
-// Test Sqlite.cloneStorageConnection.
-add_task(function* test_cloneStorageConnection() {
- let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
- "test_cloneStorageConnection.sqlite"));
- let c = yield new Promise((resolve, reject) => {
- Services.storage.openAsyncDatabase(file, null, (status, db) => {
- if (Components.isSuccessCode(status)) {
- resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
- } else {
- reject(new Error(status));
- }
- });
- });
-
- let clone = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: true });
- // Just check that it works.
- yield clone.execute("SELECT 1");
-
- let clone2 = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: false });
- // Just check that it works.
- yield clone2.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)");
-
- // Closing order should not matter.
- yield c.asyncClose();
- yield clone2.close();
- yield clone.close();
-});
-
-// Test Sqlite.cloneStorageConnection invalid argument.
-add_task(function* test_cloneStorageConnection() {
- try {
- let clone = yield 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(function* test_clone() {
- let c = yield getDummyDatabase("clone");
-
- let clone = yield c.clone();
- // Just check that it works.
- yield clone.execute("SELECT 1");
- // Closing order should not matter.
- yield c.close();
- yield clone.close();
-});
-
-// Test clone(readOnly) method.
-add_task(function* test_readOnly_clone() {
- let path = OS.Path.join(OS.Constants.Path.profileDir, "test_readOnly_clone.sqlite");
- let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
-
- let clone = yield c.clone(true);
- // Just check that it works.
- yield clone.execute("SELECT 1");
- // But should not be able to write.
-
- yield Assert.rejects(clone.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
- /readonly/);
- // Closing order should not matter.
- yield c.close();
- yield clone.close();
-});
-
-// Test Sqlite.wrapStorageConnection.
-add_task(function* test_wrapStorageConnection() {
- let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
- "test_wrapStorageConnection.sqlite"));
- let c = yield new Promise((resolve, reject) => {
- Services.storage.openAsyncDatabase(file, null, (status, db) => {
- if (Components.isSuccessCode(status)) {
- resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
- } else {
- reject(new Error(status));
- }
- });
- });
-
- let wrapper = yield Sqlite.wrapStorageConnection({ connection: c });
- // Just check that it works.
- yield wrapper.execute("SELECT 1");
- yield wrapper.executeCached("SELECT 1");
-
- // Closing the wrapper should just finalize statements but not close the
- // database.
- yield wrapper.close();
- yield c.asyncClose();
-});
-
-// Test finalization
-add_task(function* test_closed_by_witness() {
- failTestsOnAutoClose(false);
- let c = yield 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();
- yield c._connectionData._deferredClose.promise;
- do_check_false(c._connectionData._open);
- failTestsOnAutoClose(true);
-});
-
-add_task(function* test_warning_message_on_finalization() {
- failTestsOnAutoClose(false);
- let c = yield getDummyDatabase("warning_message_on_finalization");
- let identifier = c._connectionData._identifier;
- let deferred = Promise.defer();
-
- let listener = {
- observe: function(msg) {
- let messageText = msg.message;
- // Make sure the message starts with a warning containing the
- // connection identifier
- if (messageText.indexOf("Warning: Sqlite connection '" + identifier + "'") !== -1) {
- 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();
-
- yield deferred.promise;
- Services.console.unregisterListener(listener);
- failTestsOnAutoClose(true);
-});
-
-add_task(function* test_error_message_on_unknown_finalization() {
- failTestsOnAutoClose(false);
- let deferred = Promise.defer();
-
- let listener = {
- observe: function(msg) {
- let messageText = msg.message;
- if (messageText.indexOf("Error: Attempt to finalize unknown " +
- "Sqlite connection: foo") !== -1) {
- deferred.resolve();
- }
- }
- };
- Services.console.registerListener(listener);
- Services.obs.notifyObservers(null, "sqlite-finalization-witness", "foo");
-
- yield deferred.promise;
- Services.console.unregisterListener(listener);
- failTestsOnAutoClose(true);
-});
-
-add_task(function* test_forget_witness_on_close() {
- let c = yield getDummyDatabase("forget_witness_on_close");
-
- let forgetCalled = false;
- let oldWitness = c._witness;
- c._witness = {
- forget: function () {
- forgetCalled = true;
- oldWitness.forget();
- },
- };
-
- yield c.close();
- // After close, witness should have forgotten the connection
- do_check_true(forgetCalled);
-});
-
-add_task(function* test_close_database_on_gc() {
- failTestsOnAutoClose(false);
- let finalPromise;
-
- {
- let collectedPromises = [];
- for (let i = 0; i < 100; ++i) {
- let deferred = PromiseUtils.defer();
- let c = yield 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 = yield getDummyDatabase("gc_last");
- yield last.close();
-
- Components.utils.forceGC();
- Components.utils.forceCC();
- Components.utils.forceShrinkingGC();
-
- yield finalPromise;
- failTestsOnAutoClose(true);
-});