diff options
Diffstat (limited to 'toolkit/modules/tests/xpcshell/test_sqlite.js')
-rw-r--r-- | toolkit/modules/tests/xpcshell/test_sqlite.js | 1094 |
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); -}); |