1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
3
4 #include "storage_test_harness.h"
5 #include "prthread.h"
6 #include "nsIEventTarget.h"
7 #include "nsIInterfaceRequestorUtils.h"
8 #include "mozilla/Attributes.h"
9
10 #include "sqlite3.h"
11
12 ////////////////////////////////////////////////////////////////////////////////
13 //// Async Helpers
14
15 /**
16 * Spins the events loop for current thread until aCondition is true.
17 */
18 void
spin_events_loop_until_true(const bool * const aCondition)19 spin_events_loop_until_true(const bool* const aCondition)
20 {
21 nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
22 nsresult rv = NS_OK;
23 bool processed = true;
24 while (!(*aCondition) && NS_SUCCEEDED(rv)) {
25 rv = thread->ProcessNextEvent(true, &processed);
26 }
27 }
28
29 ////////////////////////////////////////////////////////////////////////////////
30 //// mozIStorageStatementCallback implementation
31
32 class UnownedCallback final : public mozIStorageStatementCallback
33 {
34 public:
35 NS_DECL_ISUPPORTS
36
37 // Whether the object has been destroyed.
38 static bool sAlive;
39 // Whether the first result was received.
40 static bool sResult;
41 // Whether an error was received.
42 static bool sError;
43
UnownedCallback(mozIStorageConnection * aDBConn)44 explicit UnownedCallback(mozIStorageConnection* aDBConn)
45 : mDBConn(aDBConn)
46 , mCompleted(false)
47 {
48 sAlive = true;
49 sResult = false;
50 sError = false;
51 }
52
53 private:
~UnownedCallback()54 ~UnownedCallback()
55 {
56 sAlive = false;
57 blocking_async_close(mDBConn);
58 }
59
60 public:
HandleResult(mozIStorageResultSet * aResultSet)61 NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) override
62 {
63 sResult = true;
64 spin_events_loop_until_true(&mCompleted);
65 if (!sAlive) {
66 MOZ_CRASH("The statement callback was destroyed prematurely.");
67 }
68 return NS_OK;
69 }
70
HandleError(mozIStorageError * aError)71 NS_IMETHOD HandleError(mozIStorageError* aError) override
72 {
73 sError = true;
74 spin_events_loop_until_true(&mCompleted);
75 if (!sAlive) {
76 MOZ_CRASH("The statement callback was destroyed prematurely.");
77 }
78 return NS_OK;
79 }
80
HandleCompletion(uint16_t aReason)81 NS_IMETHOD HandleCompletion(uint16_t aReason) override
82 {
83 mCompleted = true;
84 return NS_OK;
85 }
86
87 protected:
88 nsCOMPtr<mozIStorageConnection> mDBConn;
89 bool mCompleted;
90 };
91
92 NS_IMPL_ISUPPORTS(UnownedCallback, mozIStorageStatementCallback)
93
94 bool UnownedCallback::sAlive = false;
95 bool UnownedCallback::sResult = false;
96 bool UnownedCallback::sError = false;
97
98 ////////////////////////////////////////////////////////////////////////////////
99 //// Tests
100
TEST(storage_async_callbacks_with_spun_event_loops,SpinEventsLoopInHandleResult)101 TEST(storage_async_callbacks_with_spun_event_loops, SpinEventsLoopInHandleResult)
102 {
103 nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
104
105 // Create a test table and populate it.
106 nsCOMPtr<mozIStorageStatement> stmt;
107 db->CreateStatement(NS_LITERAL_CSTRING(
108 "CREATE TABLE test (id INTEGER PRIMARY KEY)"
109 ), getter_AddRefs(stmt));
110 stmt->Execute();
111 stmt->Finalize();
112
113 db->CreateStatement(NS_LITERAL_CSTRING(
114 "INSERT INTO test (id) VALUES (?)"
115 ), getter_AddRefs(stmt));
116 for (int32_t i = 0; i < 30; ++i) {
117 stmt->BindInt32ByIndex(0, i);
118 stmt->Execute();
119 stmt->Reset();
120 }
121 stmt->Finalize();
122
123 db->CreateStatement(NS_LITERAL_CSTRING(
124 "SELECT * FROM test"
125 ), getter_AddRefs(stmt));
126 nsCOMPtr<mozIStoragePendingStatement> ps;
127 do_check_success(stmt->ExecuteAsync(new UnownedCallback(db),
128 getter_AddRefs(ps)));
129 stmt->Finalize();
130
131 spin_events_loop_until_true(&UnownedCallback::sResult);
132 }
133
TEST(storage_async_callbacks_with_spun_event_loops,SpinEventsLoopInHandleError)134 TEST(storage_async_callbacks_with_spun_event_loops, SpinEventsLoopInHandleError)
135 {
136 nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
137
138 // Create a test table and populate it.
139 nsCOMPtr<mozIStorageStatement> stmt;
140 db->CreateStatement(NS_LITERAL_CSTRING(
141 "CREATE TABLE test (id INTEGER PRIMARY KEY)"
142 ), getter_AddRefs(stmt));
143 stmt->Execute();
144 stmt->Finalize();
145
146 db->CreateStatement(NS_LITERAL_CSTRING(
147 "INSERT INTO test (id) VALUES (1)"
148 ), getter_AddRefs(stmt));
149 stmt->Execute();
150 stmt->Finalize();
151
152 // This will cause a constraint error.
153 db->CreateStatement(NS_LITERAL_CSTRING(
154 "INSERT INTO test (id) VALUES (1)"
155 ), getter_AddRefs(stmt));
156 nsCOMPtr<mozIStoragePendingStatement> ps;
157 do_check_success(stmt->ExecuteAsync(new UnownedCallback(db),
158 getter_AddRefs(ps)));
159 stmt->Finalize();
160
161 spin_events_loop_until_true(&UnownedCallback::sError);
162 }
163