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.

     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();
  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);
  1011 function run_test()
  1013   cleanup();
  1015   do_test_pending();
  1016   run_next_test();

mercurial