storage/test/unit/test_storage_connection.js

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 // This file tests the functions of mozIStorageConnection
     7 ////////////////////////////////////////////////////////////////////////////////
     8 //// Test Functions
    10 function asyncClone(db, readOnly) {
    11   let deferred = Promise.defer();
    12   db.asyncClone(readOnly, function(status, db2) {
    13     if (Components.isSuccessCode(status)) {
    14       deferred.resolve(db2);
    15     } else {
    16       deferred.reject(status);
    17     }
    18   });
    19   return deferred.promise;
    20 }
    22 function asyncClose(db) {
    23   let deferred = Promise.defer();
    24   db.asyncClose(function(status) {
    25     if (Components.isSuccessCode(status)) {
    26       deferred.resolve();
    27     } else {
    28       deferred.reject(status);
    29     }
    30   });
    31   return deferred.promise;
    32 }
    34 function openAsyncDatabase(file, options) {
    35   let deferred = Promise.defer();
    36   let properties;
    37   if (options) {
    38     properties = Cc["@mozilla.org/hash-property-bag;1"].
    39         createInstance(Ci.nsIWritablePropertyBag);
    40     for (let k in options) {
    41       properties.setProperty(k, options[k]);
    42     }
    43   }
    44   getService().openAsyncDatabase(file, properties, function(status, db) {
    45     if (Components.isSuccessCode(status)) {
    46       deferred.resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
    47     } else {
    48       deferred.reject(status);
    49     }
    50   });
    51   return deferred.promise;
    52 }
    54 function executeAsync(statement, onResult) {
    55   let deferred = Promise.defer();
    56   statement.executeAsync({
    57     handleError: function(error) {
    58       deferred.reject(error);
    59     },
    60     handleResult: function(result) {
    61       if (onResult) {
    62         onResult(result);
    63       }
    64     },
    65     handleCompletion: function(result) {
    66       deferred.resolve(result);
    67     }
    68   });
    69   return deferred.promise;
    70 }
    72 add_task(function test_connectionReady_open()
    73 {
    74   // there doesn't seem to be a way for the connection to not be ready (unless
    75   // we close it with mozIStorageConnection::Close(), but we don't for this).
    76   // It can only fail if GetPath fails on the database file, or if we run out
    77   // of memory trying to use an in-memory database
    79   var msc = getOpenedDatabase();
    80   do_check_true(msc.connectionReady);
    81 });
    83 add_task(function test_connectionReady_closed()
    84 {
    85   // This also tests mozIStorageConnection::Close()
    87   var msc = getOpenedDatabase();
    88   msc.close();
    89   do_check_false(msc.connectionReady);
    90   gDBConn = null; // this is so later tests don't start to fail.
    91 });
    93 add_task(function test_databaseFile()
    94 {
    95   var msc = getOpenedDatabase();
    96   do_check_true(getTestDB().equals(msc.databaseFile));
    97 });
    99 add_task(function test_tableExists_not_created()
   100 {
   101   var msc = getOpenedDatabase();
   102   do_check_false(msc.tableExists("foo"));
   103 });
   105 add_task(function test_indexExists_not_created()
   106 {
   107   var msc = getOpenedDatabase();
   108   do_check_false(msc.indexExists("foo"));
   109 });
   111 add_task(function test_temp_tableExists_and_indexExists()
   112 {
   113   var msc = getOpenedDatabase();
   114   msc.executeSimpleSQL("CREATE TEMP TABLE test_temp(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
   115   do_check_true(msc.tableExists("test_temp"));
   117   msc.executeSimpleSQL("CREATE INDEX test_temp_ind ON test_temp (name)");
   118   do_check_true(msc.indexExists("test_temp_ind"));
   120   msc.executeSimpleSQL("DROP INDEX test_temp_ind");
   121   msc.executeSimpleSQL("DROP TABLE test_temp");
   122 });
   124 add_task(function test_createTable_not_created()
   125 {
   126   var msc = getOpenedDatabase();
   127   msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
   128   do_check_true(msc.tableExists("test"));
   129 });
   131 add_task(function test_indexExists_created()
   132 {
   133   var msc = getOpenedDatabase();
   134   msc.executeSimpleSQL("CREATE INDEX name_ind ON test (name)");
   135   do_check_true(msc.indexExists("name_ind"));
   136 });
   138 add_task(function test_createTable_already_created()
   139 {
   140   var msc = getOpenedDatabase();
   141   do_check_true(msc.tableExists("test"));
   142   try {
   143     msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
   144     do_throw("We shouldn't get here!");
   145   } catch (e) {
   146     do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
   147   }
   148 });
   150 add_task(function test_attach_createTable_tableExists_indexExists()
   151 {
   152   var msc = getOpenedDatabase();
   153   var file = do_get_file("storage_attach.sqlite", true);
   154   var msc2 = getDatabase(file);
   155   msc.executeSimpleSQL("ATTACH DATABASE '" + file.path + "' AS sample");
   157   do_check_false(msc.tableExists("sample.test"));
   158   msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT");
   159   do_check_true(msc.tableExists("sample.test"));
   160   try {
   161     msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT");
   162     do_throw("We shouldn't get here!");
   163   } catch (e if e.result == Components.results.NS_ERROR_FAILURE) {
   164     // we expect to fail because this table should exist already.
   165   }
   167   do_check_false(msc.indexExists("sample.test_ind"));
   168   msc.executeSimpleSQL("CREATE INDEX sample.test_ind ON test (name)");
   169   do_check_true(msc.indexExists("sample.test_ind"));
   171   msc.executeSimpleSQL("DETACH DATABASE sample");
   172   msc2.close();
   173   try { file.remove(false); } catch(e) { }
   174 });
   176 add_task(function test_lastInsertRowID()
   177 {
   178   var msc = getOpenedDatabase();
   179   msc.executeSimpleSQL("INSERT INTO test (name) VALUES ('foo')");
   180   do_check_eq(1, msc.lastInsertRowID);
   181 });
   183 add_task(function test_transactionInProgress_no()
   184 {
   185   var msc = getOpenedDatabase();
   186   do_check_false(msc.transactionInProgress);
   187 });
   189 add_task(function test_transactionInProgress_yes()
   190 {
   191   var msc = getOpenedDatabase();
   192   msc.beginTransaction();
   193   do_check_true(msc.transactionInProgress);
   194   msc.commitTransaction();
   195   do_check_false(msc.transactionInProgress);
   197   msc.beginTransaction();
   198   do_check_true(msc.transactionInProgress);
   199   msc.rollbackTransaction();
   200   do_check_false(msc.transactionInProgress);
   201 });
   203 add_task(function test_commitTransaction_no_transaction()
   204 {
   205   var msc = getOpenedDatabase();
   206   do_check_false(msc.transactionInProgress);
   207   try {
   208     msc.commitTransaction();
   209     do_throw("We should not get here!");
   210   } catch (e) {
   211     do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result);
   212   }
   213 });
   215 add_task(function test_rollbackTransaction_no_transaction()
   216 {
   217   var msc = getOpenedDatabase();
   218   do_check_false(msc.transactionInProgress);
   219   try {
   220     msc.rollbackTransaction();
   221     do_throw("We should not get here!");
   222   } catch (e) {
   223     do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result);
   224   }
   225 });
   227 add_task(function test_get_schemaVersion_not_set()
   228 {
   229   do_check_eq(0, getOpenedDatabase().schemaVersion);
   230 });
   232 add_task(function test_set_schemaVersion()
   233 {
   234   var msc = getOpenedDatabase();
   235   const version = 1;
   236   msc.schemaVersion = version;
   237   do_check_eq(version, msc.schemaVersion);
   238 });
   240 add_task(function test_set_schemaVersion_same()
   241 {
   242   var msc = getOpenedDatabase();
   243   const version = 1;
   244   msc.schemaVersion = version; // should still work ok
   245   do_check_eq(version, msc.schemaVersion);
   246 });
   248 add_task(function test_set_schemaVersion_negative()
   249 {
   250   var msc = getOpenedDatabase();
   251   const version = -1;
   252   msc.schemaVersion = version;
   253   do_check_eq(version, msc.schemaVersion);
   254 });
   256 add_task(function test_createTable(){
   257   var temp = getTestDB().parent;
   258   temp.append("test_db_table");
   259   try {
   260     var con = getService().openDatabase(temp);
   261     con.createTable("a","");
   262   } catch (e) {
   263     if (temp.exists()) try {
   264       temp.remove(false);
   265     } catch (e2) {}
   266     do_check_true(e.result==Cr.NS_ERROR_NOT_INITIALIZED ||
   267                   e.result==Cr.NS_ERROR_FAILURE);
   268   } finally {
   269     if (con) {
   270       con.close();
   271     }
   272   }
   273 });
   275 add_task(function test_defaultSynchronousAtNormal()
   276 {
   277   var msc = getOpenedDatabase();
   278   var stmt = createStatement("PRAGMA synchronous;");
   279   try {
   280     stmt.executeStep();
   281     do_check_eq(1, stmt.getInt32(0));
   282   }
   283   finally {
   284     stmt.reset();
   285     stmt.finalize();
   286   }
   287 });
   289 // must be ran before executeAsync tests
   290 add_task(function test_close_does_not_spin_event_loop()
   291 {
   292   // We want to make sure that the event loop on the calling thread does not
   293   // spin when close is called.
   294   let event = {
   295     ran: false,
   296     run: function()
   297     {
   298       this.ran = true;
   299     },
   300   };
   302   // Post the event before we call close, so it would run if the event loop was
   303   // spun during close.
   304   let thread = Cc["@mozilla.org/thread-manager;1"].
   305                getService(Ci.nsIThreadManager).
   306                currentThread;
   307   thread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL);
   309   // Sanity check, then close the database.  Afterwards, we should not have ran!
   310   do_check_false(event.ran);
   311   getOpenedDatabase().close();
   312   do_check_false(event.ran);
   314   // Reset gDBConn so that later tests will get a new connection object.
   315   gDBConn = null;
   316 });
   318 add_task(function test_asyncClose_succeeds_with_finalized_async_statement()
   319 {
   320   // XXX this test isn't perfect since we can't totally control when events will
   321   //     run.  If this paticular function fails randomly, it means we have a
   322   //     real bug.
   324   // We want to make sure we create a cached async statement to make sure that
   325   // when we finalize our statement, we end up finalizing the async one too so
   326   // close will succeed.
   327   let stmt = createStatement("SELECT * FROM test");
   328   stmt.executeAsync();
   329   stmt.finalize();
   331   yield asyncClose(getOpenedDatabase());
   332   // Reset gDBConn so that later tests will get a new connection object.
   333   gDBConn = null;
   334 });
   336 add_task(function test_close_then_release_statement() {
   337   // Testing the behavior in presence of a bad client that finalizes
   338   // statements after the database has been closed (typically by
   339   // letting the gc finalize the statement).
   340   let db = getOpenedDatabase();
   341   let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement");
   342   db.close();
   343   stmt.finalize(); // Finalize too late - this should not crash
   345   // Reset gDBConn so that later tests will get a new connection object.
   346   gDBConn = null;
   347 });
   349 add_task(function test_asyncClose_then_release_statement() {
   350   // Testing the behavior in presence of a bad client that finalizes
   351   // statements after the database has been async closed (typically by
   352   // letting the gc finalize the statement).
   353   let db = getOpenedDatabase();
   354   let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement");
   355   yield asyncClose(db);
   356   stmt.finalize(); // Finalize too late - this should not crash
   358   // Reset gDBConn so that later tests will get a new connection object.
   359   gDBConn = null;
   360 });
   362 add_task(function test_close_fails_with_async_statement_ran()
   363 {
   364   let deferred = Promise.defer();
   365   let stmt = createStatement("SELECT * FROM test");
   366   stmt.executeAsync();
   367   stmt.finalize();
   369   let db = getOpenedDatabase();
   370   try {
   371     db.close();
   372     do_throw("should have thrown");
   373   }
   374   catch (e) {
   375     do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
   376   }
   377   finally {
   378     // Clean up after ourselves.
   379     db.asyncClose(function() {
   380       // Reset gDBConn so that later tests will get a new connection object.
   381       gDBConn = null;
   382       deferred.resolve();
   383     });
   384   }
   385   yield deferred.promise;
   386 });
   388 add_task(function test_clone_optional_param()
   389 {
   390   let db1 = getService().openUnsharedDatabase(getTestDB());
   391   let db2 = db1.clone();
   392   do_check_true(db2.connectionReady);
   394   // A write statement should not fail here.
   395   let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
   396   stmt.params.name = "dwitte";
   397   stmt.execute();
   398   stmt.finalize();
   400   // And a read statement should succeed.
   401   stmt = db2.createStatement("SELECT * FROM test");
   402   do_check_true(stmt.executeStep());
   403   stmt.finalize();
   405   // Additionally check that it is a connection on the same database.
   406   do_check_true(db1.databaseFile.equals(db2.databaseFile));
   408   db1.close();
   409   db2.close();
   410 });
   412 function standardAsyncTest(promisedDB, name, shouldInit = false) {
   413   do_print("Performing standard async test " + name);
   415   let adb = yield promisedDB;
   416   do_check_true(adb instanceof Ci.mozIStorageAsyncConnection);
   417   do_check_false(adb instanceof Ci.mozIStorageConnection);
   419   if (shouldInit) {
   420     let stmt = adb.createAsyncStatement("CREATE TABLE test(name TEXT)");
   421     yield executeAsync(stmt);
   422     stmt.finalize();
   423   }
   425   // Generate a name to insert and fetch back
   426   name = "worker bee " + Math.random() + " (" + name + ")";
   428   let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
   429   stmt.params.name = name;
   430   let result = yield executeAsync(stmt);
   431   do_print("Request complete");
   432   stmt.finalize();
   433   do_check_true(Components.isSuccessCode(result));
   434   do_print("Extracting data");
   435   stmt = adb.createAsyncStatement("SELECT * FROM test");
   436   let found = false;
   437   yield executeAsync(stmt, function(result) {
   438     do_print("Data has been extracted");
   440     for (let row = result.getNextRow(); row != null; row = result.getNextRow()) {
   441       if (row.getResultByName("name") == name) {
   442         found = true;
   443       break;
   444       }
   445     }
   446   });
   447   do_check_true(found);
   448   stmt.finalize();
   449   yield asyncClose(adb);
   451   do_print("Standard async test " + name + " complete");
   452 }
   454 add_task(function test_open_async() {
   455   yield standardAsyncTest(openAsyncDatabase(getTestDB(), null), "default");
   456   yield standardAsyncTest(openAsyncDatabase(getTestDB()), "no optional arg");
   457   yield standardAsyncTest(openAsyncDatabase(getTestDB(),
   458     {shared: false, growthIncrement: 54}), "non-default options");
   459   yield standardAsyncTest(openAsyncDatabase("memory"),
   460     "in-memory database", true);
   461   yield standardAsyncTest(openAsyncDatabase("memory",
   462     {shared: false, growthIncrement: 54}),
   463     "in-memory database and options", true);
   465   do_print("Testing async opening with bogus options 1");
   466   let raised = false;
   467   let adb = null;
   468   try {
   469     adb = yield openAsyncDatabase(getTestDB(), {shared: "forty-two"});
   470   } catch (ex) {
   471     raised = true;
   472   } finally {
   473     if (adb) {
   474       yield asyncClose(adb);
   475     }
   476   }
   477   do_check_true(raised);
   479   do_print("Testing async opening with bogus options 2");
   480   raised = false;
   481   adb = null;
   482   try {
   483     adb = yield openAsyncDatabase(getTestDB(), {growthIncrement: "forty-two"});
   484   } catch (ex) {
   485     raised = true;
   486   } finally {
   487     if (adb) {
   488       yield asyncClose(adb);
   489     }
   490   }
   491   do_check_true(raised);
   492 });
   495 add_task(function test_async_open_with_shared_cache() {
   496   do_print("Testing that opening with a shared cache doesn't break stuff");
   497   let adb = yield openAsyncDatabase(getTestDB(), {shared: true});
   499   let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
   500   stmt.params.name = "clockworker";
   501   let result = yield executeAsync(stmt);
   502   do_print("Request complete");
   503   stmt.finalize();
   504   do_check_true(Components.isSuccessCode(result));
   505   do_print("Extracting data");
   506   stmt = adb.createAsyncStatement("SELECT * FROM test");
   507   let found = false;
   508   yield executeAsync(stmt, function(result) {
   509     do_print("Data has been extracted");
   511     for (let row = result.getNextRow(); row != null; row = result.getNextRow()) {
   512       if (row.getResultByName("name") == "clockworker") {
   513         found = true;
   514       break;
   515       }
   516     }
   517   });
   518   do_check_true(found);
   519   stmt.finalize();
   520   yield asyncClose(adb);
   521 });
   523 add_task(function test_clone_trivial_async()
   524 {
   525   let db1 = getService().openDatabase(getTestDB());
   526   do_print("Opened adb1");
   527   do_check_true(db1 instanceof Ci.mozIStorageAsyncConnection);
   528   let adb2 = yield asyncClone(db1, true);
   529   do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection);
   530   do_print("Cloned to adb2");
   531   db1.close();
   532   do_print("Closed db1");
   533   yield asyncClose(adb2);
   534 });
   536 add_task(function test_clone_no_optional_param_async()
   537 {
   538   "use strict";
   539   do_print("Testing async cloning");
   540   let adb1 = yield openAsyncDatabase(getTestDB(), null);
   541   do_check_true(adb1 instanceof Ci.mozIStorageAsyncConnection);
   543   do_print("Cloning database");
   544   do_check_true(Components.isSuccessCode(result));
   546   let adb2 = yield asyncClone(adb1);
   547   do_print("Testing that the cloned db is a mozIStorageAsyncConnection " +
   548            "and not a mozIStorageConnection");
   549   do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection);
   550   do_check_false(adb2 instanceof Ci.mozIStorageConnection);
   552   do_print("Inserting data into source db");
   553   let stmt = adb1.
   554                createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
   556   stmt.params.name = "yoric";
   557   let result = yield executeAsync(stmt);
   558   do_print("Request complete");
   559   stmt.finalize();
   560   do_check_true(Components.isSuccessCode(result));
   561   do_print("Extracting data from clone db");
   562   stmt = adb2.createAsyncStatement("SELECT * FROM test");
   563   let found = false;
   564   yield executeAsync(stmt, function(result) {
   565     do_print("Data has been extracted");
   567     for (let row = result.getNextRow(); row != null; row = result.getNextRow()) {
   568       if (row.getResultByName("name") == "yoric") {
   569         found = true;
   570       break;
   571       }
   572     }
   573   });
   574   do_check_true(found);
   575   stmt.finalize();
   576   do_print("Closing databases");
   577   yield asyncClose(adb2);
   578   do_print("First db closed");
   580   yield asyncClose(adb1);
   581   do_print("Second db closed");
   582 });
   584 add_task(function test_clone_readonly()
   585 {
   586   let db1 = getService().openUnsharedDatabase(getTestDB());
   587   let db2 = db1.clone(true);
   588   do_check_true(db2.connectionReady);
   590   // A write statement should fail here.
   591   let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
   592   stmt.params.name = "reed";
   593   expectError(Cr.NS_ERROR_FILE_READ_ONLY, function() stmt.execute());
   594   stmt.finalize();
   596   // And a read statement should succeed.
   597   stmt = db2.createStatement("SELECT * FROM test");
   598   do_check_true(stmt.executeStep());
   599   stmt.finalize();
   601   db1.close();
   602   db2.close();
   603 });
   605 add_task(function test_clone_shared_readonly()
   606 {
   607   let db1 = getService().openDatabase(getTestDB());
   608   let db2 = db1.clone(true);
   609   do_check_true(db2.connectionReady);
   611   let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
   612   stmt.params.name = "parker";
   613   // TODO currently SQLite does not actually work correctly here.  The behavior
   614   //      we want is commented out, and the current behavior is being tested
   615   //      for.  Our IDL comments will have to be updated when this starts to
   616   //      work again.
   617   stmt.execute();
   618   // expectError(Components.results.NS_ERROR_FILE_READ_ONLY, function() stmt.execute());
   619   stmt.finalize();
   621   // And a read statement should succeed.
   622   stmt = db2.createStatement("SELECT * FROM test");
   623   do_check_true(stmt.executeStep());
   624   stmt.finalize();
   626   db1.close();
   627   db2.close();
   628 });
   630 add_task(function test_close_clone_fails()
   631 {
   632   let calls = [
   633     "openDatabase",
   634     "openUnsharedDatabase",
   635   ];
   636   calls.forEach(function(methodName) {
   637     let db = getService()[methodName](getTestDB());
   638     db.close();
   639     expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone());
   640   });
   641 });
   643 add_task(function test_memory_clone_fails()
   644 {
   645   let db = getService().openSpecialDatabase("memory");
   646   db.close();
   647   expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone());
   648 });
   650 add_task(function test_clone_copies_functions()
   651 {
   652   const FUNC_NAME = "test_func";
   653   let calls = [
   654     "openDatabase",
   655     "openUnsharedDatabase",
   656   ];
   657   let functionMethods = [
   658     "createFunction",
   659     "createAggregateFunction",
   660   ];
   661   calls.forEach(function(methodName) {
   662     [true, false].forEach(function(readOnly) {
   663       functionMethods.forEach(function(functionMethod) {
   664         let db1 = getService()[methodName](getTestDB());
   665         // Create a function for db1.
   666         db1[functionMethod](FUNC_NAME, 1, {
   667           onFunctionCall: function() 0,
   668           onStep: function() 0,
   669           onFinal: function() 0,
   670         });
   672         // Clone it, and make sure the function exists still.
   673         let db2 = db1.clone(readOnly);
   674         // Note: this would fail if the function did not exist.
   675         let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test");
   676         stmt.finalize();
   677         db1.close();
   678         db2.close();
   679       });
   680     });
   681   });
   682 });
   684 add_task(function test_clone_copies_overridden_functions()
   685 {
   686   const FUNC_NAME = "lower";
   687   function test_func() {
   688     this.called = false;
   689   }
   690   test_func.prototype = {
   691     onFunctionCall: function() {
   692       this.called = true;
   693     },
   694     onStep: function() {
   695       this.called = true;
   696     },
   697     onFinal: function() 0,
   698   };
   700   let calls = [
   701     "openDatabase",
   702     "openUnsharedDatabase",
   703   ];
   704   let functionMethods = [
   705     "createFunction",
   706     "createAggregateFunction",
   707   ];
   708   calls.forEach(function(methodName) {
   709     [true, false].forEach(function(readOnly) {
   710       functionMethods.forEach(function(functionMethod) {
   711         let db1 = getService()[methodName](getTestDB());
   712         // Create a function for db1.
   713         let func = new test_func();
   714         db1[functionMethod](FUNC_NAME, 1, func);
   715         do_check_false(func.called);
   717         // Clone it, and make sure the function gets called.
   718         let db2 = db1.clone(readOnly);
   719         let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test");
   720         stmt.executeStep();
   721         do_check_true(func.called);
   722         stmt.finalize();
   723         db1.close();
   724         db2.close();
   725       });
   726     });
   727   });
   728 });
   730 add_task(function test_clone_copies_pragmas()
   731 {
   732   const PRAGMAS = [
   733     { name: "cache_size", value: 500, copied: true },
   734     { name: "temp_store", value: 2, copied: true },
   735     { name: "foreign_keys", value: 1, copied: true },
   736     { name: "journal_size_limit", value: 524288, copied: true },
   737     { name: "synchronous", value: 2, copied: true },
   738     { name: "wal_autocheckpoint", value: 16, copied: true },
   739     { name: "ignore_check_constraints", value: 1, copied: false },
   740   ];
   742   let db1 = getService().openUnsharedDatabase(getTestDB());
   744   // Sanity check initial values are different from enforced ones.
   745   PRAGMAS.forEach(function (pragma) {
   746     let stmt = db1.createStatement("PRAGMA " + pragma.name);
   747     do_check_true(stmt.executeStep());
   748     do_check_neq(pragma.value, stmt.getInt32(0));
   749     stmt.finalize();
   750   });
   751   // Execute pragmas.
   752   PRAGMAS.forEach(function (pragma) {
   753     db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value);
   754   });
   756   let db2 = db1.clone();
   757   do_check_true(db2.connectionReady);
   759   // Check cloned connection inherited pragma values.
   760   PRAGMAS.forEach(function (pragma) {
   761     let stmt = db2.createStatement("PRAGMA " + pragma.name);
   762     do_check_true(stmt.executeStep());
   763     let validate = pragma.copied ? do_check_eq : do_check_neq;
   764     validate(pragma.value, stmt.getInt32(0));
   765     stmt.finalize();
   766   });
   768   db1.close();
   769   db2.close();
   770 });
   772 add_task(function test_readonly_clone_copies_pragmas()
   773 {
   774   const PRAGMAS = [
   775     { name: "cache_size", value: 500, copied: true },
   776     { name: "temp_store", value: 2, copied: true },
   777     { name: "foreign_keys", value: 1, copied: false },
   778     { name: "journal_size_limit", value: 524288, copied: false },
   779     { name: "synchronous", value: 2, copied: false },
   780     { name: "wal_autocheckpoint", value: 16, copied: false },
   781     { name: "ignore_check_constraints", value: 1, copied: false },
   782   ];
   784   let db1 = getService().openUnsharedDatabase(getTestDB());
   786   // Sanity check initial values are different from enforced ones.
   787   PRAGMAS.forEach(function (pragma) {
   788     let stmt = db1.createStatement("PRAGMA " + pragma.name);
   789     do_check_true(stmt.executeStep());
   790     do_check_neq(pragma.value, stmt.getInt32(0));
   791     stmt.finalize();
   792   });
   793   // Execute pragmas.
   794   PRAGMAS.forEach(function (pragma) {
   795     db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value);
   796   });
   798   let db2 = db1.clone(true);
   799   do_check_true(db2.connectionReady);
   801   // Check cloned connection inherited pragma values.
   802   PRAGMAS.forEach(function (pragma) {
   803     let stmt = db2.createStatement("PRAGMA " + pragma.name);
   804     do_check_true(stmt.executeStep());
   805     let validate = pragma.copied ? do_check_eq : do_check_neq;
   806     validate(pragma.value, stmt.getInt32(0));
   807     stmt.finalize();
   808   });
   810   db1.close();
   811   db2.close();
   812 });
   814 add_task(function test_getInterface()
   815 {
   816   let db = getOpenedDatabase();
   817   let target = db.QueryInterface(Ci.nsIInterfaceRequestor)
   818                  .getInterface(Ci.nsIEventTarget);
   819   // Just check that target is non-null.  Other tests will ensure that it has
   820   // the correct value.
   821   do_check_true(target != null);
   823   yield asyncClose(db);
   824   gDBConn = null;
   825 });
   828 function run_test()
   829 {
   830   run_next_test();
   831 }

mercurial