storage/test/unit/test_storage_connection.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/storage/test/unit/test_storage_connection.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,831 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +// This file tests the functions of mozIStorageConnection
     1.9 +
    1.10 +////////////////////////////////////////////////////////////////////////////////
    1.11 +//// Test Functions
    1.12 +
    1.13 +function asyncClone(db, readOnly) {
    1.14 +  let deferred = Promise.defer();
    1.15 +  db.asyncClone(readOnly, function(status, db2) {
    1.16 +    if (Components.isSuccessCode(status)) {
    1.17 +      deferred.resolve(db2);
    1.18 +    } else {
    1.19 +      deferred.reject(status);
    1.20 +    }
    1.21 +  });
    1.22 +  return deferred.promise;
    1.23 +}
    1.24 +
    1.25 +function asyncClose(db) {
    1.26 +  let deferred = Promise.defer();
    1.27 +  db.asyncClose(function(status) {
    1.28 +    if (Components.isSuccessCode(status)) {
    1.29 +      deferred.resolve();
    1.30 +    } else {
    1.31 +      deferred.reject(status);
    1.32 +    }
    1.33 +  });
    1.34 +  return deferred.promise;
    1.35 +}
    1.36 +
    1.37 +function openAsyncDatabase(file, options) {
    1.38 +  let deferred = Promise.defer();
    1.39 +  let properties;
    1.40 +  if (options) {
    1.41 +    properties = Cc["@mozilla.org/hash-property-bag;1"].
    1.42 +        createInstance(Ci.nsIWritablePropertyBag);
    1.43 +    for (let k in options) {
    1.44 +      properties.setProperty(k, options[k]);
    1.45 +    }
    1.46 +  }
    1.47 +  getService().openAsyncDatabase(file, properties, function(status, db) {
    1.48 +    if (Components.isSuccessCode(status)) {
    1.49 +      deferred.resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
    1.50 +    } else {
    1.51 +      deferred.reject(status);
    1.52 +    }
    1.53 +  });
    1.54 +  return deferred.promise;
    1.55 +}
    1.56 +
    1.57 +function executeAsync(statement, onResult) {
    1.58 +  let deferred = Promise.defer();
    1.59 +  statement.executeAsync({
    1.60 +    handleError: function(error) {
    1.61 +      deferred.reject(error);
    1.62 +    },
    1.63 +    handleResult: function(result) {
    1.64 +      if (onResult) {
    1.65 +        onResult(result);
    1.66 +      }
    1.67 +    },
    1.68 +    handleCompletion: function(result) {
    1.69 +      deferred.resolve(result);
    1.70 +    }
    1.71 +  });
    1.72 +  return deferred.promise;
    1.73 +}
    1.74 +
    1.75 +add_task(function test_connectionReady_open()
    1.76 +{
    1.77 +  // there doesn't seem to be a way for the connection to not be ready (unless
    1.78 +  // we close it with mozIStorageConnection::Close(), but we don't for this).
    1.79 +  // It can only fail if GetPath fails on the database file, or if we run out
    1.80 +  // of memory trying to use an in-memory database
    1.81 +
    1.82 +  var msc = getOpenedDatabase();
    1.83 +  do_check_true(msc.connectionReady);
    1.84 +});
    1.85 +
    1.86 +add_task(function test_connectionReady_closed()
    1.87 +{
    1.88 +  // This also tests mozIStorageConnection::Close()
    1.89 +
    1.90 +  var msc = getOpenedDatabase();
    1.91 +  msc.close();
    1.92 +  do_check_false(msc.connectionReady);
    1.93 +  gDBConn = null; // this is so later tests don't start to fail.
    1.94 +});
    1.95 +
    1.96 +add_task(function test_databaseFile()
    1.97 +{
    1.98 +  var msc = getOpenedDatabase();
    1.99 +  do_check_true(getTestDB().equals(msc.databaseFile));
   1.100 +});
   1.101 +
   1.102 +add_task(function test_tableExists_not_created()
   1.103 +{
   1.104 +  var msc = getOpenedDatabase();
   1.105 +  do_check_false(msc.tableExists("foo"));
   1.106 +});
   1.107 +
   1.108 +add_task(function test_indexExists_not_created()
   1.109 +{
   1.110 +  var msc = getOpenedDatabase();
   1.111 +  do_check_false(msc.indexExists("foo"));
   1.112 +});
   1.113 +
   1.114 +add_task(function test_temp_tableExists_and_indexExists()
   1.115 +{
   1.116 +  var msc = getOpenedDatabase();
   1.117 +  msc.executeSimpleSQL("CREATE TEMP TABLE test_temp(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
   1.118 +  do_check_true(msc.tableExists("test_temp"));
   1.119 +
   1.120 +  msc.executeSimpleSQL("CREATE INDEX test_temp_ind ON test_temp (name)");
   1.121 +  do_check_true(msc.indexExists("test_temp_ind"));
   1.122 +
   1.123 +  msc.executeSimpleSQL("DROP INDEX test_temp_ind");
   1.124 +  msc.executeSimpleSQL("DROP TABLE test_temp");
   1.125 +});
   1.126 +
   1.127 +add_task(function test_createTable_not_created()
   1.128 +{
   1.129 +  var msc = getOpenedDatabase();
   1.130 +  msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
   1.131 +  do_check_true(msc.tableExists("test"));
   1.132 +});
   1.133 +
   1.134 +add_task(function test_indexExists_created()
   1.135 +{
   1.136 +  var msc = getOpenedDatabase();
   1.137 +  msc.executeSimpleSQL("CREATE INDEX name_ind ON test (name)");
   1.138 +  do_check_true(msc.indexExists("name_ind"));
   1.139 +});
   1.140 +
   1.141 +add_task(function test_createTable_already_created()
   1.142 +{
   1.143 +  var msc = getOpenedDatabase();
   1.144 +  do_check_true(msc.tableExists("test"));
   1.145 +  try {
   1.146 +    msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT");
   1.147 +    do_throw("We shouldn't get here!");
   1.148 +  } catch (e) {
   1.149 +    do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
   1.150 +  }
   1.151 +});
   1.152 +
   1.153 +add_task(function test_attach_createTable_tableExists_indexExists()
   1.154 +{
   1.155 +  var msc = getOpenedDatabase();
   1.156 +  var file = do_get_file("storage_attach.sqlite", true);
   1.157 +  var msc2 = getDatabase(file);
   1.158 +  msc.executeSimpleSQL("ATTACH DATABASE '" + file.path + "' AS sample");
   1.159 +
   1.160 +  do_check_false(msc.tableExists("sample.test"));
   1.161 +  msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT");
   1.162 +  do_check_true(msc.tableExists("sample.test"));
   1.163 +  try {
   1.164 +    msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT");
   1.165 +    do_throw("We shouldn't get here!");
   1.166 +  } catch (e if e.result == Components.results.NS_ERROR_FAILURE) {
   1.167 +    // we expect to fail because this table should exist already.
   1.168 +  }
   1.169 +
   1.170 +  do_check_false(msc.indexExists("sample.test_ind"));
   1.171 +  msc.executeSimpleSQL("CREATE INDEX sample.test_ind ON test (name)");
   1.172 +  do_check_true(msc.indexExists("sample.test_ind"));
   1.173 +
   1.174 +  msc.executeSimpleSQL("DETACH DATABASE sample");
   1.175 +  msc2.close();
   1.176 +  try { file.remove(false); } catch(e) { }
   1.177 +});
   1.178 +
   1.179 +add_task(function test_lastInsertRowID()
   1.180 +{
   1.181 +  var msc = getOpenedDatabase();
   1.182 +  msc.executeSimpleSQL("INSERT INTO test (name) VALUES ('foo')");
   1.183 +  do_check_eq(1, msc.lastInsertRowID);
   1.184 +});
   1.185 +
   1.186 +add_task(function test_transactionInProgress_no()
   1.187 +{
   1.188 +  var msc = getOpenedDatabase();
   1.189 +  do_check_false(msc.transactionInProgress);
   1.190 +});
   1.191 +
   1.192 +add_task(function test_transactionInProgress_yes()
   1.193 +{
   1.194 +  var msc = getOpenedDatabase();
   1.195 +  msc.beginTransaction();
   1.196 +  do_check_true(msc.transactionInProgress);
   1.197 +  msc.commitTransaction();
   1.198 +  do_check_false(msc.transactionInProgress);
   1.199 +
   1.200 +  msc.beginTransaction();
   1.201 +  do_check_true(msc.transactionInProgress);
   1.202 +  msc.rollbackTransaction();
   1.203 +  do_check_false(msc.transactionInProgress);
   1.204 +});
   1.205 +
   1.206 +add_task(function test_commitTransaction_no_transaction()
   1.207 +{
   1.208 +  var msc = getOpenedDatabase();
   1.209 +  do_check_false(msc.transactionInProgress);
   1.210 +  try {
   1.211 +    msc.commitTransaction();
   1.212 +    do_throw("We should not get here!");
   1.213 +  } catch (e) {
   1.214 +    do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result);
   1.215 +  }
   1.216 +});
   1.217 +
   1.218 +add_task(function test_rollbackTransaction_no_transaction()
   1.219 +{
   1.220 +  var msc = getOpenedDatabase();
   1.221 +  do_check_false(msc.transactionInProgress);
   1.222 +  try {
   1.223 +    msc.rollbackTransaction();
   1.224 +    do_throw("We should not get here!");
   1.225 +  } catch (e) {
   1.226 +    do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result);
   1.227 +  }
   1.228 +});
   1.229 +
   1.230 +add_task(function test_get_schemaVersion_not_set()
   1.231 +{
   1.232 +  do_check_eq(0, getOpenedDatabase().schemaVersion);
   1.233 +});
   1.234 +
   1.235 +add_task(function test_set_schemaVersion()
   1.236 +{
   1.237 +  var msc = getOpenedDatabase();
   1.238 +  const version = 1;
   1.239 +  msc.schemaVersion = version;
   1.240 +  do_check_eq(version, msc.schemaVersion);
   1.241 +});
   1.242 +
   1.243 +add_task(function test_set_schemaVersion_same()
   1.244 +{
   1.245 +  var msc = getOpenedDatabase();
   1.246 +  const version = 1;
   1.247 +  msc.schemaVersion = version; // should still work ok
   1.248 +  do_check_eq(version, msc.schemaVersion);
   1.249 +});
   1.250 +
   1.251 +add_task(function test_set_schemaVersion_negative()
   1.252 +{
   1.253 +  var msc = getOpenedDatabase();
   1.254 +  const version = -1;
   1.255 +  msc.schemaVersion = version;
   1.256 +  do_check_eq(version, msc.schemaVersion);
   1.257 +});
   1.258 +
   1.259 +add_task(function test_createTable(){
   1.260 +  var temp = getTestDB().parent;
   1.261 +  temp.append("test_db_table");
   1.262 +  try {
   1.263 +    var con = getService().openDatabase(temp);
   1.264 +    con.createTable("a","");
   1.265 +  } catch (e) {
   1.266 +    if (temp.exists()) try {
   1.267 +      temp.remove(false);
   1.268 +    } catch (e2) {}
   1.269 +    do_check_true(e.result==Cr.NS_ERROR_NOT_INITIALIZED ||
   1.270 +                  e.result==Cr.NS_ERROR_FAILURE);
   1.271 +  } finally {
   1.272 +    if (con) {
   1.273 +      con.close();
   1.274 +    }
   1.275 +  }
   1.276 +});
   1.277 +
   1.278 +add_task(function test_defaultSynchronousAtNormal()
   1.279 +{
   1.280 +  var msc = getOpenedDatabase();
   1.281 +  var stmt = createStatement("PRAGMA synchronous;");
   1.282 +  try {
   1.283 +    stmt.executeStep();
   1.284 +    do_check_eq(1, stmt.getInt32(0));
   1.285 +  }
   1.286 +  finally {
   1.287 +    stmt.reset();
   1.288 +    stmt.finalize();
   1.289 +  }
   1.290 +});
   1.291 +
   1.292 +// must be ran before executeAsync tests
   1.293 +add_task(function test_close_does_not_spin_event_loop()
   1.294 +{
   1.295 +  // We want to make sure that the event loop on the calling thread does not
   1.296 +  // spin when close is called.
   1.297 +  let event = {
   1.298 +    ran: false,
   1.299 +    run: function()
   1.300 +    {
   1.301 +      this.ran = true;
   1.302 +    },
   1.303 +  };
   1.304 +
   1.305 +  // Post the event before we call close, so it would run if the event loop was
   1.306 +  // spun during close.
   1.307 +  let thread = Cc["@mozilla.org/thread-manager;1"].
   1.308 +               getService(Ci.nsIThreadManager).
   1.309 +               currentThread;
   1.310 +  thread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL);
   1.311 +
   1.312 +  // Sanity check, then close the database.  Afterwards, we should not have ran!
   1.313 +  do_check_false(event.ran);
   1.314 +  getOpenedDatabase().close();
   1.315 +  do_check_false(event.ran);
   1.316 +
   1.317 +  // Reset gDBConn so that later tests will get a new connection object.
   1.318 +  gDBConn = null;
   1.319 +});
   1.320 +
   1.321 +add_task(function test_asyncClose_succeeds_with_finalized_async_statement()
   1.322 +{
   1.323 +  // XXX this test isn't perfect since we can't totally control when events will
   1.324 +  //     run.  If this paticular function fails randomly, it means we have a
   1.325 +  //     real bug.
   1.326 +
   1.327 +  // We want to make sure we create a cached async statement to make sure that
   1.328 +  // when we finalize our statement, we end up finalizing the async one too so
   1.329 +  // close will succeed.
   1.330 +  let stmt = createStatement("SELECT * FROM test");
   1.331 +  stmt.executeAsync();
   1.332 +  stmt.finalize();
   1.333 +
   1.334 +  yield asyncClose(getOpenedDatabase());
   1.335 +  // Reset gDBConn so that later tests will get a new connection object.
   1.336 +  gDBConn = null;
   1.337 +});
   1.338 +
   1.339 +add_task(function test_close_then_release_statement() {
   1.340 +  // Testing the behavior in presence of a bad client that finalizes
   1.341 +  // statements after the database has been closed (typically by
   1.342 +  // letting the gc finalize the statement).
   1.343 +  let db = getOpenedDatabase();
   1.344 +  let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement");
   1.345 +  db.close();
   1.346 +  stmt.finalize(); // Finalize too late - this should not crash
   1.347 +
   1.348 +  // Reset gDBConn so that later tests will get a new connection object.
   1.349 +  gDBConn = null;
   1.350 +});
   1.351 +
   1.352 +add_task(function test_asyncClose_then_release_statement() {
   1.353 +  // Testing the behavior in presence of a bad client that finalizes
   1.354 +  // statements after the database has been async closed (typically by
   1.355 +  // letting the gc finalize the statement).
   1.356 +  let db = getOpenedDatabase();
   1.357 +  let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement");
   1.358 +  yield asyncClose(db);
   1.359 +  stmt.finalize(); // Finalize too late - this should not crash
   1.360 +
   1.361 +  // Reset gDBConn so that later tests will get a new connection object.
   1.362 +  gDBConn = null;
   1.363 +});
   1.364 +
   1.365 +add_task(function test_close_fails_with_async_statement_ran()
   1.366 +{
   1.367 +  let deferred = Promise.defer();
   1.368 +  let stmt = createStatement("SELECT * FROM test");
   1.369 +  stmt.executeAsync();
   1.370 +  stmt.finalize();
   1.371 +
   1.372 +  let db = getOpenedDatabase();
   1.373 +  try {
   1.374 +    db.close();
   1.375 +    do_throw("should have thrown");
   1.376 +  }
   1.377 +  catch (e) {
   1.378 +    do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
   1.379 +  }
   1.380 +  finally {
   1.381 +    // Clean up after ourselves.
   1.382 +    db.asyncClose(function() {
   1.383 +      // Reset gDBConn so that later tests will get a new connection object.
   1.384 +      gDBConn = null;
   1.385 +      deferred.resolve();
   1.386 +    });
   1.387 +  }
   1.388 +  yield deferred.promise;
   1.389 +});
   1.390 +
   1.391 +add_task(function test_clone_optional_param()
   1.392 +{
   1.393 +  let db1 = getService().openUnsharedDatabase(getTestDB());
   1.394 +  let db2 = db1.clone();
   1.395 +  do_check_true(db2.connectionReady);
   1.396 +
   1.397 +  // A write statement should not fail here.
   1.398 +  let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
   1.399 +  stmt.params.name = "dwitte";
   1.400 +  stmt.execute();
   1.401 +  stmt.finalize();
   1.402 +
   1.403 +  // And a read statement should succeed.
   1.404 +  stmt = db2.createStatement("SELECT * FROM test");
   1.405 +  do_check_true(stmt.executeStep());
   1.406 +  stmt.finalize();
   1.407 +
   1.408 +  // Additionally check that it is a connection on the same database.
   1.409 +  do_check_true(db1.databaseFile.equals(db2.databaseFile));
   1.410 +
   1.411 +  db1.close();
   1.412 +  db2.close();
   1.413 +});
   1.414 +
   1.415 +function standardAsyncTest(promisedDB, name, shouldInit = false) {
   1.416 +  do_print("Performing standard async test " + name);
   1.417 +
   1.418 +  let adb = yield promisedDB;
   1.419 +  do_check_true(adb instanceof Ci.mozIStorageAsyncConnection);
   1.420 +  do_check_false(adb instanceof Ci.mozIStorageConnection);
   1.421 +
   1.422 +  if (shouldInit) {
   1.423 +    let stmt = adb.createAsyncStatement("CREATE TABLE test(name TEXT)");
   1.424 +    yield executeAsync(stmt);
   1.425 +    stmt.finalize();
   1.426 +  }
   1.427 +
   1.428 +  // Generate a name to insert and fetch back
   1.429 +  name = "worker bee " + Math.random() + " (" + name + ")";
   1.430 +
   1.431 +  let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
   1.432 +  stmt.params.name = name;
   1.433 +  let result = yield executeAsync(stmt);
   1.434 +  do_print("Request complete");
   1.435 +  stmt.finalize();
   1.436 +  do_check_true(Components.isSuccessCode(result));
   1.437 +  do_print("Extracting data");
   1.438 +  stmt = adb.createAsyncStatement("SELECT * FROM test");
   1.439 +  let found = false;
   1.440 +  yield executeAsync(stmt, function(result) {
   1.441 +    do_print("Data has been extracted");
   1.442 +
   1.443 +    for (let row = result.getNextRow(); row != null; row = result.getNextRow()) {
   1.444 +      if (row.getResultByName("name") == name) {
   1.445 +        found = true;
   1.446 +      break;
   1.447 +      }
   1.448 +    }
   1.449 +  });
   1.450 +  do_check_true(found);
   1.451 +  stmt.finalize();
   1.452 +  yield asyncClose(adb);
   1.453 +
   1.454 +  do_print("Standard async test " + name + " complete");
   1.455 +}
   1.456 +
   1.457 +add_task(function test_open_async() {
   1.458 +  yield standardAsyncTest(openAsyncDatabase(getTestDB(), null), "default");
   1.459 +  yield standardAsyncTest(openAsyncDatabase(getTestDB()), "no optional arg");
   1.460 +  yield standardAsyncTest(openAsyncDatabase(getTestDB(),
   1.461 +    {shared: false, growthIncrement: 54}), "non-default options");
   1.462 +  yield standardAsyncTest(openAsyncDatabase("memory"),
   1.463 +    "in-memory database", true);
   1.464 +  yield standardAsyncTest(openAsyncDatabase("memory",
   1.465 +    {shared: false, growthIncrement: 54}),
   1.466 +    "in-memory database and options", true);
   1.467 +
   1.468 +  do_print("Testing async opening with bogus options 1");
   1.469 +  let raised = false;
   1.470 +  let adb = null;
   1.471 +  try {
   1.472 +    adb = yield openAsyncDatabase(getTestDB(), {shared: "forty-two"});
   1.473 +  } catch (ex) {
   1.474 +    raised = true;
   1.475 +  } finally {
   1.476 +    if (adb) {
   1.477 +      yield asyncClose(adb);
   1.478 +    }
   1.479 +  }
   1.480 +  do_check_true(raised);
   1.481 +
   1.482 +  do_print("Testing async opening with bogus options 2");
   1.483 +  raised = false;
   1.484 +  adb = null;
   1.485 +  try {
   1.486 +    adb = yield openAsyncDatabase(getTestDB(), {growthIncrement: "forty-two"});
   1.487 +  } catch (ex) {
   1.488 +    raised = true;
   1.489 +  } finally {
   1.490 +    if (adb) {
   1.491 +      yield asyncClose(adb);
   1.492 +    }
   1.493 +  }
   1.494 +  do_check_true(raised);
   1.495 +});
   1.496 +
   1.497 +
   1.498 +add_task(function test_async_open_with_shared_cache() {
   1.499 +  do_print("Testing that opening with a shared cache doesn't break stuff");
   1.500 +  let adb = yield openAsyncDatabase(getTestDB(), {shared: true});
   1.501 +
   1.502 +  let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
   1.503 +  stmt.params.name = "clockworker";
   1.504 +  let result = yield executeAsync(stmt);
   1.505 +  do_print("Request complete");
   1.506 +  stmt.finalize();
   1.507 +  do_check_true(Components.isSuccessCode(result));
   1.508 +  do_print("Extracting data");
   1.509 +  stmt = adb.createAsyncStatement("SELECT * FROM test");
   1.510 +  let found = false;
   1.511 +  yield executeAsync(stmt, function(result) {
   1.512 +    do_print("Data has been extracted");
   1.513 +
   1.514 +    for (let row = result.getNextRow(); row != null; row = result.getNextRow()) {
   1.515 +      if (row.getResultByName("name") == "clockworker") {
   1.516 +        found = true;
   1.517 +      break;
   1.518 +      }
   1.519 +    }
   1.520 +  });
   1.521 +  do_check_true(found);
   1.522 +  stmt.finalize();
   1.523 +  yield asyncClose(adb);
   1.524 +});
   1.525 +
   1.526 +add_task(function test_clone_trivial_async()
   1.527 +{
   1.528 +  let db1 = getService().openDatabase(getTestDB());
   1.529 +  do_print("Opened adb1");
   1.530 +  do_check_true(db1 instanceof Ci.mozIStorageAsyncConnection);
   1.531 +  let adb2 = yield asyncClone(db1, true);
   1.532 +  do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection);
   1.533 +  do_print("Cloned to adb2");
   1.534 +  db1.close();
   1.535 +  do_print("Closed db1");
   1.536 +  yield asyncClose(adb2);
   1.537 +});
   1.538 +
   1.539 +add_task(function test_clone_no_optional_param_async()
   1.540 +{
   1.541 +  "use strict";
   1.542 +  do_print("Testing async cloning");
   1.543 +  let adb1 = yield openAsyncDatabase(getTestDB(), null);
   1.544 +  do_check_true(adb1 instanceof Ci.mozIStorageAsyncConnection);
   1.545 +
   1.546 +  do_print("Cloning database");
   1.547 +  do_check_true(Components.isSuccessCode(result));
   1.548 +
   1.549 +  let adb2 = yield asyncClone(adb1);
   1.550 +  do_print("Testing that the cloned db is a mozIStorageAsyncConnection " +
   1.551 +           "and not a mozIStorageConnection");
   1.552 +  do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection);
   1.553 +  do_check_false(adb2 instanceof Ci.mozIStorageConnection);
   1.554 +
   1.555 +  do_print("Inserting data into source db");
   1.556 +  let stmt = adb1.
   1.557 +               createAsyncStatement("INSERT INTO test (name) VALUES (:name)");
   1.558 +
   1.559 +  stmt.params.name = "yoric";
   1.560 +  let result = yield executeAsync(stmt);
   1.561 +  do_print("Request complete");
   1.562 +  stmt.finalize();
   1.563 +  do_check_true(Components.isSuccessCode(result));
   1.564 +  do_print("Extracting data from clone db");
   1.565 +  stmt = adb2.createAsyncStatement("SELECT * FROM test");
   1.566 +  let found = false;
   1.567 +  yield executeAsync(stmt, function(result) {
   1.568 +    do_print("Data has been extracted");
   1.569 +
   1.570 +    for (let row = result.getNextRow(); row != null; row = result.getNextRow()) {
   1.571 +      if (row.getResultByName("name") == "yoric") {
   1.572 +        found = true;
   1.573 +      break;
   1.574 +      }
   1.575 +    }
   1.576 +  });
   1.577 +  do_check_true(found);
   1.578 +  stmt.finalize();
   1.579 +  do_print("Closing databases");
   1.580 +  yield asyncClose(adb2);
   1.581 +  do_print("First db closed");
   1.582 +
   1.583 +  yield asyncClose(adb1);
   1.584 +  do_print("Second db closed");
   1.585 +});
   1.586 +
   1.587 +add_task(function test_clone_readonly()
   1.588 +{
   1.589 +  let db1 = getService().openUnsharedDatabase(getTestDB());
   1.590 +  let db2 = db1.clone(true);
   1.591 +  do_check_true(db2.connectionReady);
   1.592 +
   1.593 +  // A write statement should fail here.
   1.594 +  let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
   1.595 +  stmt.params.name = "reed";
   1.596 +  expectError(Cr.NS_ERROR_FILE_READ_ONLY, function() stmt.execute());
   1.597 +  stmt.finalize();
   1.598 +
   1.599 +  // And a read statement should succeed.
   1.600 +  stmt = db2.createStatement("SELECT * FROM test");
   1.601 +  do_check_true(stmt.executeStep());
   1.602 +  stmt.finalize();
   1.603 +
   1.604 +  db1.close();
   1.605 +  db2.close();
   1.606 +});
   1.607 +
   1.608 +add_task(function test_clone_shared_readonly()
   1.609 +{
   1.610 +  let db1 = getService().openDatabase(getTestDB());
   1.611 +  let db2 = db1.clone(true);
   1.612 +  do_check_true(db2.connectionReady);
   1.613 +
   1.614 +  let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)");
   1.615 +  stmt.params.name = "parker";
   1.616 +  // TODO currently SQLite does not actually work correctly here.  The behavior
   1.617 +  //      we want is commented out, and the current behavior is being tested
   1.618 +  //      for.  Our IDL comments will have to be updated when this starts to
   1.619 +  //      work again.
   1.620 +  stmt.execute();
   1.621 +  // expectError(Components.results.NS_ERROR_FILE_READ_ONLY, function() stmt.execute());
   1.622 +  stmt.finalize();
   1.623 +
   1.624 +  // And a read statement should succeed.
   1.625 +  stmt = db2.createStatement("SELECT * FROM test");
   1.626 +  do_check_true(stmt.executeStep());
   1.627 +  stmt.finalize();
   1.628 +
   1.629 +  db1.close();
   1.630 +  db2.close();
   1.631 +});
   1.632 +
   1.633 +add_task(function test_close_clone_fails()
   1.634 +{
   1.635 +  let calls = [
   1.636 +    "openDatabase",
   1.637 +    "openUnsharedDatabase",
   1.638 +  ];
   1.639 +  calls.forEach(function(methodName) {
   1.640 +    let db = getService()[methodName](getTestDB());
   1.641 +    db.close();
   1.642 +    expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone());
   1.643 +  });
   1.644 +});
   1.645 +
   1.646 +add_task(function test_memory_clone_fails()
   1.647 +{
   1.648 +  let db = getService().openSpecialDatabase("memory");
   1.649 +  db.close();
   1.650 +  expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone());
   1.651 +});
   1.652 +
   1.653 +add_task(function test_clone_copies_functions()
   1.654 +{
   1.655 +  const FUNC_NAME = "test_func";
   1.656 +  let calls = [
   1.657 +    "openDatabase",
   1.658 +    "openUnsharedDatabase",
   1.659 +  ];
   1.660 +  let functionMethods = [
   1.661 +    "createFunction",
   1.662 +    "createAggregateFunction",
   1.663 +  ];
   1.664 +  calls.forEach(function(methodName) {
   1.665 +    [true, false].forEach(function(readOnly) {
   1.666 +      functionMethods.forEach(function(functionMethod) {
   1.667 +        let db1 = getService()[methodName](getTestDB());
   1.668 +        // Create a function for db1.
   1.669 +        db1[functionMethod](FUNC_NAME, 1, {
   1.670 +          onFunctionCall: function() 0,
   1.671 +          onStep: function() 0,
   1.672 +          onFinal: function() 0,
   1.673 +        });
   1.674 +
   1.675 +        // Clone it, and make sure the function exists still.
   1.676 +        let db2 = db1.clone(readOnly);
   1.677 +        // Note: this would fail if the function did not exist.
   1.678 +        let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test");
   1.679 +        stmt.finalize();
   1.680 +        db1.close();
   1.681 +        db2.close();
   1.682 +      });
   1.683 +    });
   1.684 +  });
   1.685 +});
   1.686 +
   1.687 +add_task(function test_clone_copies_overridden_functions()
   1.688 +{
   1.689 +  const FUNC_NAME = "lower";
   1.690 +  function test_func() {
   1.691 +    this.called = false;
   1.692 +  }
   1.693 +  test_func.prototype = {
   1.694 +    onFunctionCall: function() {
   1.695 +      this.called = true;
   1.696 +    },
   1.697 +    onStep: function() {
   1.698 +      this.called = true;
   1.699 +    },
   1.700 +    onFinal: function() 0,
   1.701 +  };
   1.702 +
   1.703 +  let calls = [
   1.704 +    "openDatabase",
   1.705 +    "openUnsharedDatabase",
   1.706 +  ];
   1.707 +  let functionMethods = [
   1.708 +    "createFunction",
   1.709 +    "createAggregateFunction",
   1.710 +  ];
   1.711 +  calls.forEach(function(methodName) {
   1.712 +    [true, false].forEach(function(readOnly) {
   1.713 +      functionMethods.forEach(function(functionMethod) {
   1.714 +        let db1 = getService()[methodName](getTestDB());
   1.715 +        // Create a function for db1.
   1.716 +        let func = new test_func();
   1.717 +        db1[functionMethod](FUNC_NAME, 1, func);
   1.718 +        do_check_false(func.called);
   1.719 +
   1.720 +        // Clone it, and make sure the function gets called.
   1.721 +        let db2 = db1.clone(readOnly);
   1.722 +        let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test");
   1.723 +        stmt.executeStep();
   1.724 +        do_check_true(func.called);
   1.725 +        stmt.finalize();
   1.726 +        db1.close();
   1.727 +        db2.close();
   1.728 +      });
   1.729 +    });
   1.730 +  });
   1.731 +});
   1.732 +
   1.733 +add_task(function test_clone_copies_pragmas()
   1.734 +{
   1.735 +  const PRAGMAS = [
   1.736 +    { name: "cache_size", value: 500, copied: true },
   1.737 +    { name: "temp_store", value: 2, copied: true },
   1.738 +    { name: "foreign_keys", value: 1, copied: true },
   1.739 +    { name: "journal_size_limit", value: 524288, copied: true },
   1.740 +    { name: "synchronous", value: 2, copied: true },
   1.741 +    { name: "wal_autocheckpoint", value: 16, copied: true },
   1.742 +    { name: "ignore_check_constraints", value: 1, copied: false },
   1.743 +  ];
   1.744 +
   1.745 +  let db1 = getService().openUnsharedDatabase(getTestDB());
   1.746 +
   1.747 +  // Sanity check initial values are different from enforced ones.
   1.748 +  PRAGMAS.forEach(function (pragma) {
   1.749 +    let stmt = db1.createStatement("PRAGMA " + pragma.name);
   1.750 +    do_check_true(stmt.executeStep());
   1.751 +    do_check_neq(pragma.value, stmt.getInt32(0));
   1.752 +    stmt.finalize();
   1.753 +  });
   1.754 +  // Execute pragmas.
   1.755 +  PRAGMAS.forEach(function (pragma) {
   1.756 +    db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value);
   1.757 +  });
   1.758 +
   1.759 +  let db2 = db1.clone();
   1.760 +  do_check_true(db2.connectionReady);
   1.761 +
   1.762 +  // Check cloned connection inherited pragma values.
   1.763 +  PRAGMAS.forEach(function (pragma) {
   1.764 +    let stmt = db2.createStatement("PRAGMA " + pragma.name);
   1.765 +    do_check_true(stmt.executeStep());
   1.766 +    let validate = pragma.copied ? do_check_eq : do_check_neq;
   1.767 +    validate(pragma.value, stmt.getInt32(0));
   1.768 +    stmt.finalize();
   1.769 +  });
   1.770 +
   1.771 +  db1.close();
   1.772 +  db2.close();
   1.773 +});
   1.774 +
   1.775 +add_task(function test_readonly_clone_copies_pragmas()
   1.776 +{
   1.777 +  const PRAGMAS = [
   1.778 +    { name: "cache_size", value: 500, copied: true },
   1.779 +    { name: "temp_store", value: 2, copied: true },
   1.780 +    { name: "foreign_keys", value: 1, copied: false },
   1.781 +    { name: "journal_size_limit", value: 524288, copied: false },
   1.782 +    { name: "synchronous", value: 2, copied: false },
   1.783 +    { name: "wal_autocheckpoint", value: 16, copied: false },
   1.784 +    { name: "ignore_check_constraints", value: 1, copied: false },
   1.785 +  ];
   1.786 +
   1.787 +  let db1 = getService().openUnsharedDatabase(getTestDB());
   1.788 +
   1.789 +  // Sanity check initial values are different from enforced ones.
   1.790 +  PRAGMAS.forEach(function (pragma) {
   1.791 +    let stmt = db1.createStatement("PRAGMA " + pragma.name);
   1.792 +    do_check_true(stmt.executeStep());
   1.793 +    do_check_neq(pragma.value, stmt.getInt32(0));
   1.794 +    stmt.finalize();
   1.795 +  });
   1.796 +  // Execute pragmas.
   1.797 +  PRAGMAS.forEach(function (pragma) {
   1.798 +    db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value);
   1.799 +  });
   1.800 +
   1.801 +  let db2 = db1.clone(true);
   1.802 +  do_check_true(db2.connectionReady);
   1.803 +
   1.804 +  // Check cloned connection inherited pragma values.
   1.805 +  PRAGMAS.forEach(function (pragma) {
   1.806 +    let stmt = db2.createStatement("PRAGMA " + pragma.name);
   1.807 +    do_check_true(stmt.executeStep());
   1.808 +    let validate = pragma.copied ? do_check_eq : do_check_neq;
   1.809 +    validate(pragma.value, stmt.getInt32(0));
   1.810 +    stmt.finalize();
   1.811 +  });
   1.812 +
   1.813 +  db1.close();
   1.814 +  db2.close();
   1.815 +});
   1.816 +
   1.817 +add_task(function test_getInterface()
   1.818 +{
   1.819 +  let db = getOpenedDatabase();
   1.820 +  let target = db.QueryInterface(Ci.nsIInterfaceRequestor)
   1.821 +                 .getInterface(Ci.nsIEventTarget);
   1.822 +  // Just check that target is non-null.  Other tests will ensure that it has
   1.823 +  // the correct value.
   1.824 +  do_check_true(target != null);
   1.825 +
   1.826 +  yield asyncClose(db);
   1.827 +  gDBConn = null;
   1.828 +});
   1.829 +
   1.830 +
   1.831 +function run_test()
   1.832 +{
   1.833 +  run_next_test();
   1.834 +}

mercurial