storage/test/unit/test_statement_executeAsync.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * This file tests the functionality of mozIStorageBaseStatement::executeAsync
michael@0 7 * for both mozIStorageStatement and mozIStorageAsyncStatement.
michael@0 8 */
michael@0 9
michael@0 10 const INTEGER = 1;
michael@0 11 const TEXT = "this is test text";
michael@0 12 const REAL = 3.23;
michael@0 13 const BLOB = [1, 2];
michael@0 14
michael@0 15 /**
michael@0 16 * Execute the given statement asynchronously, spinning an event loop until the
michael@0 17 * async statement completes.
michael@0 18 *
michael@0 19 * @param aStmt
michael@0 20 * The statement to execute.
michael@0 21 * @param [aOptions={}]
michael@0 22 * @param [aOptions.error=false]
michael@0 23 * If true we should expect an error whose code we do not care about. If
michael@0 24 * a numeric value, that's the error code we expect and require. If we
michael@0 25 * are expecting an error, we expect a completion reason of REASON_ERROR.
michael@0 26 * Otherwise we expect no error notification and a completion reason of
michael@0 27 * REASON_FINISHED.
michael@0 28 * @param [aOptions.cancel]
michael@0 29 * If true we cancel the pending statement and additionally return the
michael@0 30 * pending statement in case you want to further manipulate it.
michael@0 31 * @param [aOptions.returnPending=false]
michael@0 32 * If true we keep the pending statement around and return it to you. We
michael@0 33 * normally avoid doing this to try and minimize the amount of time a
michael@0 34 * reference is held to the returned pending statement.
michael@0 35 * @param [aResults]
michael@0 36 * If omitted, we assume no results rows are expected. If it is a
michael@0 37 * number, we assume it is the number of results rows expected. If it is
michael@0 38 * a function, we assume it is a function that takes the 1) result row
michael@0 39 * number, 2) result tuple, 3) call stack for the original call to
michael@0 40 * execAsync as arguments. If it is a list, we currently assume it is a
michael@0 41 * list of functions where each function is intended to evaluate the
michael@0 42 * result row at that ordinal position and takes the result tuple and
michael@0 43 * the call stack for the original call.
michael@0 44 */
michael@0 45 function execAsync(aStmt, aOptions, aResults)
michael@0 46 {
michael@0 47 let caller = Components.stack.caller;
michael@0 48 if (aOptions == null)
michael@0 49 aOptions = {};
michael@0 50
michael@0 51 let resultsExpected;
michael@0 52 let resultsChecker;
michael@0 53 if (aResults == null) {
michael@0 54 resultsExpected = 0;
michael@0 55 }
michael@0 56 else if (typeof(aResults) == "number") {
michael@0 57 resultsExpected = aResults;
michael@0 58 }
michael@0 59 else if (typeof(aResults) == "function") {
michael@0 60 resultsChecker = aResults;
michael@0 61 }
michael@0 62 else { // array
michael@0 63 resultsExpected = aResults.length;
michael@0 64 resultsChecker = function(aResultNum, aTup, aCaller) {
michael@0 65 aResults[aResultNum](aTup, aCaller);
michael@0 66 };
michael@0 67 }
michael@0 68 let resultsSeen = 0;
michael@0 69
michael@0 70 let errorCodeExpected = false;
michael@0 71 let reasonExpected = Ci.mozIStorageStatementCallback.REASON_FINISHED;
michael@0 72 let altReasonExpected = null;
michael@0 73 if ("error" in aOptions) {
michael@0 74 errorCodeExpected = aOptions.error;
michael@0 75 if (errorCodeExpected)
michael@0 76 reasonExpected = Ci.mozIStorageStatementCallback.REASON_ERROR;
michael@0 77 }
michael@0 78 let errorCodeSeen = false;
michael@0 79
michael@0 80 if ("cancel" in aOptions && aOptions.cancel)
michael@0 81 altReasonExpected = Ci.mozIStorageStatementCallback.REASON_CANCELED;
michael@0 82
michael@0 83 let completed = false;
michael@0 84
michael@0 85 let listener = {
michael@0 86 handleResult: function(aResultSet)
michael@0 87 {
michael@0 88 let row, resultsSeenThisCall = 0;
michael@0 89 while ((row = aResultSet.getNextRow()) != null) {
michael@0 90 if (resultsChecker)
michael@0 91 resultsChecker(resultsSeen, row, caller);
michael@0 92 resultsSeen++;
michael@0 93 resultsSeenThisCall++;
michael@0 94 }
michael@0 95
michael@0 96 if (!resultsSeenThisCall)
michael@0 97 do_throw("handleResult invoked with 0 result rows!");
michael@0 98 },
michael@0 99 handleError: function(aError)
michael@0 100 {
michael@0 101 if (errorCodeSeen != false)
michael@0 102 do_throw("handleError called when we already had an error!");
michael@0 103 errorCodeSeen = aError.result;
michael@0 104 },
michael@0 105 handleCompletion: function(aReason)
michael@0 106 {
michael@0 107 if (completed) // paranoia check
michael@0 108 do_throw("Received a second handleCompletion notification!", caller);
michael@0 109
michael@0 110 if (resultsSeen != resultsExpected)
michael@0 111 do_throw("Expected " + resultsExpected + " rows of results but " +
michael@0 112 "got " + resultsSeen + " rows!", caller);
michael@0 113
michael@0 114 if (errorCodeExpected == true && errorCodeSeen == false)
michael@0 115 do_throw("Expected an error, but did not see one.", caller);
michael@0 116 else if (errorCodeExpected != errorCodeSeen)
michael@0 117 do_throw("Expected error code " + errorCodeExpected + " but got " +
michael@0 118 errorCodeSeen, caller);
michael@0 119
michael@0 120 if (aReason != reasonExpected && aReason != altReasonExpected)
michael@0 121 do_throw("Expected reason " + reasonExpected +
michael@0 122 (altReasonExpected ? (" or " + altReasonExpected) : "") +
michael@0 123 " but got " + aReason, caller);
michael@0 124
michael@0 125 completed = true;
michael@0 126 }
michael@0 127 };
michael@0 128
michael@0 129 let pending;
michael@0 130 // Only get a pending reference if we're supposed to do.
michael@0 131 // (note: This does not stop XPConnect from holding onto one currently.)
michael@0 132 if (("cancel" in aOptions && aOptions.cancel) ||
michael@0 133 ("returnPending" in aOptions && aOptions.returnPending)) {
michael@0 134 pending = aStmt.executeAsync(listener);
michael@0 135 }
michael@0 136 else {
michael@0 137 aStmt.executeAsync(listener);
michael@0 138 }
michael@0 139
michael@0 140 if ("cancel" in aOptions && aOptions.cancel)
michael@0 141 pending.cancel();
michael@0 142
michael@0 143 let curThread = Components.classes["@mozilla.org/thread-manager;1"]
michael@0 144 .getService().currentThread;
michael@0 145 while (!completed && !_quit)
michael@0 146 curThread.processNextEvent(true);
michael@0 147
michael@0 148 return pending;
michael@0 149 }
michael@0 150
michael@0 151 /**
michael@0 152 * Make sure that illegal SQL generates the expected runtime error and does not
michael@0 153 * result in any crashes. Async-only since the synchronous case generates the
michael@0 154 * error synchronously (and is tested elsewhere).
michael@0 155 */
michael@0 156 function test_illegal_sql_async_deferred()
michael@0 157 {
michael@0 158 // gibberish
michael@0 159 let stmt = makeTestStatement("I AM A ROBOT. DO AS I SAY.");
michael@0 160 execAsync(stmt, {error: Ci.mozIStorageError.ERROR});
michael@0 161 stmt.finalize();
michael@0 162
michael@0 163 // legal SQL syntax, but with semantics issues.
michael@0 164 stmt = makeTestStatement("SELECT destination FROM funkytown");
michael@0 165 execAsync(stmt, {error: Ci.mozIStorageError.ERROR});
michael@0 166 stmt.finalize();
michael@0 167
michael@0 168 run_next_test();
michael@0 169 }
michael@0 170 test_illegal_sql_async_deferred.asyncOnly = true;
michael@0 171
michael@0 172 function test_create_table()
michael@0 173 {
michael@0 174 // Ensure our table doesn't exist
michael@0 175 do_check_false(getOpenedDatabase().tableExists("test"));
michael@0 176
michael@0 177 var stmt = makeTestStatement(
michael@0 178 "CREATE TABLE test (" +
michael@0 179 "id INTEGER, " +
michael@0 180 "string TEXT, " +
michael@0 181 "number REAL, " +
michael@0 182 "nuller NULL, " +
michael@0 183 "blober BLOB" +
michael@0 184 ")"
michael@0 185 );
michael@0 186 execAsync(stmt);
michael@0 187 stmt.finalize();
michael@0 188
michael@0 189 // Check that the table has been created
michael@0 190 do_check_true(getOpenedDatabase().tableExists("test"));
michael@0 191
michael@0 192 // Verify that it's created correctly (this will throw if it wasn't)
michael@0 193 let checkStmt = getOpenedDatabase().createStatement(
michael@0 194 "SELECT id, string, number, nuller, blober FROM test"
michael@0 195 );
michael@0 196 checkStmt.finalize();
michael@0 197 run_next_test();
michael@0 198 }
michael@0 199
michael@0 200 function test_add_data()
michael@0 201 {
michael@0 202 var stmt = makeTestStatement(
michael@0 203 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 204 "VALUES (?, ?, ?, ?, ?)"
michael@0 205 );
michael@0 206 stmt.bindBlobByIndex(4, BLOB, BLOB.length);
michael@0 207 stmt.bindByIndex(3, null);
michael@0 208 stmt.bindByIndex(2, REAL);
michael@0 209 stmt.bindByIndex(1, TEXT);
michael@0 210 stmt.bindByIndex(0, INTEGER);
michael@0 211
michael@0 212 execAsync(stmt);
michael@0 213 stmt.finalize();
michael@0 214
michael@0 215 // Check that the result is in the table
michael@0 216 verifyQuery("SELECT string, number, nuller, blober FROM test WHERE id = ?",
michael@0 217 INTEGER,
michael@0 218 [TEXT, REAL, null, BLOB]);
michael@0 219 run_next_test();
michael@0 220 }
michael@0 221
michael@0 222 function test_get_data()
michael@0 223 {
michael@0 224 var stmt = makeTestStatement(
michael@0 225 "SELECT string, number, nuller, blober, id FROM test WHERE id = ?"
michael@0 226 );
michael@0 227 stmt.bindByIndex(0, INTEGER);
michael@0 228 execAsync(stmt, {}, [
michael@0 229 function(tuple)
michael@0 230 {
michael@0 231 do_check_neq(null, tuple);
michael@0 232
michael@0 233 // Check that it's what we expect
michael@0 234 do_check_false(tuple.getIsNull(0));
michael@0 235 do_check_eq(tuple.getResultByName("string"), tuple.getResultByIndex(0));
michael@0 236 do_check_eq(TEXT, tuple.getResultByName("string"));
michael@0 237 do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_TEXT,
michael@0 238 tuple.getTypeOfIndex(0));
michael@0 239
michael@0 240 do_check_false(tuple.getIsNull(1));
michael@0 241 do_check_eq(tuple.getResultByName("number"), tuple.getResultByIndex(1));
michael@0 242 do_check_eq(REAL, tuple.getResultByName("number"));
michael@0 243 do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT,
michael@0 244 tuple.getTypeOfIndex(1));
michael@0 245
michael@0 246 do_check_true(tuple.getIsNull(2));
michael@0 247 do_check_eq(tuple.getResultByName("nuller"), tuple.getResultByIndex(2));
michael@0 248 do_check_eq(null, tuple.getResultByName("nuller"));
michael@0 249 do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_NULL,
michael@0 250 tuple.getTypeOfIndex(2));
michael@0 251
michael@0 252 do_check_false(tuple.getIsNull(3));
michael@0 253 var blobByName = tuple.getResultByName("blober");
michael@0 254 do_check_eq(BLOB.length, blobByName.length);
michael@0 255 var blobByIndex = tuple.getResultByIndex(3);
michael@0 256 do_check_eq(BLOB.length, blobByIndex.length);
michael@0 257 for (var i = 0; i < BLOB.length; i++) {
michael@0 258 do_check_eq(BLOB[i], blobByName[i]);
michael@0 259 do_check_eq(BLOB[i], blobByIndex[i]);
michael@0 260 }
michael@0 261 var count = { value: 0 };
michael@0 262 var blob = { value: null };
michael@0 263 tuple.getBlob(3, count, blob);
michael@0 264 do_check_eq(BLOB.length, count.value);
michael@0 265 for (var i = 0; i < BLOB.length; i++)
michael@0 266 do_check_eq(BLOB[i], blob.value[i]);
michael@0 267 do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_BLOB,
michael@0 268 tuple.getTypeOfIndex(3));
michael@0 269
michael@0 270 do_check_false(tuple.getIsNull(4));
michael@0 271 do_check_eq(tuple.getResultByName("id"), tuple.getResultByIndex(4));
michael@0 272 do_check_eq(INTEGER, tuple.getResultByName("id"));
michael@0 273 do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER,
michael@0 274 tuple.getTypeOfIndex(4));
michael@0 275 }]);
michael@0 276 stmt.finalize();
michael@0 277 run_next_test();
michael@0 278 }
michael@0 279
michael@0 280 function test_tuple_out_of_bounds()
michael@0 281 {
michael@0 282 var stmt = makeTestStatement(
michael@0 283 "SELECT string FROM test"
michael@0 284 );
michael@0 285 execAsync(stmt, {}, [
michael@0 286 function(tuple) {
michael@0 287 do_check_neq(null, tuple);
michael@0 288
michael@0 289 // Check all out of bounds - should throw
michael@0 290 var methods = [
michael@0 291 "getTypeOfIndex",
michael@0 292 "getInt32",
michael@0 293 "getInt64",
michael@0 294 "getDouble",
michael@0 295 "getUTF8String",
michael@0 296 "getString",
michael@0 297 "getIsNull",
michael@0 298 ];
michael@0 299 for (var i in methods) {
michael@0 300 try {
michael@0 301 tuple[methods[i]](tuple.numEntries);
michael@0 302 do_throw("did not throw :(");
michael@0 303 }
michael@0 304 catch (e) {
michael@0 305 do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
michael@0 306 }
michael@0 307 }
michael@0 308
michael@0 309 // getBlob requires more args...
michael@0 310 try {
michael@0 311 var blob = { value: null };
michael@0 312 var size = { value: 0 };
michael@0 313 tuple.getBlob(tuple.numEntries, blob, size);
michael@0 314 do_throw("did not throw :(");
michael@0 315 }
michael@0 316 catch (e) {
michael@0 317 do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
michael@0 318 }
michael@0 319 }]);
michael@0 320 stmt.finalize();
michael@0 321 run_next_test();
michael@0 322 }
michael@0 323
michael@0 324 function test_no_listener_works_on_success()
michael@0 325 {
michael@0 326 var stmt = makeTestStatement(
michael@0 327 "DELETE FROM test WHERE id = ?"
michael@0 328 );
michael@0 329 stmt.bindByIndex(0, 0);
michael@0 330 stmt.executeAsync();
michael@0 331 stmt.finalize();
michael@0 332
michael@0 333 // Run the next test.
michael@0 334 run_next_test();
michael@0 335 }
michael@0 336
michael@0 337 function test_no_listener_works_on_results()
michael@0 338 {
michael@0 339 var stmt = makeTestStatement(
michael@0 340 "SELECT ?"
michael@0 341 );
michael@0 342 stmt.bindByIndex(0, 1);
michael@0 343 stmt.executeAsync();
michael@0 344 stmt.finalize();
michael@0 345
michael@0 346 // Run the next test.
michael@0 347 run_next_test();
michael@0 348 }
michael@0 349
michael@0 350 function test_no_listener_works_on_error()
michael@0 351 {
michael@0 352 // commit without a transaction will trigger an error
michael@0 353 var stmt = makeTestStatement(
michael@0 354 "COMMIT"
michael@0 355 );
michael@0 356 stmt.executeAsync();
michael@0 357 stmt.finalize();
michael@0 358
michael@0 359 // Run the next test.
michael@0 360 run_next_test();
michael@0 361 }
michael@0 362
michael@0 363 function test_partial_listener_works()
michael@0 364 {
michael@0 365 var stmt = makeTestStatement(
michael@0 366 "DELETE FROM test WHERE id = ?"
michael@0 367 );
michael@0 368 stmt.bindByIndex(0, 0);
michael@0 369 stmt.executeAsync({
michael@0 370 handleResult: function(aResultSet)
michael@0 371 {
michael@0 372 }
michael@0 373 });
michael@0 374 stmt.executeAsync({
michael@0 375 handleError: function(aError)
michael@0 376 {
michael@0 377 }
michael@0 378 });
michael@0 379 stmt.executeAsync({
michael@0 380 handleCompletion: function(aReason)
michael@0 381 {
michael@0 382 }
michael@0 383 });
michael@0 384 stmt.finalize();
michael@0 385
michael@0 386 // Run the next test.
michael@0 387 run_next_test();
michael@0 388 }
michael@0 389
michael@0 390 /**
michael@0 391 * Dubious cancellation test that depends on system loading may or may not
michael@0 392 * succeed in canceling things. It does at least test if calling cancel blows
michael@0 393 * up. test_AsyncCancellation in test_true_async.cpp is our test that canceling
michael@0 394 * actually works correctly.
michael@0 395 */
michael@0 396 function test_immediate_cancellation()
michael@0 397 {
michael@0 398 var stmt = makeTestStatement(
michael@0 399 "DELETE FROM test WHERE id = ?"
michael@0 400 );
michael@0 401 stmt.bindByIndex(0, 0);
michael@0 402 execAsync(stmt, {cancel: true});
michael@0 403 stmt.finalize();
michael@0 404 run_next_test();
michael@0 405 }
michael@0 406
michael@0 407 /**
michael@0 408 * Test that calling cancel twice throws the second time.
michael@0 409 */
michael@0 410 function test_double_cancellation()
michael@0 411 {
michael@0 412 var stmt = makeTestStatement(
michael@0 413 "DELETE FROM test WHERE id = ?"
michael@0 414 );
michael@0 415 stmt.bindByIndex(0, 0);
michael@0 416 let pendingStatement = execAsync(stmt, {cancel: true});
michael@0 417 // And cancel again - expect an exception
michael@0 418 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 419 function() pendingStatement.cancel());
michael@0 420
michael@0 421 stmt.finalize();
michael@0 422 run_next_test();
michael@0 423 }
michael@0 424
michael@0 425 /**
michael@0 426 * Verify that nothing untoward happens if we try and cancel something after it
michael@0 427 * has fully run to completion.
michael@0 428 */
michael@0 429 function test_cancellation_after_execution()
michael@0 430 {
michael@0 431 var stmt = makeTestStatement(
michael@0 432 "DELETE FROM test WHERE id = ?"
michael@0 433 );
michael@0 434 stmt.bindByIndex(0, 0);
michael@0 435 let pendingStatement = execAsync(stmt, {returnPending: true});
michael@0 436 // (the statement has fully executed at this point)
michael@0 437 // canceling after the statement has run to completion should not throw!
michael@0 438 pendingStatement.cancel();
michael@0 439
michael@0 440 stmt.finalize();
michael@0 441 run_next_test();
michael@0 442 }
michael@0 443
michael@0 444 /**
michael@0 445 * Verifies that a single statement can be executed more than once. Might once
michael@0 446 * have been intended to also ensure that callback notifications were not
michael@0 447 * incorrectly interleaved, but that part was brittle (it's totally fine for
michael@0 448 * handleResult to get called multiple times) and not comprehensive.
michael@0 449 */
michael@0 450 function test_double_execute()
michael@0 451 {
michael@0 452 var stmt = makeTestStatement(
michael@0 453 "SELECT 1"
michael@0 454 );
michael@0 455 execAsync(stmt, null, 1);
michael@0 456 execAsync(stmt, null, 1);
michael@0 457 stmt.finalize();
michael@0 458 run_next_test();
michael@0 459 }
michael@0 460
michael@0 461 function test_finalized_statement_does_not_crash()
michael@0 462 {
michael@0 463 var stmt = makeTestStatement(
michael@0 464 "SELECT * FROM TEST"
michael@0 465 );
michael@0 466 stmt.finalize();
michael@0 467 // we are concerned about a crash here; an error is fine.
michael@0 468 try {
michael@0 469 stmt.executeAsync();
michael@0 470 }
michael@0 471 catch (ex) {}
michael@0 472
michael@0 473 // Run the next test.
michael@0 474 run_next_test();
michael@0 475 }
michael@0 476
michael@0 477 /**
michael@0 478 * Bind by mozIStorageBindingParams on the mozIStorageBaseStatement by index.
michael@0 479 */
michael@0 480 function test_bind_direct_binding_params_by_index()
michael@0 481 {
michael@0 482 var stmt = makeTestStatement(
michael@0 483 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 484 "VALUES (?, ?, ?, ?, ?)"
michael@0 485 );
michael@0 486 let insertId = nextUniqueId++;
michael@0 487 stmt.bindByIndex(0, insertId);
michael@0 488 stmt.bindByIndex(1, TEXT);
michael@0 489 stmt.bindByIndex(2, REAL);
michael@0 490 stmt.bindByIndex(3, null);
michael@0 491 stmt.bindBlobByIndex(4, BLOB, BLOB.length);
michael@0 492 execAsync(stmt);
michael@0 493 stmt.finalize();
michael@0 494 verifyQuery("SELECT string, number, nuller, blober FROM test WHERE id = ?",
michael@0 495 insertId,
michael@0 496 [TEXT, REAL, null, BLOB]);
michael@0 497 run_next_test();
michael@0 498 }
michael@0 499
michael@0 500 /**
michael@0 501 * Bind by mozIStorageBindingParams on the mozIStorageBaseStatement by name.
michael@0 502 */
michael@0 503 function test_bind_direct_binding_params_by_name()
michael@0 504 {
michael@0 505 var stmt = makeTestStatement(
michael@0 506 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 507 "VALUES (:int, :text, :real, :null, :blob)"
michael@0 508 );
michael@0 509 let insertId = nextUniqueId++;
michael@0 510 stmt.bindByName("int", insertId);
michael@0 511 stmt.bindByName("text", TEXT);
michael@0 512 stmt.bindByName("real", REAL);
michael@0 513 stmt.bindByName("null", null);
michael@0 514 stmt.bindBlobByName("blob", BLOB, BLOB.length);
michael@0 515 execAsync(stmt);
michael@0 516 stmt.finalize();
michael@0 517 verifyQuery("SELECT string, number, nuller, blober FROM test WHERE id = ?",
michael@0 518 insertId,
michael@0 519 [TEXT, REAL, null, BLOB]);
michael@0 520 run_next_test();
michael@0 521 }
michael@0 522
michael@0 523 function test_bind_js_params_helper_by_index()
michael@0 524 {
michael@0 525 var stmt = makeTestStatement(
michael@0 526 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 527 "VALUES (?, ?, ?, ?, NULL)"
michael@0 528 );
michael@0 529 let insertId = nextUniqueId++;
michael@0 530 // we cannot bind blobs this way; no blober
michael@0 531 stmt.params[3] = null;
michael@0 532 stmt.params[2] = REAL;
michael@0 533 stmt.params[1] = TEXT;
michael@0 534 stmt.params[0] = insertId;
michael@0 535 execAsync(stmt);
michael@0 536 stmt.finalize();
michael@0 537 verifyQuery("SELECT string, number, nuller FROM test WHERE id = ?", insertId,
michael@0 538 [TEXT, REAL, null]);
michael@0 539 run_next_test();
michael@0 540 }
michael@0 541
michael@0 542 function test_bind_js_params_helper_by_name()
michael@0 543 {
michael@0 544 var stmt = makeTestStatement(
michael@0 545 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 546 "VALUES (:int, :text, :real, :null, NULL)"
michael@0 547 );
michael@0 548 let insertId = nextUniqueId++;
michael@0 549 // we cannot bind blobs this way; no blober
michael@0 550 stmt.params.null = null;
michael@0 551 stmt.params.real = REAL;
michael@0 552 stmt.params.text = TEXT;
michael@0 553 stmt.params.int = insertId;
michael@0 554 execAsync(stmt);
michael@0 555 stmt.finalize();
michael@0 556 verifyQuery("SELECT string, number, nuller FROM test WHERE id = ?", insertId,
michael@0 557 [TEXT, REAL, null]);
michael@0 558 run_next_test();
michael@0 559 }
michael@0 560
michael@0 561 function test_bind_multiple_rows_by_index()
michael@0 562 {
michael@0 563 const AMOUNT_TO_ADD = 5;
michael@0 564 var stmt = makeTestStatement(
michael@0 565 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 566 "VALUES (?, ?, ?, ?, ?)"
michael@0 567 );
michael@0 568 var array = stmt.newBindingParamsArray();
michael@0 569 for (let i = 0; i < AMOUNT_TO_ADD; i++) {
michael@0 570 let bp = array.newBindingParams();
michael@0 571 bp.bindByIndex(0, INTEGER);
michael@0 572 bp.bindByIndex(1, TEXT);
michael@0 573 bp.bindByIndex(2, REAL);
michael@0 574 bp.bindByIndex(3, null);
michael@0 575 bp.bindBlobByIndex(4, BLOB, BLOB.length);
michael@0 576 array.addParams(bp);
michael@0 577 do_check_eq(array.length, i + 1);
michael@0 578 }
michael@0 579 stmt.bindParameters(array);
michael@0 580
michael@0 581 let rowCount = getTableRowCount("test");
michael@0 582 execAsync(stmt);
michael@0 583 do_check_eq(rowCount + AMOUNT_TO_ADD, getTableRowCount("test"));
michael@0 584 stmt.finalize();
michael@0 585 run_next_test();
michael@0 586 }
michael@0 587
michael@0 588 function test_bind_multiple_rows_by_name()
michael@0 589 {
michael@0 590 const AMOUNT_TO_ADD = 5;
michael@0 591 var stmt = makeTestStatement(
michael@0 592 "INSERT INTO test (id, string, number, nuller, blober) " +
michael@0 593 "VALUES (:int, :text, :real, :null, :blob)"
michael@0 594 );
michael@0 595 var array = stmt.newBindingParamsArray();
michael@0 596 for (let i = 0; i < AMOUNT_TO_ADD; i++) {
michael@0 597 let bp = array.newBindingParams();
michael@0 598 bp.bindByName("int", INTEGER);
michael@0 599 bp.bindByName("text", TEXT);
michael@0 600 bp.bindByName("real", REAL);
michael@0 601 bp.bindByName("null", null);
michael@0 602 bp.bindBlobByName("blob", BLOB, BLOB.length);
michael@0 603 array.addParams(bp);
michael@0 604 do_check_eq(array.length, i + 1);
michael@0 605 }
michael@0 606 stmt.bindParameters(array);
michael@0 607
michael@0 608 let rowCount = getTableRowCount("test");
michael@0 609 execAsync(stmt);
michael@0 610 do_check_eq(rowCount + AMOUNT_TO_ADD, getTableRowCount("test"));
michael@0 611 stmt.finalize();
michael@0 612 run_next_test();
michael@0 613 }
michael@0 614
michael@0 615 /**
michael@0 616 * Verify that a mozIStorageStatement instance throws immediately when we
michael@0 617 * try and bind to an illegal index.
michael@0 618 */
michael@0 619 function test_bind_out_of_bounds_sync_immediate()
michael@0 620 {
michael@0 621 let stmt = makeTestStatement(
michael@0 622 "INSERT INTO test (id) " +
michael@0 623 "VALUES (?)"
michael@0 624 );
michael@0 625
michael@0 626 let array = stmt.newBindingParamsArray();
michael@0 627 let bp = array.newBindingParams();
michael@0 628
michael@0 629 // Check variant binding.
michael@0 630 expectError(Cr.NS_ERROR_INVALID_ARG,
michael@0 631 function() bp.bindByIndex(1, INTEGER));
michael@0 632 // Check blob binding.
michael@0 633 expectError(Cr.NS_ERROR_INVALID_ARG,
michael@0 634 function() bp.bindBlobByIndex(1, BLOB, BLOB.length));
michael@0 635
michael@0 636 stmt.finalize();
michael@0 637 run_next_test();
michael@0 638 }
michael@0 639 test_bind_out_of_bounds_sync_immediate.syncOnly = true;
michael@0 640
michael@0 641 /**
michael@0 642 * Verify that a mozIStorageAsyncStatement reports an error asynchronously when
michael@0 643 * we bind to an illegal index.
michael@0 644 */
michael@0 645 function test_bind_out_of_bounds_async_deferred()
michael@0 646 {
michael@0 647 let stmt = makeTestStatement(
michael@0 648 "INSERT INTO test (id) " +
michael@0 649 "VALUES (?)"
michael@0 650 );
michael@0 651
michael@0 652 let array = stmt.newBindingParamsArray();
michael@0 653 let bp = array.newBindingParams();
michael@0 654
michael@0 655 // There is no difference between variant and blob binding for async purposes.
michael@0 656 bp.bindByIndex(1, INTEGER);
michael@0 657 array.addParams(bp);
michael@0 658 stmt.bindParameters(array);
michael@0 659 execAsync(stmt, {error: Ci.mozIStorageError.RANGE});
michael@0 660
michael@0 661 stmt.finalize();
michael@0 662 run_next_test();
michael@0 663 }
michael@0 664 test_bind_out_of_bounds_async_deferred.asyncOnly = true;
michael@0 665
michael@0 666 function test_bind_no_such_name_sync_immediate()
michael@0 667 {
michael@0 668 let stmt = makeTestStatement(
michael@0 669 "INSERT INTO test (id) " +
michael@0 670 "VALUES (:foo)"
michael@0 671 );
michael@0 672
michael@0 673 let array = stmt.newBindingParamsArray();
michael@0 674 let bp = array.newBindingParams();
michael@0 675
michael@0 676 // Check variant binding.
michael@0 677 expectError(Cr.NS_ERROR_INVALID_ARG,
michael@0 678 function() bp.bindByName("doesnotexist", INTEGER));
michael@0 679 // Check blob binding.
michael@0 680 expectError(Cr.NS_ERROR_INVALID_ARG,
michael@0 681 function() bp.bindBlobByName("doesnotexist", BLOB, BLOB.length));
michael@0 682
michael@0 683 stmt.finalize();
michael@0 684 run_next_test();
michael@0 685 }
michael@0 686 test_bind_no_such_name_sync_immediate.syncOnly = true;
michael@0 687
michael@0 688 function test_bind_no_such_name_async_deferred()
michael@0 689 {
michael@0 690 let stmt = makeTestStatement(
michael@0 691 "INSERT INTO test (id) " +
michael@0 692 "VALUES (:foo)"
michael@0 693 );
michael@0 694
michael@0 695 let array = stmt.newBindingParamsArray();
michael@0 696 let bp = array.newBindingParams();
michael@0 697
michael@0 698 bp.bindByName("doesnotexist", INTEGER);
michael@0 699 array.addParams(bp);
michael@0 700 stmt.bindParameters(array);
michael@0 701 execAsync(stmt, {error: Ci.mozIStorageError.RANGE});
michael@0 702
michael@0 703 stmt.finalize();
michael@0 704 run_next_test();
michael@0 705 }
michael@0 706 test_bind_no_such_name_async_deferred.asyncOnly = true;
michael@0 707
michael@0 708 function test_bind_bogus_type_by_index()
michael@0 709 {
michael@0 710 // We try to bind a JS Object here that should fail to bind.
michael@0 711 let stmt = makeTestStatement(
michael@0 712 "INSERT INTO test (blober) " +
michael@0 713 "VALUES (?)"
michael@0 714 );
michael@0 715
michael@0 716 let array = stmt.newBindingParamsArray();
michael@0 717 let bp = array.newBindingParams();
michael@0 718 // We get an error after calling executeAsync, not when we bind.
michael@0 719 bp.bindByIndex(0, run_test);
michael@0 720 array.addParams(bp);
michael@0 721 stmt.bindParameters(array);
michael@0 722
michael@0 723 execAsync(stmt, {error: Ci.mozIStorageError.MISMATCH});
michael@0 724
michael@0 725 stmt.finalize();
michael@0 726 run_next_test();
michael@0 727 }
michael@0 728
michael@0 729 function test_bind_bogus_type_by_name()
michael@0 730 {
michael@0 731 // We try to bind a JS Object here that should fail to bind.
michael@0 732 let stmt = makeTestStatement(
michael@0 733 "INSERT INTO test (blober) " +
michael@0 734 "VALUES (:blob)"
michael@0 735 );
michael@0 736
michael@0 737 let array = stmt.newBindingParamsArray();
michael@0 738 let bp = array.newBindingParams();
michael@0 739 // We get an error after calling executeAsync, not when we bind.
michael@0 740 bp.bindByName("blob", run_test);
michael@0 741 array.addParams(bp);
michael@0 742 stmt.bindParameters(array);
michael@0 743
michael@0 744 execAsync(stmt, {error: Ci.mozIStorageError.MISMATCH});
michael@0 745
michael@0 746 stmt.finalize();
michael@0 747 run_next_test();
michael@0 748 }
michael@0 749
michael@0 750 function test_bind_params_already_locked()
michael@0 751 {
michael@0 752 let stmt = makeTestStatement(
michael@0 753 "INSERT INTO test (id) " +
michael@0 754 "VALUES (:int)"
michael@0 755 );
michael@0 756
michael@0 757 let array = stmt.newBindingParamsArray();
michael@0 758 let bp = array.newBindingParams();
michael@0 759 bp.bindByName("int", INTEGER);
michael@0 760 array.addParams(bp);
michael@0 761
michael@0 762 // We should get an error after we call addParams and try to bind again.
michael@0 763 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 764 function() bp.bindByName("int", INTEGER));
michael@0 765
michael@0 766 stmt.finalize();
michael@0 767 run_next_test();
michael@0 768 }
michael@0 769
michael@0 770 function test_bind_params_array_already_locked()
michael@0 771 {
michael@0 772 let stmt = makeTestStatement(
michael@0 773 "INSERT INTO test (id) " +
michael@0 774 "VALUES (:int)"
michael@0 775 );
michael@0 776
michael@0 777 let array = stmt.newBindingParamsArray();
michael@0 778 let bp1 = array.newBindingParams();
michael@0 779 bp1.bindByName("int", INTEGER);
michael@0 780 array.addParams(bp1);
michael@0 781 let bp2 = array.newBindingParams();
michael@0 782 stmt.bindParameters(array);
michael@0 783 bp2.bindByName("int", INTEGER);
michael@0 784
michael@0 785 // We should get an error after we have bound the array to the statement.
michael@0 786 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 787 function() array.addParams(bp2));
michael@0 788
michael@0 789 stmt.finalize();
michael@0 790 run_next_test();
michael@0 791 }
michael@0 792
michael@0 793 function test_no_binding_params_from_locked_array()
michael@0 794 {
michael@0 795 let stmt = makeTestStatement(
michael@0 796 "INSERT INTO test (id) " +
michael@0 797 "VALUES (:int)"
michael@0 798 );
michael@0 799
michael@0 800 let array = stmt.newBindingParamsArray();
michael@0 801 let bp = array.newBindingParams();
michael@0 802 bp.bindByName("int", INTEGER);
michael@0 803 array.addParams(bp);
michael@0 804 stmt.bindParameters(array);
michael@0 805
michael@0 806 // We should not be able to get a new BindingParams object after we have bound
michael@0 807 // to the statement.
michael@0 808 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 809 function() array.newBindingParams());
michael@0 810
michael@0 811 stmt.finalize();
michael@0 812 run_next_test();
michael@0 813 }
michael@0 814
michael@0 815 function test_not_right_owning_array()
michael@0 816 {
michael@0 817 let stmt = makeTestStatement(
michael@0 818 "INSERT INTO test (id) " +
michael@0 819 "VALUES (:int)"
michael@0 820 );
michael@0 821
michael@0 822 let array1 = stmt.newBindingParamsArray();
michael@0 823 let array2 = stmt.newBindingParamsArray();
michael@0 824 let bp = array1.newBindingParams();
michael@0 825 bp.bindByName("int", INTEGER);
michael@0 826
michael@0 827 // We should not be able to add bp to array2 since it was created from array1.
michael@0 828 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 829 function() array2.addParams(bp));
michael@0 830
michael@0 831 stmt.finalize();
michael@0 832 run_next_test();
michael@0 833 }
michael@0 834
michael@0 835 function test_not_right_owning_statement()
michael@0 836 {
michael@0 837 let stmt1 = makeTestStatement(
michael@0 838 "INSERT INTO test (id) " +
michael@0 839 "VALUES (:int)"
michael@0 840 );
michael@0 841 let stmt2 = makeTestStatement(
michael@0 842 "INSERT INTO test (id) " +
michael@0 843 "VALUES (:int)"
michael@0 844 );
michael@0 845
michael@0 846 let array1 = stmt1.newBindingParamsArray();
michael@0 847 let array2 = stmt2.newBindingParamsArray();
michael@0 848 let bp = array1.newBindingParams();
michael@0 849 bp.bindByName("int", INTEGER);
michael@0 850 array1.addParams(bp);
michael@0 851
michael@0 852 // We should not be able to bind array1 since it was created from stmt1.
michael@0 853 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 854 function() stmt2.bindParameters(array1));
michael@0 855
michael@0 856 stmt1.finalize();
michael@0 857 stmt2.finalize();
michael@0 858 run_next_test();
michael@0 859 }
michael@0 860
michael@0 861 function test_bind_empty_array()
michael@0 862 {
michael@0 863 let stmt = makeTestStatement(
michael@0 864 "INSERT INTO test (id) " +
michael@0 865 "VALUES (:int)"
michael@0 866 );
michael@0 867
michael@0 868 let paramsArray = stmt.newBindingParamsArray();
michael@0 869
michael@0 870 // We should not be able to bind this array to the statement because it is
michael@0 871 // empty.
michael@0 872 expectError(Cr.NS_ERROR_UNEXPECTED,
michael@0 873 function() stmt.bindParameters(paramsArray));
michael@0 874
michael@0 875 stmt.finalize();
michael@0 876 run_next_test();
michael@0 877 }
michael@0 878
michael@0 879 function test_multiple_results()
michael@0 880 {
michael@0 881 let expectedResults = getTableRowCount("test");
michael@0 882 // Sanity check - we should have more than one result, but let's be sure.
michael@0 883 do_check_true(expectedResults > 1);
michael@0 884
michael@0 885 // Now check that we get back two rows of data from our async query.
michael@0 886 let stmt = makeTestStatement("SELECT * FROM test");
michael@0 887 execAsync(stmt, {}, expectedResults);
michael@0 888
michael@0 889 stmt.finalize();
michael@0 890 run_next_test();
michael@0 891 }
michael@0 892
michael@0 893 ////////////////////////////////////////////////////////////////////////////////
michael@0 894 //// Test Runner
michael@0 895
michael@0 896
michael@0 897 const TEST_PASS_SYNC = 0;
michael@0 898 const TEST_PASS_ASYNC = 1;
michael@0 899 /**
michael@0 900 * We run 2 passes against the test. One where makeTestStatement generates
michael@0 901 * synchronous (mozIStorageStatement) statements and one where it generates
michael@0 902 * asynchronous (mozIStorageAsyncStatement) statements.
michael@0 903 *
michael@0 904 * Because of differences in the ability to know the number of parameters before
michael@0 905 * dispatching, some tests are sync/async specific. These functions are marked
michael@0 906 * with 'syncOnly' or 'asyncOnly' attributes and run_next_test knows what to do.
michael@0 907 */
michael@0 908 let testPass = TEST_PASS_SYNC;
michael@0 909
michael@0 910 /**
michael@0 911 * Create a statement of the type under test per testPass.
michael@0 912 *
michael@0 913 * @param aSQL
michael@0 914 * The SQL string from which to build a statement.
michael@0 915 * @return a statement of the type under test per testPass.
michael@0 916 */
michael@0 917 function makeTestStatement(aSQL) {
michael@0 918 if (testPass == TEST_PASS_SYNC)
michael@0 919 return getOpenedDatabase().createStatement(aSQL);
michael@0 920 else
michael@0 921 return getOpenedDatabase().createAsyncStatement(aSQL);
michael@0 922 }
michael@0 923
michael@0 924 var tests =
michael@0 925 [
michael@0 926 test_illegal_sql_async_deferred,
michael@0 927 test_create_table,
michael@0 928 test_add_data,
michael@0 929 test_get_data,
michael@0 930 test_tuple_out_of_bounds,
michael@0 931 test_no_listener_works_on_success,
michael@0 932 test_no_listener_works_on_results,
michael@0 933 test_no_listener_works_on_error,
michael@0 934 test_partial_listener_works,
michael@0 935 test_immediate_cancellation,
michael@0 936 test_double_cancellation,
michael@0 937 test_cancellation_after_execution,
michael@0 938 test_double_execute,
michael@0 939 test_finalized_statement_does_not_crash,
michael@0 940 test_bind_direct_binding_params_by_index,
michael@0 941 test_bind_direct_binding_params_by_name,
michael@0 942 test_bind_js_params_helper_by_index,
michael@0 943 test_bind_js_params_helper_by_name,
michael@0 944 test_bind_multiple_rows_by_index,
michael@0 945 test_bind_multiple_rows_by_name,
michael@0 946 test_bind_out_of_bounds_sync_immediate,
michael@0 947 test_bind_out_of_bounds_async_deferred,
michael@0 948 test_bind_no_such_name_sync_immediate,
michael@0 949 test_bind_no_such_name_async_deferred,
michael@0 950 test_bind_bogus_type_by_index,
michael@0 951 test_bind_bogus_type_by_name,
michael@0 952 test_bind_params_already_locked,
michael@0 953 test_bind_params_array_already_locked,
michael@0 954 test_bind_empty_array,
michael@0 955 test_no_binding_params_from_locked_array,
michael@0 956 test_not_right_owning_array,
michael@0 957 test_not_right_owning_statement,
michael@0 958 test_multiple_results,
michael@0 959 ];
michael@0 960 let index = 0;
michael@0 961
michael@0 962 const STARTING_UNIQUE_ID = 2;
michael@0 963 let nextUniqueId = STARTING_UNIQUE_ID;
michael@0 964
michael@0 965 function run_next_test()
michael@0 966 {
michael@0 967 function _run_next_test() {
michael@0 968 // use a loop so we can skip tests...
michael@0 969 while (index < tests.length) {
michael@0 970 let test = tests[index++];
michael@0 971 // skip tests not appropriate to the current test pass
michael@0 972 if ((testPass == TEST_PASS_SYNC && ("asyncOnly" in test)) ||
michael@0 973 (testPass == TEST_PASS_ASYNC && ("syncOnly" in test)))
michael@0 974 continue;
michael@0 975
michael@0 976 // Asynchronous tests means that exceptions don't kill the test.
michael@0 977 try {
michael@0 978 print("****** Running the next test: " + test.name);
michael@0 979 test();
michael@0 980 return;
michael@0 981 }
michael@0 982 catch (e) {
michael@0 983 do_throw(e);
michael@0 984 }
michael@0 985 }
michael@0 986
michael@0 987 // if we only completed the first pass, move to the next pass
michael@0 988 if (testPass == TEST_PASS_SYNC) {
michael@0 989 print("********* Beginning mozIStorageAsyncStatement pass.");
michael@0 990 testPass++;
michael@0 991 index = 0;
michael@0 992 // a new pass demands a new database
michael@0 993 asyncCleanup();
michael@0 994 nextUniqueId = STARTING_UNIQUE_ID;
michael@0 995 _run_next_test();
michael@0 996 return;
michael@0 997 }
michael@0 998
michael@0 999 // we did some async stuff; we need to clean up.
michael@0 1000 asyncCleanup();
michael@0 1001 do_test_finished();
michael@0 1002 }
michael@0 1003
michael@0 1004 // Don't actually schedule another test if we're quitting.
michael@0 1005 if (!_quit) {
michael@0 1006 // For saner stacks, we execute this code RSN.
michael@0 1007 do_execute_soon(_run_next_test);
michael@0 1008 }
michael@0 1009 }
michael@0 1010
michael@0 1011 function run_test()
michael@0 1012 {
michael@0 1013 cleanup();
michael@0 1014
michael@0 1015 do_test_pending();
michael@0 1016 run_next_test();
michael@0 1017 }

mercurial