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