1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "storage_test_harness.h"
8 
9 ////////////////////////////////////////////////////////////////////////////////
10 //// Tests
11 
TEST(storage_true_async,TrueAsyncStatement)12 TEST(storage_true_async, TrueAsyncStatement)
13 {
14   HookSqliteMutex hook;
15 
16   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
17 
18   // Start watching for forbidden mutex usage.
19   watch_for_mutex_use_on_this_thread();
20 
21   // - statement with nothing to bind
22   nsCOMPtr<mozIStorageAsyncStatement> stmt;
23   db->CreateAsyncStatement(
24     NS_LITERAL_CSTRING("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
25     getter_AddRefs(stmt)
26   );
27   blocking_async_execute(stmt);
28   stmt->Finalize();
29   do_check_false(mutex_used_on_watched_thread);
30 
31   // - statement with something to bind ordinally
32   db->CreateAsyncStatement(
33     NS_LITERAL_CSTRING("INSERT INTO test (id) VALUES (?)"),
34     getter_AddRefs(stmt)
35   );
36   stmt->BindInt32ByIndex(0, 1);
37   blocking_async_execute(stmt);
38   stmt->Finalize();
39   do_check_false(mutex_used_on_watched_thread);
40 
41   // - statement with something to bind by name
42   db->CreateAsyncStatement(
43     NS_LITERAL_CSTRING("INSERT INTO test (id) VALUES (:id)"),
44     getter_AddRefs(stmt)
45   );
46   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
47   stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
48   nsCOMPtr<mozIStorageBindingParams> params;
49   paramsArray->NewBindingParams(getter_AddRefs(params));
50   params->BindInt32ByName(NS_LITERAL_CSTRING("id"), 2);
51   paramsArray->AddParams(params);
52   params = nullptr;
53   stmt->BindParameters(paramsArray);
54   paramsArray = nullptr;
55   blocking_async_execute(stmt);
56   stmt->Finalize();
57   do_check_false(mutex_used_on_watched_thread);
58 
59   // - now, make sure creating a sync statement does trigger our guard.
60   // (If this doesn't happen, our test is bunk and it's important to know that.)
61   nsCOMPtr<mozIStorageStatement> syncStmt;
62   db->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM test"),
63                       getter_AddRefs(syncStmt));
64   syncStmt->Finalize();
65   do_check_true(mutex_used_on_watched_thread);
66 
67   blocking_async_close(db);
68 }
69 
70 /**
71  * Test that cancellation before a statement is run successfully stops the
72  * statement from executing.
73  */
TEST(storage_true_async,AsyncCancellation)74 TEST(storage_true_async, AsyncCancellation)
75 {
76   HookSqliteMutex hook;
77 
78   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
79 
80   // -- wedge the thread
81   nsCOMPtr<nsIThread> target(get_conn_async_thread(db));
82   do_check_true(target);
83   RefPtr<ThreadWedger> wedger (new ThreadWedger(target));
84 
85   // -- create statements and cancel them
86   // - async
87   nsCOMPtr<mozIStorageAsyncStatement> asyncStmt;
88   db->CreateAsyncStatement(
89     NS_LITERAL_CSTRING("CREATE TABLE asyncTable (id INTEGER PRIMARY KEY)"),
90     getter_AddRefs(asyncStmt)
91   );
92 
93   RefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
94   nsCOMPtr<mozIStoragePendingStatement> asyncPend;
95   (void)asyncStmt->ExecuteAsync(asyncSpin, getter_AddRefs(asyncPend));
96   do_check_true(asyncPend);
97   asyncPend->Cancel();
98 
99   // - sync
100   nsCOMPtr<mozIStorageStatement> syncStmt;
101   db->CreateStatement(
102     NS_LITERAL_CSTRING("CREATE TABLE syncTable (id INTEGER PRIMARY KEY)"),
103     getter_AddRefs(syncStmt)
104   );
105 
106   RefPtr<AsyncStatementSpinner> syncSpin(new AsyncStatementSpinner());
107   nsCOMPtr<mozIStoragePendingStatement> syncPend;
108   (void)syncStmt->ExecuteAsync(syncSpin, getter_AddRefs(syncPend));
109   do_check_true(syncPend);
110   syncPend->Cancel();
111 
112   // -- unwedge the async thread
113   wedger->unwedge();
114 
115   // -- verify that both statements report they were canceled
116   asyncSpin->SpinUntilCompleted();
117   do_check_true(asyncSpin->completionReason ==
118                 mozIStorageStatementCallback::REASON_CANCELED);
119 
120   syncSpin->SpinUntilCompleted();
121   do_check_true(syncSpin->completionReason ==
122                 mozIStorageStatementCallback::REASON_CANCELED);
123 
124   // -- verify that neither statement constructed their tables
125   nsresult rv;
126   bool exists;
127   rv = db->TableExists(NS_LITERAL_CSTRING("asyncTable"), &exists);
128   do_check_true(rv == NS_OK);
129   do_check_false(exists);
130   rv = db->TableExists(NS_LITERAL_CSTRING("syncTable"), &exists);
131   do_check_true(rv == NS_OK);
132   do_check_false(exists);
133 
134   // -- cleanup
135   asyncStmt->Finalize();
136   syncStmt->Finalize();
137   blocking_async_close(db);
138 }
139 
140 /**
141  * Test that the destructor for an asynchronous statement which has a
142  *  sqlite3_stmt will dispatch that statement to the async thread for
143  *  finalization rather than trying to finalize it on the main thread
144  *  (and thereby running afoul of our mutex use detector).
145  */
TEST(storage_true_async,AsyncDestructorFinalizesOnAsyncThread)146 TEST(storage_true_async, AsyncDestructorFinalizesOnAsyncThread)
147 {
148   HookSqliteMutex hook;
149 
150   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
151   watch_for_mutex_use_on_this_thread();
152 
153   // -- create an async statement
154   nsCOMPtr<mozIStorageAsyncStatement> stmt;
155   db->CreateAsyncStatement(
156     NS_LITERAL_CSTRING("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
157     getter_AddRefs(stmt)
158   );
159 
160   // -- execute it so it gets a sqlite3_stmt that needs to be finalized
161   blocking_async_execute(stmt);
162   do_check_false(mutex_used_on_watched_thread);
163 
164   // -- forget our reference
165   stmt = nullptr;
166 
167   // -- verify the mutex was not touched
168   do_check_false(mutex_used_on_watched_thread);
169 
170   // -- make sure the statement actually gets finalized / cleanup
171   // the close will assert if we failed to finalize!
172   blocking_async_close(db);
173 }
174 
175