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