1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim set: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/. */
7 #include "storage_test_harness.h"
9 #include "mozilla/ReentrantMonitor.h"
10 #include "nsThreadUtils.h"
11 #include "mozIStorageStatement.h"
14 * This file tests that our implementation around sqlite3_unlock_notify works
18 ////////////////////////////////////////////////////////////////////////////////
28 class DatabaseLocker
: public nsRunnable
31 DatabaseLocker(const char* aSQL
)
32 : monitor("DatabaseLocker::monitor")
38 void RunInBackground()
40 (void)NS_NewThread(getter_AddRefs(mThread
));
41 do_check_true(mThread
);
43 do_check_success(mThread
->Dispatch(this, NS_DISPATCH_NORMAL
));
48 mozilla::ReentrantMonitorAutoEnter
lock(monitor
);
50 nsCOMPtr
<mozIStorageConnection
> db(getDatabase());
53 nsCOMPtr
<mozIStorageStatement
> stmt
;
54 do_check_success(db
->CreateStatement(sql
, getter_AddRefs(stmt
)));
57 do_check_success(stmt
->ExecuteStep(&hasResult
));
65 void WaitFor(State aState
)
67 monitor
.AssertCurrentThreadIn();
68 while (mState
!= aState
) {
69 do_check_success(monitor
.Wait());
73 void Notify(State aState
)
75 monitor
.AssertCurrentThreadIn();
77 do_check_success(monitor
.Notify());
80 mozilla::ReentrantMonitor monitor
;
83 nsCOMPtr
<nsIThread
> mThread
;
84 const char *const mSQL
;
88 class DatabaseTester
: public DatabaseLocker
91 DatabaseTester(mozIStorageConnection
*aConnection
,
93 : DatabaseLocker(aSQL
)
94 , mConnection(aConnection
)
100 mozilla::ReentrantMonitorAutoEnter
lock(monitor
);
104 nsCOMPtr
<mozIStorageStatement
> stmt
;
105 do_check_success(mConnection
->CreateStatement(sql
, getter_AddRefs(stmt
)));
108 nsresult rv
= stmt
->ExecuteStep(&hasResult
);
109 do_check_eq(rv
, NS_ERROR_FILE_IS_LOCKED
);
111 // Finalize our statement and null out our connection before notifying to
112 // ensure that we close on the proper thread.
113 rv
= stmt
->Finalize();
114 do_check_eq(rv
, NS_ERROR_FILE_IS_LOCKED
);
115 mConnection
= nullptr;
123 nsCOMPtr
<mozIStorageConnection
> mConnection
;
126 ////////////////////////////////////////////////////////////////////////////////
132 nsCOMPtr
<mozIStorageConnection
> db(getDatabase());
134 // Create and populate a dummy table.
135 nsresult rv
= db
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
136 "CREATE TABLE test (id INTEGER PRIMARY KEY, data STRING)"
138 do_check_success(rv
);
139 rv
= db
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
140 "INSERT INTO test (data) VALUES ('foo')"
142 do_check_success(rv
);
143 rv
= db
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
144 "INSERT INTO test (data) VALUES ('bar')"
146 do_check_success(rv
);
147 rv
= db
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
148 "CREATE UNIQUE INDEX unique_data ON test (data)"
150 do_check_success(rv
);
154 test_step_locked_does_not_block_main_thread()
156 nsCOMPtr
<mozIStorageConnection
> db(getDatabase());
158 // Need to prepare our statement ahead of time so we make sure to only test
159 // step and not prepare.
160 nsCOMPtr
<mozIStorageStatement
> stmt
;
161 nsresult rv
= db
->CreateStatement(NS_LITERAL_CSTRING(
162 "INSERT INTO test (data) VALUES ('test1')"
163 ), getter_AddRefs(stmt
));
164 do_check_success(rv
);
166 nsRefPtr
<DatabaseLocker
> locker(new DatabaseLocker("SELECT * FROM test"));
167 do_check_true(locker
);
168 mozilla::ReentrantMonitorAutoEnter
lock(locker
->monitor
);
169 locker
->RunInBackground();
171 // Wait for the locker to notify us that it has locked the database properly.
172 locker
->WaitFor(WRITE_LOCK
);
175 rv
= stmt
->ExecuteStep(&hasResult
);
176 do_check_eq(rv
, NS_ERROR_FILE_IS_LOCKED
);
178 locker
->Notify(TEST_DONE
);
182 test_drop_index_does_not_loop()
184 nsCOMPtr
<mozIStorageConnection
> db(getDatabase());
186 // Need to prepare our statement ahead of time so we make sure to only test
187 // step and not prepare.
188 nsCOMPtr
<mozIStorageStatement
> stmt
;
189 nsresult rv
= db
->CreateStatement(NS_LITERAL_CSTRING(
191 ), getter_AddRefs(stmt
));
192 do_check_success(rv
);
194 nsRefPtr
<DatabaseTester
> tester
=
195 new DatabaseTester(db
, "DROP INDEX unique_data");
196 do_check_true(tester
);
197 mozilla::ReentrantMonitorAutoEnter
lock(tester
->monitor
);
198 tester
->RunInBackground();
200 // Hold a read lock on the database, and then let the tester try to execute.
202 rv
= stmt
->ExecuteStep(&hasResult
);
203 do_check_success(rv
);
204 do_check_true(hasResult
);
205 tester
->Notify(READ_LOCK
);
207 // Make sure the tester finishes its test before we move on.
208 tester
->WaitFor(TEST_DONE
);
212 test_drop_table_does_not_loop()
214 nsCOMPtr
<mozIStorageConnection
> db(getDatabase());
216 // Need to prepare our statement ahead of time so we make sure to only test
217 // step and not prepare.
218 nsCOMPtr
<mozIStorageStatement
> stmt
;
219 nsresult rv
= db
->CreateStatement(NS_LITERAL_CSTRING(
221 ), getter_AddRefs(stmt
));
222 do_check_success(rv
);
224 nsRefPtr
<DatabaseTester
> tester(new DatabaseTester(db
, "DROP TABLE test"));
225 do_check_true(tester
);
226 mozilla::ReentrantMonitorAutoEnter
lock(tester
->monitor
);
227 tester
->RunInBackground();
229 // Hold a read lock on the database, and then let the tester try to execute.
231 rv
= stmt
->ExecuteStep(&hasResult
);
232 do_check_success(rv
);
233 do_check_true(hasResult
);
234 tester
->Notify(READ_LOCK
);
236 // Make sure the tester finishes its test before we move on.
237 tester
->WaitFor(TEST_DONE
);
240 void (*gTests
[])(void) = {
242 test_step_locked_does_not_block_main_thread
,
243 test_drop_index_does_not_loop
,
244 test_drop_table_does_not_loop
,
247 const char *file
= __FILE__
;
248 #define TEST_NAME "sqlite3_unlock_notify"
249 #define TEST_FILE file
250 #include "storage_test_harness_tail.h"