|
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/. */ |
|
4 |
|
5 // This file tests the functions of mozIStorageConnection |
|
6 |
|
7 //////////////////////////////////////////////////////////////////////////////// |
|
8 //// Test Functions |
|
9 |
|
10 function asyncClone(db, readOnly) { |
|
11 let deferred = Promise.defer(); |
|
12 db.asyncClone(readOnly, function(status, db2) { |
|
13 if (Components.isSuccessCode(status)) { |
|
14 deferred.resolve(db2); |
|
15 } else { |
|
16 deferred.reject(status); |
|
17 } |
|
18 }); |
|
19 return deferred.promise; |
|
20 } |
|
21 |
|
22 function asyncClose(db) { |
|
23 let deferred = Promise.defer(); |
|
24 db.asyncClose(function(status) { |
|
25 if (Components.isSuccessCode(status)) { |
|
26 deferred.resolve(); |
|
27 } else { |
|
28 deferred.reject(status); |
|
29 } |
|
30 }); |
|
31 return deferred.promise; |
|
32 } |
|
33 |
|
34 function openAsyncDatabase(file, options) { |
|
35 let deferred = Promise.defer(); |
|
36 let properties; |
|
37 if (options) { |
|
38 properties = Cc["@mozilla.org/hash-property-bag;1"]. |
|
39 createInstance(Ci.nsIWritablePropertyBag); |
|
40 for (let k in options) { |
|
41 properties.setProperty(k, options[k]); |
|
42 } |
|
43 } |
|
44 getService().openAsyncDatabase(file, properties, function(status, db) { |
|
45 if (Components.isSuccessCode(status)) { |
|
46 deferred.resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection)); |
|
47 } else { |
|
48 deferred.reject(status); |
|
49 } |
|
50 }); |
|
51 return deferred.promise; |
|
52 } |
|
53 |
|
54 function executeAsync(statement, onResult) { |
|
55 let deferred = Promise.defer(); |
|
56 statement.executeAsync({ |
|
57 handleError: function(error) { |
|
58 deferred.reject(error); |
|
59 }, |
|
60 handleResult: function(result) { |
|
61 if (onResult) { |
|
62 onResult(result); |
|
63 } |
|
64 }, |
|
65 handleCompletion: function(result) { |
|
66 deferred.resolve(result); |
|
67 } |
|
68 }); |
|
69 return deferred.promise; |
|
70 } |
|
71 |
|
72 add_task(function test_connectionReady_open() |
|
73 { |
|
74 // there doesn't seem to be a way for the connection to not be ready (unless |
|
75 // we close it with mozIStorageConnection::Close(), but we don't for this). |
|
76 // It can only fail if GetPath fails on the database file, or if we run out |
|
77 // of memory trying to use an in-memory database |
|
78 |
|
79 var msc = getOpenedDatabase(); |
|
80 do_check_true(msc.connectionReady); |
|
81 }); |
|
82 |
|
83 add_task(function test_connectionReady_closed() |
|
84 { |
|
85 // This also tests mozIStorageConnection::Close() |
|
86 |
|
87 var msc = getOpenedDatabase(); |
|
88 msc.close(); |
|
89 do_check_false(msc.connectionReady); |
|
90 gDBConn = null; // this is so later tests don't start to fail. |
|
91 }); |
|
92 |
|
93 add_task(function test_databaseFile() |
|
94 { |
|
95 var msc = getOpenedDatabase(); |
|
96 do_check_true(getTestDB().equals(msc.databaseFile)); |
|
97 }); |
|
98 |
|
99 add_task(function test_tableExists_not_created() |
|
100 { |
|
101 var msc = getOpenedDatabase(); |
|
102 do_check_false(msc.tableExists("foo")); |
|
103 }); |
|
104 |
|
105 add_task(function test_indexExists_not_created() |
|
106 { |
|
107 var msc = getOpenedDatabase(); |
|
108 do_check_false(msc.indexExists("foo")); |
|
109 }); |
|
110 |
|
111 add_task(function test_temp_tableExists_and_indexExists() |
|
112 { |
|
113 var msc = getOpenedDatabase(); |
|
114 msc.executeSimpleSQL("CREATE TEMP TABLE test_temp(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)"); |
|
115 do_check_true(msc.tableExists("test_temp")); |
|
116 |
|
117 msc.executeSimpleSQL("CREATE INDEX test_temp_ind ON test_temp (name)"); |
|
118 do_check_true(msc.indexExists("test_temp_ind")); |
|
119 |
|
120 msc.executeSimpleSQL("DROP INDEX test_temp_ind"); |
|
121 msc.executeSimpleSQL("DROP TABLE test_temp"); |
|
122 }); |
|
123 |
|
124 add_task(function test_createTable_not_created() |
|
125 { |
|
126 var msc = getOpenedDatabase(); |
|
127 msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT"); |
|
128 do_check_true(msc.tableExists("test")); |
|
129 }); |
|
130 |
|
131 add_task(function test_indexExists_created() |
|
132 { |
|
133 var msc = getOpenedDatabase(); |
|
134 msc.executeSimpleSQL("CREATE INDEX name_ind ON test (name)"); |
|
135 do_check_true(msc.indexExists("name_ind")); |
|
136 }); |
|
137 |
|
138 add_task(function test_createTable_already_created() |
|
139 { |
|
140 var msc = getOpenedDatabase(); |
|
141 do_check_true(msc.tableExists("test")); |
|
142 try { |
|
143 msc.createTable("test", "id INTEGER PRIMARY KEY, name TEXT"); |
|
144 do_throw("We shouldn't get here!"); |
|
145 } catch (e) { |
|
146 do_check_eq(Cr.NS_ERROR_FAILURE, e.result); |
|
147 } |
|
148 }); |
|
149 |
|
150 add_task(function test_attach_createTable_tableExists_indexExists() |
|
151 { |
|
152 var msc = getOpenedDatabase(); |
|
153 var file = do_get_file("storage_attach.sqlite", true); |
|
154 var msc2 = getDatabase(file); |
|
155 msc.executeSimpleSQL("ATTACH DATABASE '" + file.path + "' AS sample"); |
|
156 |
|
157 do_check_false(msc.tableExists("sample.test")); |
|
158 msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT"); |
|
159 do_check_true(msc.tableExists("sample.test")); |
|
160 try { |
|
161 msc.createTable("sample.test", "id INTEGER PRIMARY KEY, name TEXT"); |
|
162 do_throw("We shouldn't get here!"); |
|
163 } catch (e if e.result == Components.results.NS_ERROR_FAILURE) { |
|
164 // we expect to fail because this table should exist already. |
|
165 } |
|
166 |
|
167 do_check_false(msc.indexExists("sample.test_ind")); |
|
168 msc.executeSimpleSQL("CREATE INDEX sample.test_ind ON test (name)"); |
|
169 do_check_true(msc.indexExists("sample.test_ind")); |
|
170 |
|
171 msc.executeSimpleSQL("DETACH DATABASE sample"); |
|
172 msc2.close(); |
|
173 try { file.remove(false); } catch(e) { } |
|
174 }); |
|
175 |
|
176 add_task(function test_lastInsertRowID() |
|
177 { |
|
178 var msc = getOpenedDatabase(); |
|
179 msc.executeSimpleSQL("INSERT INTO test (name) VALUES ('foo')"); |
|
180 do_check_eq(1, msc.lastInsertRowID); |
|
181 }); |
|
182 |
|
183 add_task(function test_transactionInProgress_no() |
|
184 { |
|
185 var msc = getOpenedDatabase(); |
|
186 do_check_false(msc.transactionInProgress); |
|
187 }); |
|
188 |
|
189 add_task(function test_transactionInProgress_yes() |
|
190 { |
|
191 var msc = getOpenedDatabase(); |
|
192 msc.beginTransaction(); |
|
193 do_check_true(msc.transactionInProgress); |
|
194 msc.commitTransaction(); |
|
195 do_check_false(msc.transactionInProgress); |
|
196 |
|
197 msc.beginTransaction(); |
|
198 do_check_true(msc.transactionInProgress); |
|
199 msc.rollbackTransaction(); |
|
200 do_check_false(msc.transactionInProgress); |
|
201 }); |
|
202 |
|
203 add_task(function test_commitTransaction_no_transaction() |
|
204 { |
|
205 var msc = getOpenedDatabase(); |
|
206 do_check_false(msc.transactionInProgress); |
|
207 try { |
|
208 msc.commitTransaction(); |
|
209 do_throw("We should not get here!"); |
|
210 } catch (e) { |
|
211 do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result); |
|
212 } |
|
213 }); |
|
214 |
|
215 add_task(function test_rollbackTransaction_no_transaction() |
|
216 { |
|
217 var msc = getOpenedDatabase(); |
|
218 do_check_false(msc.transactionInProgress); |
|
219 try { |
|
220 msc.rollbackTransaction(); |
|
221 do_throw("We should not get here!"); |
|
222 } catch (e) { |
|
223 do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result); |
|
224 } |
|
225 }); |
|
226 |
|
227 add_task(function test_get_schemaVersion_not_set() |
|
228 { |
|
229 do_check_eq(0, getOpenedDatabase().schemaVersion); |
|
230 }); |
|
231 |
|
232 add_task(function test_set_schemaVersion() |
|
233 { |
|
234 var msc = getOpenedDatabase(); |
|
235 const version = 1; |
|
236 msc.schemaVersion = version; |
|
237 do_check_eq(version, msc.schemaVersion); |
|
238 }); |
|
239 |
|
240 add_task(function test_set_schemaVersion_same() |
|
241 { |
|
242 var msc = getOpenedDatabase(); |
|
243 const version = 1; |
|
244 msc.schemaVersion = version; // should still work ok |
|
245 do_check_eq(version, msc.schemaVersion); |
|
246 }); |
|
247 |
|
248 add_task(function test_set_schemaVersion_negative() |
|
249 { |
|
250 var msc = getOpenedDatabase(); |
|
251 const version = -1; |
|
252 msc.schemaVersion = version; |
|
253 do_check_eq(version, msc.schemaVersion); |
|
254 }); |
|
255 |
|
256 add_task(function test_createTable(){ |
|
257 var temp = getTestDB().parent; |
|
258 temp.append("test_db_table"); |
|
259 try { |
|
260 var con = getService().openDatabase(temp); |
|
261 con.createTable("a",""); |
|
262 } catch (e) { |
|
263 if (temp.exists()) try { |
|
264 temp.remove(false); |
|
265 } catch (e2) {} |
|
266 do_check_true(e.result==Cr.NS_ERROR_NOT_INITIALIZED || |
|
267 e.result==Cr.NS_ERROR_FAILURE); |
|
268 } finally { |
|
269 if (con) { |
|
270 con.close(); |
|
271 } |
|
272 } |
|
273 }); |
|
274 |
|
275 add_task(function test_defaultSynchronousAtNormal() |
|
276 { |
|
277 var msc = getOpenedDatabase(); |
|
278 var stmt = createStatement("PRAGMA synchronous;"); |
|
279 try { |
|
280 stmt.executeStep(); |
|
281 do_check_eq(1, stmt.getInt32(0)); |
|
282 } |
|
283 finally { |
|
284 stmt.reset(); |
|
285 stmt.finalize(); |
|
286 } |
|
287 }); |
|
288 |
|
289 // must be ran before executeAsync tests |
|
290 add_task(function test_close_does_not_spin_event_loop() |
|
291 { |
|
292 // We want to make sure that the event loop on the calling thread does not |
|
293 // spin when close is called. |
|
294 let event = { |
|
295 ran: false, |
|
296 run: function() |
|
297 { |
|
298 this.ran = true; |
|
299 }, |
|
300 }; |
|
301 |
|
302 // Post the event before we call close, so it would run if the event loop was |
|
303 // spun during close. |
|
304 let thread = Cc["@mozilla.org/thread-manager;1"]. |
|
305 getService(Ci.nsIThreadManager). |
|
306 currentThread; |
|
307 thread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL); |
|
308 |
|
309 // Sanity check, then close the database. Afterwards, we should not have ran! |
|
310 do_check_false(event.ran); |
|
311 getOpenedDatabase().close(); |
|
312 do_check_false(event.ran); |
|
313 |
|
314 // Reset gDBConn so that later tests will get a new connection object. |
|
315 gDBConn = null; |
|
316 }); |
|
317 |
|
318 add_task(function test_asyncClose_succeeds_with_finalized_async_statement() |
|
319 { |
|
320 // XXX this test isn't perfect since we can't totally control when events will |
|
321 // run. If this paticular function fails randomly, it means we have a |
|
322 // real bug. |
|
323 |
|
324 // We want to make sure we create a cached async statement to make sure that |
|
325 // when we finalize our statement, we end up finalizing the async one too so |
|
326 // close will succeed. |
|
327 let stmt = createStatement("SELECT * FROM test"); |
|
328 stmt.executeAsync(); |
|
329 stmt.finalize(); |
|
330 |
|
331 yield asyncClose(getOpenedDatabase()); |
|
332 // Reset gDBConn so that later tests will get a new connection object. |
|
333 gDBConn = null; |
|
334 }); |
|
335 |
|
336 add_task(function test_close_then_release_statement() { |
|
337 // Testing the behavior in presence of a bad client that finalizes |
|
338 // statements after the database has been closed (typically by |
|
339 // letting the gc finalize the statement). |
|
340 let db = getOpenedDatabase(); |
|
341 let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement"); |
|
342 db.close(); |
|
343 stmt.finalize(); // Finalize too late - this should not crash |
|
344 |
|
345 // Reset gDBConn so that later tests will get a new connection object. |
|
346 gDBConn = null; |
|
347 }); |
|
348 |
|
349 add_task(function test_asyncClose_then_release_statement() { |
|
350 // Testing the behavior in presence of a bad client that finalizes |
|
351 // statements after the database has been async closed (typically by |
|
352 // letting the gc finalize the statement). |
|
353 let db = getOpenedDatabase(); |
|
354 let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement"); |
|
355 yield asyncClose(db); |
|
356 stmt.finalize(); // Finalize too late - this should not crash |
|
357 |
|
358 // Reset gDBConn so that later tests will get a new connection object. |
|
359 gDBConn = null; |
|
360 }); |
|
361 |
|
362 add_task(function test_close_fails_with_async_statement_ran() |
|
363 { |
|
364 let deferred = Promise.defer(); |
|
365 let stmt = createStatement("SELECT * FROM test"); |
|
366 stmt.executeAsync(); |
|
367 stmt.finalize(); |
|
368 |
|
369 let db = getOpenedDatabase(); |
|
370 try { |
|
371 db.close(); |
|
372 do_throw("should have thrown"); |
|
373 } |
|
374 catch (e) { |
|
375 do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED); |
|
376 } |
|
377 finally { |
|
378 // Clean up after ourselves. |
|
379 db.asyncClose(function() { |
|
380 // Reset gDBConn so that later tests will get a new connection object. |
|
381 gDBConn = null; |
|
382 deferred.resolve(); |
|
383 }); |
|
384 } |
|
385 yield deferred.promise; |
|
386 }); |
|
387 |
|
388 add_task(function test_clone_optional_param() |
|
389 { |
|
390 let db1 = getService().openUnsharedDatabase(getTestDB()); |
|
391 let db2 = db1.clone(); |
|
392 do_check_true(db2.connectionReady); |
|
393 |
|
394 // A write statement should not fail here. |
|
395 let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)"); |
|
396 stmt.params.name = "dwitte"; |
|
397 stmt.execute(); |
|
398 stmt.finalize(); |
|
399 |
|
400 // And a read statement should succeed. |
|
401 stmt = db2.createStatement("SELECT * FROM test"); |
|
402 do_check_true(stmt.executeStep()); |
|
403 stmt.finalize(); |
|
404 |
|
405 // Additionally check that it is a connection on the same database. |
|
406 do_check_true(db1.databaseFile.equals(db2.databaseFile)); |
|
407 |
|
408 db1.close(); |
|
409 db2.close(); |
|
410 }); |
|
411 |
|
412 function standardAsyncTest(promisedDB, name, shouldInit = false) { |
|
413 do_print("Performing standard async test " + name); |
|
414 |
|
415 let adb = yield promisedDB; |
|
416 do_check_true(adb instanceof Ci.mozIStorageAsyncConnection); |
|
417 do_check_false(adb instanceof Ci.mozIStorageConnection); |
|
418 |
|
419 if (shouldInit) { |
|
420 let stmt = adb.createAsyncStatement("CREATE TABLE test(name TEXT)"); |
|
421 yield executeAsync(stmt); |
|
422 stmt.finalize(); |
|
423 } |
|
424 |
|
425 // Generate a name to insert and fetch back |
|
426 name = "worker bee " + Math.random() + " (" + name + ")"; |
|
427 |
|
428 let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)"); |
|
429 stmt.params.name = name; |
|
430 let result = yield executeAsync(stmt); |
|
431 do_print("Request complete"); |
|
432 stmt.finalize(); |
|
433 do_check_true(Components.isSuccessCode(result)); |
|
434 do_print("Extracting data"); |
|
435 stmt = adb.createAsyncStatement("SELECT * FROM test"); |
|
436 let found = false; |
|
437 yield executeAsync(stmt, function(result) { |
|
438 do_print("Data has been extracted"); |
|
439 |
|
440 for (let row = result.getNextRow(); row != null; row = result.getNextRow()) { |
|
441 if (row.getResultByName("name") == name) { |
|
442 found = true; |
|
443 break; |
|
444 } |
|
445 } |
|
446 }); |
|
447 do_check_true(found); |
|
448 stmt.finalize(); |
|
449 yield asyncClose(adb); |
|
450 |
|
451 do_print("Standard async test " + name + " complete"); |
|
452 } |
|
453 |
|
454 add_task(function test_open_async() { |
|
455 yield standardAsyncTest(openAsyncDatabase(getTestDB(), null), "default"); |
|
456 yield standardAsyncTest(openAsyncDatabase(getTestDB()), "no optional arg"); |
|
457 yield standardAsyncTest(openAsyncDatabase(getTestDB(), |
|
458 {shared: false, growthIncrement: 54}), "non-default options"); |
|
459 yield standardAsyncTest(openAsyncDatabase("memory"), |
|
460 "in-memory database", true); |
|
461 yield standardAsyncTest(openAsyncDatabase("memory", |
|
462 {shared: false, growthIncrement: 54}), |
|
463 "in-memory database and options", true); |
|
464 |
|
465 do_print("Testing async opening with bogus options 1"); |
|
466 let raised = false; |
|
467 let adb = null; |
|
468 try { |
|
469 adb = yield openAsyncDatabase(getTestDB(), {shared: "forty-two"}); |
|
470 } catch (ex) { |
|
471 raised = true; |
|
472 } finally { |
|
473 if (adb) { |
|
474 yield asyncClose(adb); |
|
475 } |
|
476 } |
|
477 do_check_true(raised); |
|
478 |
|
479 do_print("Testing async opening with bogus options 2"); |
|
480 raised = false; |
|
481 adb = null; |
|
482 try { |
|
483 adb = yield openAsyncDatabase(getTestDB(), {growthIncrement: "forty-two"}); |
|
484 } catch (ex) { |
|
485 raised = true; |
|
486 } finally { |
|
487 if (adb) { |
|
488 yield asyncClose(adb); |
|
489 } |
|
490 } |
|
491 do_check_true(raised); |
|
492 }); |
|
493 |
|
494 |
|
495 add_task(function test_async_open_with_shared_cache() { |
|
496 do_print("Testing that opening with a shared cache doesn't break stuff"); |
|
497 let adb = yield openAsyncDatabase(getTestDB(), {shared: true}); |
|
498 |
|
499 let stmt = adb.createAsyncStatement("INSERT INTO test (name) VALUES (:name)"); |
|
500 stmt.params.name = "clockworker"; |
|
501 let result = yield executeAsync(stmt); |
|
502 do_print("Request complete"); |
|
503 stmt.finalize(); |
|
504 do_check_true(Components.isSuccessCode(result)); |
|
505 do_print("Extracting data"); |
|
506 stmt = adb.createAsyncStatement("SELECT * FROM test"); |
|
507 let found = false; |
|
508 yield executeAsync(stmt, function(result) { |
|
509 do_print("Data has been extracted"); |
|
510 |
|
511 for (let row = result.getNextRow(); row != null; row = result.getNextRow()) { |
|
512 if (row.getResultByName("name") == "clockworker") { |
|
513 found = true; |
|
514 break; |
|
515 } |
|
516 } |
|
517 }); |
|
518 do_check_true(found); |
|
519 stmt.finalize(); |
|
520 yield asyncClose(adb); |
|
521 }); |
|
522 |
|
523 add_task(function test_clone_trivial_async() |
|
524 { |
|
525 let db1 = getService().openDatabase(getTestDB()); |
|
526 do_print("Opened adb1"); |
|
527 do_check_true(db1 instanceof Ci.mozIStorageAsyncConnection); |
|
528 let adb2 = yield asyncClone(db1, true); |
|
529 do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection); |
|
530 do_print("Cloned to adb2"); |
|
531 db1.close(); |
|
532 do_print("Closed db1"); |
|
533 yield asyncClose(adb2); |
|
534 }); |
|
535 |
|
536 add_task(function test_clone_no_optional_param_async() |
|
537 { |
|
538 "use strict"; |
|
539 do_print("Testing async cloning"); |
|
540 let adb1 = yield openAsyncDatabase(getTestDB(), null); |
|
541 do_check_true(adb1 instanceof Ci.mozIStorageAsyncConnection); |
|
542 |
|
543 do_print("Cloning database"); |
|
544 do_check_true(Components.isSuccessCode(result)); |
|
545 |
|
546 let adb2 = yield asyncClone(adb1); |
|
547 do_print("Testing that the cloned db is a mozIStorageAsyncConnection " + |
|
548 "and not a mozIStorageConnection"); |
|
549 do_check_true(adb2 instanceof Ci.mozIStorageAsyncConnection); |
|
550 do_check_false(adb2 instanceof Ci.mozIStorageConnection); |
|
551 |
|
552 do_print("Inserting data into source db"); |
|
553 let stmt = adb1. |
|
554 createAsyncStatement("INSERT INTO test (name) VALUES (:name)"); |
|
555 |
|
556 stmt.params.name = "yoric"; |
|
557 let result = yield executeAsync(stmt); |
|
558 do_print("Request complete"); |
|
559 stmt.finalize(); |
|
560 do_check_true(Components.isSuccessCode(result)); |
|
561 do_print("Extracting data from clone db"); |
|
562 stmt = adb2.createAsyncStatement("SELECT * FROM test"); |
|
563 let found = false; |
|
564 yield executeAsync(stmt, function(result) { |
|
565 do_print("Data has been extracted"); |
|
566 |
|
567 for (let row = result.getNextRow(); row != null; row = result.getNextRow()) { |
|
568 if (row.getResultByName("name") == "yoric") { |
|
569 found = true; |
|
570 break; |
|
571 } |
|
572 } |
|
573 }); |
|
574 do_check_true(found); |
|
575 stmt.finalize(); |
|
576 do_print("Closing databases"); |
|
577 yield asyncClose(adb2); |
|
578 do_print("First db closed"); |
|
579 |
|
580 yield asyncClose(adb1); |
|
581 do_print("Second db closed"); |
|
582 }); |
|
583 |
|
584 add_task(function test_clone_readonly() |
|
585 { |
|
586 let db1 = getService().openUnsharedDatabase(getTestDB()); |
|
587 let db2 = db1.clone(true); |
|
588 do_check_true(db2.connectionReady); |
|
589 |
|
590 // A write statement should fail here. |
|
591 let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)"); |
|
592 stmt.params.name = "reed"; |
|
593 expectError(Cr.NS_ERROR_FILE_READ_ONLY, function() stmt.execute()); |
|
594 stmt.finalize(); |
|
595 |
|
596 // And a read statement should succeed. |
|
597 stmt = db2.createStatement("SELECT * FROM test"); |
|
598 do_check_true(stmt.executeStep()); |
|
599 stmt.finalize(); |
|
600 |
|
601 db1.close(); |
|
602 db2.close(); |
|
603 }); |
|
604 |
|
605 add_task(function test_clone_shared_readonly() |
|
606 { |
|
607 let db1 = getService().openDatabase(getTestDB()); |
|
608 let db2 = db1.clone(true); |
|
609 do_check_true(db2.connectionReady); |
|
610 |
|
611 let stmt = db2.createStatement("INSERT INTO test (name) VALUES (:name)"); |
|
612 stmt.params.name = "parker"; |
|
613 // TODO currently SQLite does not actually work correctly here. The behavior |
|
614 // we want is commented out, and the current behavior is being tested |
|
615 // for. Our IDL comments will have to be updated when this starts to |
|
616 // work again. |
|
617 stmt.execute(); |
|
618 // expectError(Components.results.NS_ERROR_FILE_READ_ONLY, function() stmt.execute()); |
|
619 stmt.finalize(); |
|
620 |
|
621 // And a read statement should succeed. |
|
622 stmt = db2.createStatement("SELECT * FROM test"); |
|
623 do_check_true(stmt.executeStep()); |
|
624 stmt.finalize(); |
|
625 |
|
626 db1.close(); |
|
627 db2.close(); |
|
628 }); |
|
629 |
|
630 add_task(function test_close_clone_fails() |
|
631 { |
|
632 let calls = [ |
|
633 "openDatabase", |
|
634 "openUnsharedDatabase", |
|
635 ]; |
|
636 calls.forEach(function(methodName) { |
|
637 let db = getService()[methodName](getTestDB()); |
|
638 db.close(); |
|
639 expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone()); |
|
640 }); |
|
641 }); |
|
642 |
|
643 add_task(function test_memory_clone_fails() |
|
644 { |
|
645 let db = getService().openSpecialDatabase("memory"); |
|
646 db.close(); |
|
647 expectError(Cr.NS_ERROR_NOT_INITIALIZED, function() db.clone()); |
|
648 }); |
|
649 |
|
650 add_task(function test_clone_copies_functions() |
|
651 { |
|
652 const FUNC_NAME = "test_func"; |
|
653 let calls = [ |
|
654 "openDatabase", |
|
655 "openUnsharedDatabase", |
|
656 ]; |
|
657 let functionMethods = [ |
|
658 "createFunction", |
|
659 "createAggregateFunction", |
|
660 ]; |
|
661 calls.forEach(function(methodName) { |
|
662 [true, false].forEach(function(readOnly) { |
|
663 functionMethods.forEach(function(functionMethod) { |
|
664 let db1 = getService()[methodName](getTestDB()); |
|
665 // Create a function for db1. |
|
666 db1[functionMethod](FUNC_NAME, 1, { |
|
667 onFunctionCall: function() 0, |
|
668 onStep: function() 0, |
|
669 onFinal: function() 0, |
|
670 }); |
|
671 |
|
672 // Clone it, and make sure the function exists still. |
|
673 let db2 = db1.clone(readOnly); |
|
674 // Note: this would fail if the function did not exist. |
|
675 let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test"); |
|
676 stmt.finalize(); |
|
677 db1.close(); |
|
678 db2.close(); |
|
679 }); |
|
680 }); |
|
681 }); |
|
682 }); |
|
683 |
|
684 add_task(function test_clone_copies_overridden_functions() |
|
685 { |
|
686 const FUNC_NAME = "lower"; |
|
687 function test_func() { |
|
688 this.called = false; |
|
689 } |
|
690 test_func.prototype = { |
|
691 onFunctionCall: function() { |
|
692 this.called = true; |
|
693 }, |
|
694 onStep: function() { |
|
695 this.called = true; |
|
696 }, |
|
697 onFinal: function() 0, |
|
698 }; |
|
699 |
|
700 let calls = [ |
|
701 "openDatabase", |
|
702 "openUnsharedDatabase", |
|
703 ]; |
|
704 let functionMethods = [ |
|
705 "createFunction", |
|
706 "createAggregateFunction", |
|
707 ]; |
|
708 calls.forEach(function(methodName) { |
|
709 [true, false].forEach(function(readOnly) { |
|
710 functionMethods.forEach(function(functionMethod) { |
|
711 let db1 = getService()[methodName](getTestDB()); |
|
712 // Create a function for db1. |
|
713 let func = new test_func(); |
|
714 db1[functionMethod](FUNC_NAME, 1, func); |
|
715 do_check_false(func.called); |
|
716 |
|
717 // Clone it, and make sure the function gets called. |
|
718 let db2 = db1.clone(readOnly); |
|
719 let stmt = db2.createStatement("SELECT " + FUNC_NAME + "(id) FROM test"); |
|
720 stmt.executeStep(); |
|
721 do_check_true(func.called); |
|
722 stmt.finalize(); |
|
723 db1.close(); |
|
724 db2.close(); |
|
725 }); |
|
726 }); |
|
727 }); |
|
728 }); |
|
729 |
|
730 add_task(function test_clone_copies_pragmas() |
|
731 { |
|
732 const PRAGMAS = [ |
|
733 { name: "cache_size", value: 500, copied: true }, |
|
734 { name: "temp_store", value: 2, copied: true }, |
|
735 { name: "foreign_keys", value: 1, copied: true }, |
|
736 { name: "journal_size_limit", value: 524288, copied: true }, |
|
737 { name: "synchronous", value: 2, copied: true }, |
|
738 { name: "wal_autocheckpoint", value: 16, copied: true }, |
|
739 { name: "ignore_check_constraints", value: 1, copied: false }, |
|
740 ]; |
|
741 |
|
742 let db1 = getService().openUnsharedDatabase(getTestDB()); |
|
743 |
|
744 // Sanity check initial values are different from enforced ones. |
|
745 PRAGMAS.forEach(function (pragma) { |
|
746 let stmt = db1.createStatement("PRAGMA " + pragma.name); |
|
747 do_check_true(stmt.executeStep()); |
|
748 do_check_neq(pragma.value, stmt.getInt32(0)); |
|
749 stmt.finalize(); |
|
750 }); |
|
751 // Execute pragmas. |
|
752 PRAGMAS.forEach(function (pragma) { |
|
753 db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value); |
|
754 }); |
|
755 |
|
756 let db2 = db1.clone(); |
|
757 do_check_true(db2.connectionReady); |
|
758 |
|
759 // Check cloned connection inherited pragma values. |
|
760 PRAGMAS.forEach(function (pragma) { |
|
761 let stmt = db2.createStatement("PRAGMA " + pragma.name); |
|
762 do_check_true(stmt.executeStep()); |
|
763 let validate = pragma.copied ? do_check_eq : do_check_neq; |
|
764 validate(pragma.value, stmt.getInt32(0)); |
|
765 stmt.finalize(); |
|
766 }); |
|
767 |
|
768 db1.close(); |
|
769 db2.close(); |
|
770 }); |
|
771 |
|
772 add_task(function test_readonly_clone_copies_pragmas() |
|
773 { |
|
774 const PRAGMAS = [ |
|
775 { name: "cache_size", value: 500, copied: true }, |
|
776 { name: "temp_store", value: 2, copied: true }, |
|
777 { name: "foreign_keys", value: 1, copied: false }, |
|
778 { name: "journal_size_limit", value: 524288, copied: false }, |
|
779 { name: "synchronous", value: 2, copied: false }, |
|
780 { name: "wal_autocheckpoint", value: 16, copied: false }, |
|
781 { name: "ignore_check_constraints", value: 1, copied: false }, |
|
782 ]; |
|
783 |
|
784 let db1 = getService().openUnsharedDatabase(getTestDB()); |
|
785 |
|
786 // Sanity check initial values are different from enforced ones. |
|
787 PRAGMAS.forEach(function (pragma) { |
|
788 let stmt = db1.createStatement("PRAGMA " + pragma.name); |
|
789 do_check_true(stmt.executeStep()); |
|
790 do_check_neq(pragma.value, stmt.getInt32(0)); |
|
791 stmt.finalize(); |
|
792 }); |
|
793 // Execute pragmas. |
|
794 PRAGMAS.forEach(function (pragma) { |
|
795 db1.executeSimpleSQL("PRAGMA " + pragma.name + " = " + pragma.value); |
|
796 }); |
|
797 |
|
798 let db2 = db1.clone(true); |
|
799 do_check_true(db2.connectionReady); |
|
800 |
|
801 // Check cloned connection inherited pragma values. |
|
802 PRAGMAS.forEach(function (pragma) { |
|
803 let stmt = db2.createStatement("PRAGMA " + pragma.name); |
|
804 do_check_true(stmt.executeStep()); |
|
805 let validate = pragma.copied ? do_check_eq : do_check_neq; |
|
806 validate(pragma.value, stmt.getInt32(0)); |
|
807 stmt.finalize(); |
|
808 }); |
|
809 |
|
810 db1.close(); |
|
811 db2.close(); |
|
812 }); |
|
813 |
|
814 add_task(function test_getInterface() |
|
815 { |
|
816 let db = getOpenedDatabase(); |
|
817 let target = db.QueryInterface(Ci.nsIInterfaceRequestor) |
|
818 .getInterface(Ci.nsIEventTarget); |
|
819 // Just check that target is non-null. Other tests will ensure that it has |
|
820 // the correct value. |
|
821 do_check_true(target != null); |
|
822 |
|
823 yield asyncClose(db); |
|
824 gDBConn = null; |
|
825 }); |
|
826 |
|
827 |
|
828 function run_test() |
|
829 { |
|
830 run_next_test(); |
|
831 } |