michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // This file tests the functions of mozIStorageConnection michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Test Functions michael@0: michael@0: function asyncClone(db, readOnly) { michael@0: let deferred = Promise.defer(); michael@0: db.asyncClone(readOnly, function(status, db2) { michael@0: if (Components.isSuccessCode(status)) { michael@0: deferred.resolve(db2); michael@0: } else { michael@0: deferred.reject(status); michael@0: } michael@0: }); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function asyncClose(db) { michael@0: let deferred = Promise.defer(); michael@0: db.asyncClose(function(status) { michael@0: if (Components.isSuccessCode(status)) { michael@0: deferred.resolve(); michael@0: } else { michael@0: deferred.reject(status); michael@0: } michael@0: }); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function openAsyncDatabase(file, options) { michael@0: let deferred = Promise.defer(); michael@0: let properties; michael@0: if (options) { michael@0: properties = Cc["@mozilla.org/hash-property-bag;1"]. michael@0: createInstance(Ci.nsIWritablePropertyBag); michael@0: for (let k in options) { michael@0: properties.setProperty(k, options[k]); michael@0: } michael@0: } michael@0: getService().openAsyncDatabase(file, properties, function(status, db) { michael@0: if (Components.isSuccessCode(status)) { michael@0: deferred.resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection)); michael@0: } else { michael@0: deferred.reject(status); michael@0: } michael@0: }); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function executeAsync(statement, onResult) { michael@0: let deferred = Promise.defer(); michael@0: statement.executeAsync({ michael@0: handleError: function(error) { michael@0: deferred.reject(error); michael@0: }, michael@0: handleResult: function(result) { michael@0: if (onResult) { michael@0: onResult(result); michael@0: } michael@0: }, michael@0: handleCompletion: function(result) { michael@0: deferred.resolve(result); michael@0: } michael@0: }); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: add_task(function test_connectionReady_open() michael@0: { michael@0: // there doesn't seem to be a way for the connection to not be ready (unless michael@0: // we close it with mozIStorageConnection::Close(), but we don't for this). michael@0: // It can only fail if GetPath fails on the database file, or if we run out michael@0: // of memory trying to use an in-memory database michael@0: michael@0: var msc = getOpenedDatabase(); michael@0: do_check_true(msc.connectionReady); michael@0: }); michael@0: michael@0: add_task(function test_connectionReady_closed() michael@0: { michael@0: // This also tests mozIStorageConnection::Close() michael@0: michael@0: var msc = getOpenedDatabase(); michael@0: msc.close(); michael@0: do_check_false(msc.connectionReady); michael@0: gDBConn = null; // this is so later tests don't start to fail. michael@0: }); michael@0: michael@0: add_task(function test_databaseFile() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_true(getTestDB().equals(msc.databaseFile)); michael@0: }); michael@0: michael@0: add_task(function test_tableExists_not_created() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_false(msc.tableExists("foo")); michael@0: }); michael@0: michael@0: add_task(function test_indexExists_not_created() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_false(msc.indexExists("foo")); michael@0: }); michael@0: michael@0: add_task(function test_temp_tableExists_and_indexExists() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: msc.executeSimpleSQL("CREATE TEMP TABLE test_temp(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)"); michael@0: do_check_true(msc.tableExists("test_temp")); michael@0: michael@0: msc.executeSimpleSQL("CREATE INDEX test_temp_ind ON test_temp (name)"); michael@0: do_check_true(msc.indexExists("test_temp_ind")); michael@0: michael@0: msc.executeSimpleSQL("DROP INDEX test_temp_ind"); michael@0: msc.executeSimpleSQL("DROP TABLE test_temp"); michael@0: }); michael@0: michael@0: add_task(function test_createTable_not_created() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT"); michael@0: do_check_true(msc.tableExists("test")); michael@0: }); michael@0: michael@0: add_task(function test_indexExists_created() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: msc.executeSimpleSQL("CREATE INDEX name_ind ON test (name)"); michael@0: do_check_true(msc.indexExists("name_ind")); michael@0: }); michael@0: michael@0: add_task(function test_createTable_already_created() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_true(msc.tableExists("test")); michael@0: try { michael@0: msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT"); michael@0: do_throw("We shouldn't get here!"); michael@0: } catch (e) { michael@0: do_check_eq(Cr.NS_ERROR_FAILURE, e.result); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_attach_createTable_tableExists_indexExists() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: var file = do_get_file("storage_attach.sqlite", true); michael@0: var msc2 = getDatabase(file); michael@0: msc.executeSimpleSQL("ATTACH DATABASE '" + file.path + "' AS sample"); michael@0: michael@0: do_check_false(msc.tableExists("sample.test")); michael@0: msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT"); michael@0: do_check_true(msc.tableExists("sample.test")); michael@0: try { michael@0: msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT"); michael@0: do_throw("We shouldn't get here!"); michael@0: } catch (e if e.result == Components.results.NS_ERROR_FAILURE) { michael@0: // we expect to fail because this table should exist already. michael@0: } michael@0: michael@0: do_check_false(msc.indexExists("sample.test_ind")); michael@0: msc.executeSimpleSQL("CREATE INDEX sample.test_ind ON test (name)"); michael@0: do_check_true(msc.indexExists("sample.test_ind")); michael@0: michael@0: msc.executeSimpleSQL("DETACH DATABASE sample"); michael@0: msc2.close(); michael@0: try { file.remove(false); } catch(e) { } michael@0: }); michael@0: michael@0: add_task(function test_lastInsertRowID() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: msc.executeSimpleSQL("INSERT INTO test (name) VALUES ('foo')"); michael@0: do_check_eq(1, msc.lastInsertRowID); michael@0: }); michael@0: michael@0: add_task(function test_transactionInProgress_no() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_false(msc.transactionInProgress); michael@0: }); michael@0: michael@0: add_task(function test_transactionInProgress_yes() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: msc.beginTransaction(); michael@0: do_check_true(msc.transactionInProgress); michael@0: msc.commitTransaction(); michael@0: do_check_false(msc.transactionInProgress); michael@0: michael@0: msc.beginTransaction(); michael@0: do_check_true(msc.transactionInProgress); michael@0: msc.rollbackTransaction(); michael@0: do_check_false(msc.transactionInProgress); michael@0: }); michael@0: michael@0: add_task(function test_commitTransaction_no_transaction() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_false(msc.transactionInProgress); michael@0: try { michael@0: msc.commitTransaction(); michael@0: do_throw("We should not get here!"); michael@0: } catch (e) { michael@0: do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_rollbackTransaction_no_transaction() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: do_check_false(msc.transactionInProgress); michael@0: try { michael@0: msc.rollbackTransaction(); michael@0: do_throw("We should not get here!"); michael@0: } catch (e) { michael@0: do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_get_schemaVersion_not_set() michael@0: { michael@0: do_check_eq(0, getOpenedDatabase().schemaVersion); michael@0: }); michael@0: michael@0: add_task(function test_set_schemaVersion() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: const version = 1; michael@0: msc.schemaVersion = version; michael@0: do_check_eq(version, msc.schemaVersion); michael@0: }); michael@0: michael@0: add_task(function test_set_schemaVersion_same() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: const version = 1; michael@0: msc.schemaVersion = version; // should still work ok michael@0: do_check_eq(version, msc.schemaVersion); michael@0: }); michael@0: michael@0: add_task(function test_set_schemaVersion_negative() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: const version = -1; michael@0: msc.schemaVersion = version; michael@0: do_check_eq(version, msc.schemaVersion); michael@0: }); michael@0: michael@0: add_task(function test_createTable(){ michael@0: var temp = getTestDB().parent; michael@0: temp.append("test_db_table"); michael@0: try { michael@0: var con = getService().openDatabase(temp); michael@0: con.createTable("a",""); michael@0: } catch (e) { michael@0: if (temp.exists()) try { michael@0: temp.remove(false); michael@0: } catch (e2) {} michael@0: do_check_true(e.result==Cr.NS_ERROR_NOT_INITIALIZED || michael@0: e.result==Cr.NS_ERROR_FAILURE); michael@0: } finally { michael@0: if (con) { michael@0: con.close(); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_defaultSynchronousAtNormal() michael@0: { michael@0: var msc = getOpenedDatabase(); michael@0: var stmt = createStatement("PRAGMA synchronous;"); michael@0: try { michael@0: stmt.executeStep(); michael@0: do_check_eq(1, stmt.getInt32(0)); michael@0: } michael@0: finally { michael@0: stmt.reset(); michael@0: stmt.finalize(); michael@0: } michael@0: }); michael@0: michael@0: // must be ran before executeAsync tests michael@0: add_task(function test_close_does_not_spin_event_loop() michael@0: { michael@0: // We want to make sure that the event loop on the calling thread does not michael@0: // spin when close is called. michael@0: let event = { michael@0: ran: false, michael@0: run: function() michael@0: { michael@0: this.ran = true; michael@0: }, michael@0: }; michael@0: michael@0: // Post the event before we call close, so it would run if the event loop was michael@0: // spun during close. michael@0: let thread = Cc["@mozilla.org/thread-manager;1"]. michael@0: getService(Ci.nsIThreadManager). michael@0: currentThread; michael@0: thread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL); michael@0: michael@0: // Sanity check, then close the database. Afterwards, we should not have ran! michael@0: do_check_false(event.ran); michael@0: getOpenedDatabase().close(); michael@0: do_check_false(event.ran); michael@0: michael@0: // Reset gDBConn so that later tests will get a new connection object. michael@0: gDBConn = null; michael@0: }); michael@0: michael@0: add_task(function test_asyncClose_succeeds_with_finalized_async_statement() michael@0: { michael@0: // XXX this test isn't perfect since we can't totally control when events will michael@0: // run. If this paticular function fails randomly, it means we have a michael@0: // real bug. michael@0: michael@0: // We want to make sure we create a cached async statement to make sure that michael@0: // when we finalize our statement, we end up finalizing the async one too so michael@0: // close will succeed. michael@0: let stmt = createStatement("SELECT * FROM test"); michael@0: stmt.executeAsync(); michael@0: stmt.finalize(); michael@0: michael@0: yield asyncClose(getOpenedDatabase()); michael@0: // Reset gDBConn so that later tests will get a new connection object. michael@0: gDBConn = null; michael@0: }); michael@0: michael@0: add_task(function test_close_then_release_statement() { michael@0: // Testing the behavior in presence of a bad client that finalizes michael@0: // statements after the database has been closed (typically by michael@0: // letting the gc finalize the statement). michael@0: let db = getOpenedDatabase(); michael@0: let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement"); michael@0: db.close(); michael@0: stmt.finalize(); // Finalize too late - this should not crash michael@0: michael@0: // Reset gDBConn so that later tests will get a new connection object. michael@0: gDBConn = null; michael@0: }); michael@0: michael@0: add_task(function test_asyncClose_then_release_statement() { michael@0: // Testing the behavior in presence of a bad client that finalizes michael@0: // statements after the database has been async closed (typically by michael@0: // letting the gc finalize the statement). michael@0: let db = getOpenedDatabase(); michael@0: let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement"); michael@0: yield asyncClose(db); michael@0: stmt.finalize(); // Finalize too late - this should not crash michael@0: michael@0: // Reset gDBConn so that later tests will get a new connection object. michael@0: gDBConn = null; michael@0: }); michael@0: michael@0: add_task(function test_close_fails_with_async_statement_ran() michael@0: { michael@0: let deferred = Promise.defer(); michael@0: let stmt = createStatement("SELECT * FROM test"); michael@0: stmt.executeAsync(); michael@0: stmt.finalize(); michael@0: michael@0: let db = getOpenedDatabase(); michael@0: try { michael@0: db.close(); michael@0: do_throw("should have thrown"); michael@0: } michael@0: catch (e) { michael@0: do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: finally { michael@0: // Clean up after ourselves. michael@0: db.asyncClose(function() { michael@0: // Reset gDBConn so that later tests will get a new connection object. michael@0: gDBConn = null; michael@0: deferred.resolve(); michael@0: }); michael@0: } michael@0: yield deferred.promise; michael@0: }); michael@0: michael@0: add_task(function test_clone_optional_param() michael@0: { michael@0: let db1 = getService().openUnsharedDatabase(getTestDB()); michael@0: let db2 = db1.clone(); michael@0: do_check_true(db2.connectionReady); michael@0: michael@0: // A write statement should not fail here. michael@0: let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)"); michael@0: stmt.params.name = "dwitte"; michael@0: stmt.execute(); michael@0: stmt.finalize(); michael@0: michael@0: // And a read statement should succeed. michael@0: stmt = db2.createStatement("SELECT * FROM test"); michael@0: do_check_true(stmt.executeStep()); michael@0: stmt.finalize(); michael@0: michael@0: // Additionally check that it is a connection on the same database. michael@0: do_check_true(db1.databaseFile.equals(db2.databaseFile)); michael@0: michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: michael@0: function standardAsyncTest(promisedDB, name, shouldInit = false) { michael@0: do_print("Performing standard async test " + name); michael@0: michael@0: let adb = yield promisedDB; michael@0: do_check_true(adb instanceof Ci.mozIStorageAsyncConnection); michael@0: do_check_false(adb instanceof Ci.mozIStorageConnection); michael@0: michael@0: if (shouldInit) { michael@0: let stmt = adb.createAsyncStatement("CREATE TABLE test(name TEXT)"); michael@0: yield executeAsync(stmt); michael@0: stmt.finalize(); michael@0: } michael@0: michael@0: // Generate a name to insert and fetch back michael@0: name = "worker bee " + Math.random() + " (" + name + ")"; michael@0: michael@0: let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)"); michael@0: stmt.params.name = name; michael@0: let result = yield executeAsync(stmt); michael@0: do_print("Request complete"); michael@0: stmt.finalize(); michael@0: do_check_true(Components.isSuccessCode(result)); michael@0: do_print("Extracting data"); michael@0: stmt = adb.createAsyncStatement("SELECT * FROM test"); michael@0: let found = false; michael@0: yield executeAsync(stmt, function(result) { michael@0: do_print("Data has been extracted"); michael@0: michael@0: for (let row = result.getNextRow(); row != null; row = result.getNextRow()) { michael@0: if (row.getResultByName("name") == name) { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: }); michael@0: do_check_true(found); michael@0: stmt.finalize(); michael@0: yield asyncClose(adb); michael@0: michael@0: do_print("Standard async test " + name + " complete"); michael@0: } michael@0: michael@0: add_task(function test_open_async() { michael@0: yield standardAsyncTest(openAsyncDatabase(getTestDB(), null), "default"); michael@0: yield standardAsyncTest(openAsyncDatabase(getTestDB()), "no optional arg"); michael@0: yield standardAsyncTest(openAsyncDatabase(getTestDB(), michael@0: {shared: false, growthIncrement: 54}), "non-default options"); michael@0: yield standardAsyncTest(openAsyncDatabase("memory"), michael@0: "in-memory database", true); michael@0: yield standardAsyncTest(openAsyncDatabase("memory", michael@0: {shared: false, growthIncrement: 54}), michael@0: "in-memory database and options", true); michael@0: michael@0: do_print("Testing async opening with bogus options 1"); michael@0: let raised = false; michael@0: let adb = null; michael@0: try { michael@0: adb = yield openAsyncDatabase(getTestDB(), {shared: "forty-two"}); michael@0: } catch (ex) { michael@0: raised = true; michael@0: } finally { michael@0: if (adb) { michael@0: yield asyncClose(adb); michael@0: } michael@0: } michael@0: do_check_true(raised); michael@0: michael@0: do_print("Testing async opening with bogus options 2"); michael@0: raised = false; michael@0: adb = null; michael@0: try { michael@0: adb = yield openAsyncDatabase(getTestDB(), {growthIncrement: "forty-two"}); michael@0: } catch (ex) { michael@0: raised = true; michael@0: } finally { michael@0: if (adb) { michael@0: yield asyncClose(adb); michael@0: } michael@0: } michael@0: do_check_true(raised); michael@0: }); michael@0: michael@0: michael@0: add_task(function test_async_open_with_shared_cache() { michael@0: do_print("Testing that opening with a shared cache doesn't break stuff"); michael@0: let adb = yield openAsyncDatabase(getTestDB(), {shared: true}); michael@0: michael@0: let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)"); michael@0: stmt.params.name = "clockworker"; michael@0: let result = yield executeAsync(stmt); michael@0: do_print("Request complete"); michael@0: stmt.finalize(); michael@0: do_check_true(Components.isSuccessCode(result)); michael@0: do_print("Extracting data"); michael@0: stmt = adb.createAsyncStatement("SELECT * FROM test"); michael@0: let found = false; michael@0: yield executeAsync(stmt, function(result) { michael@0: do_print("Data has been extracted"); michael@0: michael@0: for (let row = result.getNextRow(); row != null; row = result.getNextRow()) { michael@0: if (row.getResultByName("name") == "clockworker") { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: }); michael@0: do_check_true(found); michael@0: stmt.finalize(); michael@0: yield asyncClose(adb); michael@0: }); michael@0: michael@0: add_task(function test_clone_trivial_async() michael@0: { michael@0: let db1 = getService().openDatabase(getTestDB()); michael@0: do_print("Opened adb1"); michael@0: do_check_true(db1 instanceof Ci.mozIStorageAsyncConnection); michael@0: let adb2 = yield asyncClone(db1, true); michael@0: do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection); michael@0: do_print("Cloned to adb2"); michael@0: db1.close(); michael@0: do_print("Closed db1"); michael@0: yield asyncClose(adb2); michael@0: }); michael@0: michael@0: add_task(function test_clone_no_optional_param_async() michael@0: { michael@0: "use strict"; michael@0: do_print("Testing async cloning"); michael@0: let adb1 = yield openAsyncDatabase(getTestDB(), null); michael@0: do_check_true(adb1 instanceof Ci.mozIStorageAsyncConnection); michael@0: michael@0: do_print("Cloning database"); michael@0: do_check_true(Components.isSuccessCode(result)); michael@0: michael@0: let adb2 = yield asyncClone(adb1); michael@0: do_print("Testing that the cloned db is a mozIStorageAsyncConnection " + michael@0: "and not a mozIStorageConnection"); michael@0: do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection); michael@0: do_check_false(adb2 instanceof Ci.mozIStorageConnection); michael@0: michael@0: do_print("Inserting data into source db"); michael@0: let stmt = adb1. michael@0: createAsyncStatement("INSERT INTO test (name) VALUES (:name)"); michael@0: michael@0: stmt.params.name = "yoric"; michael@0: let result = yield executeAsync(stmt); michael@0: do_print("Request complete"); michael@0: stmt.finalize(); michael@0: do_check_true(Components.isSuccessCode(result)); michael@0: do_print("Extracting data from clone db"); michael@0: stmt = adb2.createAsyncStatement("SELECT * FROM test"); michael@0: let found = false; michael@0: yield executeAsync(stmt, function(result) { michael@0: do_print("Data has been extracted"); michael@0: michael@0: for (let row = result.getNextRow(); row != null; row = result.getNextRow()) { michael@0: if (row.getResultByName("name") == "yoric") { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: }); michael@0: do_check_true(found); michael@0: stmt.finalize(); michael@0: do_print("Closing databases"); michael@0: yield asyncClose(adb2); michael@0: do_print("First db closed"); michael@0: michael@0: yield asyncClose(adb1); michael@0: do_print("Second db closed"); michael@0: }); michael@0: michael@0: add_task(function test_clone_readonly() michael@0: { michael@0: let db1 = getService().openUnsharedDatabase(getTestDB()); michael@0: let db2 = db1.clone(true); michael@0: do_check_true(db2.connectionReady); michael@0: michael@0: // A write statement should fail here. michael@0: let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)"); michael@0: stmt.params.name = "reed"; michael@0: expectError(Cr.NS_ERROR_FILE_READ_ONLY, function() stmt.execute()); michael@0: stmt.finalize(); michael@0: michael@0: // And a read statement should succeed. michael@0: stmt = db2.createStatement("SELECT * FROM test"); michael@0: do_check_true(stmt.executeStep()); michael@0: stmt.finalize(); michael@0: michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: michael@0: add_task(function test_clone_shared_readonly() michael@0: { michael@0: let db1 = getService().openDatabase(getTestDB()); michael@0: let db2 = db1.clone(true); michael@0: do_check_true(db2.connectionReady); michael@0: michael@0: let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)"); michael@0: stmt.params.name = "parker"; michael@0: // TODO currently SQLite does not actually work correctly here. The behavior michael@0: // we want is commented out, and the current behavior is being tested michael@0: // for. Our IDL comments will have to be updated when this starts to michael@0: // work again. michael@0: stmt.execute(); michael@0: // expectError(Components.results.NS_ERROR_FILE_READ_ONLY, function() stmt.execute()); michael@0: stmt.finalize(); michael@0: michael@0: // And a read statement should succeed. michael@0: stmt = db2.createStatement("SELECT * FROM test"); michael@0: do_check_true(stmt.executeStep()); michael@0: stmt.finalize(); michael@0: michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: michael@0: add_task(function test_close_clone_fails() michael@0: { michael@0: let calls = [ michael@0: "openDatabase", michael@0: "openUnsharedDatabase", michael@0: ]; michael@0: calls.forEach(function(methodName) { michael@0: let db = getService()[methodName](getTestDB()); michael@0: db.close(); michael@0: expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone()); michael@0: }); michael@0: }); michael@0: michael@0: add_task(function test_memory_clone_fails() michael@0: { michael@0: let db = getService().openSpecialDatabase("memory"); michael@0: db.close(); michael@0: expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone()); michael@0: }); michael@0: michael@0: add_task(function test_clone_copies_functions() michael@0: { michael@0: const FUNC_NAME = "test_func"; michael@0: let calls = [ michael@0: "openDatabase", michael@0: "openUnsharedDatabase", michael@0: ]; michael@0: let functionMethods = [ michael@0: "createFunction", michael@0: "createAggregateFunction", michael@0: ]; michael@0: calls.forEach(function(methodName) { michael@0: [true, false].forEach(function(readOnly) { michael@0: functionMethods.forEach(function(functionMethod) { michael@0: let db1 = getService()[methodName](getTestDB()); michael@0: // Create a function for db1. michael@0: db1[functionMethod](FUNC_NAME, 1, { michael@0: onFunctionCall: function() 0, michael@0: onStep: function() 0, michael@0: onFinal: function() 0, michael@0: }); michael@0: michael@0: // Clone it, and make sure the function exists still. michael@0: let db2 = db1.clone(readOnly); michael@0: // Note: this would fail if the function did not exist. michael@0: let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test"); michael@0: stmt.finalize(); michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: add_task(function test_clone_copies_overridden_functions() michael@0: { michael@0: const FUNC_NAME = "lower"; michael@0: function test_func() { michael@0: this.called = false; michael@0: } michael@0: test_func.prototype = { michael@0: onFunctionCall: function() { michael@0: this.called = true; michael@0: }, michael@0: onStep: function() { michael@0: this.called = true; michael@0: }, michael@0: onFinal: function() 0, michael@0: }; michael@0: michael@0: let calls = [ michael@0: "openDatabase", michael@0: "openUnsharedDatabase", michael@0: ]; michael@0: let functionMethods = [ michael@0: "createFunction", michael@0: "createAggregateFunction", michael@0: ]; michael@0: calls.forEach(function(methodName) { michael@0: [true, false].forEach(function(readOnly) { michael@0: functionMethods.forEach(function(functionMethod) { michael@0: let db1 = getService()[methodName](getTestDB()); michael@0: // Create a function for db1. michael@0: let func = new test_func(); michael@0: db1[functionMethod](FUNC_NAME, 1, func); michael@0: do_check_false(func.called); michael@0: michael@0: // Clone it, and make sure the function gets called. michael@0: let db2 = db1.clone(readOnly); michael@0: let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test"); michael@0: stmt.executeStep(); michael@0: do_check_true(func.called); michael@0: stmt.finalize(); michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: michael@0: add_task(function test_clone_copies_pragmas() michael@0: { michael@0: const PRAGMAS = [ michael@0: { name: "cache_size", value: 500, copied: true }, michael@0: { name: "temp_store", value: 2, copied: true }, michael@0: { name: "foreign_keys", value: 1, copied: true }, michael@0: { name: "journal_size_limit", value: 524288, copied: true }, michael@0: { name: "synchronous", value: 2, copied: true }, michael@0: { name: "wal_autocheckpoint", value: 16, copied: true }, michael@0: { name: "ignore_check_constraints", value: 1, copied: false }, michael@0: ]; michael@0: michael@0: let db1 = getService().openUnsharedDatabase(getTestDB()); michael@0: michael@0: // Sanity check initial values are different from enforced ones. michael@0: PRAGMAS.forEach(function (pragma) { michael@0: let stmt = db1.createStatement("PRAGMA " + pragma.name); michael@0: do_check_true(stmt.executeStep()); michael@0: do_check_neq(pragma.value, stmt.getInt32(0)); michael@0: stmt.finalize(); michael@0: }); michael@0: // Execute pragmas. michael@0: PRAGMAS.forEach(function (pragma) { michael@0: db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value); michael@0: }); michael@0: michael@0: let db2 = db1.clone(); michael@0: do_check_true(db2.connectionReady); michael@0: michael@0: // Check cloned connection inherited pragma values. michael@0: PRAGMAS.forEach(function (pragma) { michael@0: let stmt = db2.createStatement("PRAGMA " + pragma.name); michael@0: do_check_true(stmt.executeStep()); michael@0: let validate = pragma.copied ? do_check_eq : do_check_neq; michael@0: validate(pragma.value, stmt.getInt32(0)); michael@0: stmt.finalize(); michael@0: }); michael@0: michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: michael@0: add_task(function test_readonly_clone_copies_pragmas() michael@0: { michael@0: const PRAGMAS = [ michael@0: { name: "cache_size", value: 500, copied: true }, michael@0: { name: "temp_store", value: 2, copied: true }, michael@0: { name: "foreign_keys", value: 1, copied: false }, michael@0: { name: "journal_size_limit", value: 524288, copied: false }, michael@0: { name: "synchronous", value: 2, copied: false }, michael@0: { name: "wal_autocheckpoint", value: 16, copied: false }, michael@0: { name: "ignore_check_constraints", value: 1, copied: false }, michael@0: ]; michael@0: michael@0: let db1 = getService().openUnsharedDatabase(getTestDB()); michael@0: michael@0: // Sanity check initial values are different from enforced ones. michael@0: PRAGMAS.forEach(function (pragma) { michael@0: let stmt = db1.createStatement("PRAGMA " + pragma.name); michael@0: do_check_true(stmt.executeStep()); michael@0: do_check_neq(pragma.value, stmt.getInt32(0)); michael@0: stmt.finalize(); michael@0: }); michael@0: // Execute pragmas. michael@0: PRAGMAS.forEach(function (pragma) { michael@0: db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value); michael@0: }); michael@0: michael@0: let db2 = db1.clone(true); michael@0: do_check_true(db2.connectionReady); michael@0: michael@0: // Check cloned connection inherited pragma values. michael@0: PRAGMAS.forEach(function (pragma) { michael@0: let stmt = db2.createStatement("PRAGMA " + pragma.name); michael@0: do_check_true(stmt.executeStep()); michael@0: let validate = pragma.copied ? do_check_eq : do_check_neq; michael@0: validate(pragma.value, stmt.getInt32(0)); michael@0: stmt.finalize(); michael@0: }); michael@0: michael@0: db1.close(); michael@0: db2.close(); michael@0: }); michael@0: michael@0: add_task(function test_getInterface() michael@0: { michael@0: let db = getOpenedDatabase(); michael@0: let target = db.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIEventTarget); michael@0: // Just check that target is non-null. Other tests will ensure that it has michael@0: // the correct value. michael@0: do_check_true(target != null); michael@0: michael@0: yield asyncClose(db); michael@0: gDBConn = null; michael@0: }); michael@0: michael@0: michael@0: function run_test() michael@0: { michael@0: run_next_test(); michael@0: }