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