test_connection_asyncClose.js (5311B)
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 /* 6 * Thorough branch coverage for asyncClose. 7 * 8 * Coverage of asyncClose by connection state at time of AsyncClose invocation: 9 * - (asyncThread && mDBConn) => AsyncCloseConnection used, actually closes 10 * - test_asyncClose_does_not_complete_before_statements 11 * - test_double_asyncClose_throws 12 * - test_asyncClose_does_not_throw_without_callback 13 * - (asyncThread && !mDBConn) => AsyncCloseConnection used, although no close 14 * is required. Note that this is only possible in the event that 15 * openAsyncDatabase was used and we failed to open the database. 16 * Additionally, the async connection will never be exposed to the caller and 17 * AsyncInitDatabase will be the one to (automatically) call AsyncClose. 18 * - test_asyncClose_failed_open 19 * - (!asyncThread && mDBConn) => Close() invoked, actually closes 20 * - test_asyncClose_on_sync_db 21 * - (!asyncThread && !mDBConn) => Close() invoked, no close needed, errors. 22 * This happens if the database has already been closed. 23 * - test_double_asyncClose_throws 24 */ 25 26 /** 27 * Sanity check that our close indeed happens after asynchronously executed 28 * statements scheduled during the same turn of the event loop. Note that we 29 * just care that the statement says it completed without error, we're not 30 * worried that the close will happen and then the statement will magically 31 * complete. 32 */ 33 add_task(async function test_asyncClose_does_not_complete_before_statements() { 34 let db = Services.storage.openDatabase(getTestDB()); 35 let stmt = db.createStatement("SELECT * FROM sqlite_master"); 36 // Issue the executeAsync but don't yield for it... 37 let asyncStatementPromise = executeAsync(stmt); 38 stmt.finalize(); 39 40 // Issue the close. (And now the order of yielding doesn't matter.) 41 // Branch coverage: (asyncThread && mDBConn) 42 await asyncClose(db); 43 equal( 44 await asyncStatementPromise, 45 Ci.mozIStorageStatementCallback.REASON_FINISHED 46 ); 47 }); 48 49 /** 50 * Open an async database (ensures the async thread is created) and then invoke 51 * AsyncClose() twice without yielding control flow. The first will initiate 52 * the actual async close after calling setClosedState which synchronously 53 * impacts what the second call will observe. The second call will then see the 54 * async thread is not available and fall back to invoking Close() which will 55 * notice the mDBConn is already gone. 56 */ 57 if (!AppConstants.DEBUG) { 58 add_task(async function test_double_asyncClose_throws() { 59 let db = await openAsyncDatabase(getTestDB()); 60 61 // (Don't yield control flow yet, save the promise for after we make the 62 // second call.) 63 // Branch coverage: (asyncThread && mDBConn) 64 let realClosePromise = await asyncClose(db); 65 try { 66 // Branch coverage: (!asyncThread && !mDBConn) 67 db.asyncClose(); 68 ok(false, "should have thrown"); 69 } catch (e) { 70 equal(e.result, Cr.NS_ERROR_NOT_INITIALIZED); 71 } 72 73 await realClosePromise; 74 }); 75 } 76 77 /** 78 * Create a sync db connection and never take it asynchronous and then call 79 * asyncClose on it. This will bring the async thread to life to perform the 80 * shutdown to avoid blocking the main thread, although we won't be able to 81 * tell the difference between this happening and the method secretly shunting 82 * to close(). 83 */ 84 add_task(async function test_asyncClose_on_sync_db() { 85 let db = Services.storage.openDatabase(getTestDB()); 86 87 // Branch coverage: (!asyncThread && mDBConn) 88 await asyncClose(db); 89 ok(true, "closed sync connection asynchronously"); 90 }); 91 92 /** 93 * Fail to asynchronously open a DB in order to get an async thread existing 94 * without having an open database and asyncClose invoked. As per the file 95 * doc-block, note that asyncClose will automatically be invoked by the 96 * AsyncInitDatabase when it fails to open the database. We will never be 97 * provided with a reference to the connection and so cannot call AsyncClose on 98 * it ourselves. 99 */ 100 add_task(async function test_asyncClose_failed_open() { 101 // This will fail and the promise will be rejected. 102 let openPromise = openAsyncDatabase(getFakeDB()); 103 await openPromise.then( 104 () => { 105 ok(false, "we should have failed to open the db; this test is broken!"); 106 }, 107 () => { 108 ok(true, "correctly failed to open db; bg asyncClose should happen"); 109 } 110 ); 111 // (NB: we are unable to observe the thread shutdown, but since we never open 112 // a database, this test is not going to interfere with other tests so much.) 113 }); 114 115 // THE TEST BELOW WANTS TO BE THE LAST TEST WE RUN. DO NOT MAKE IT SAD. 116 /** 117 * Verify that asyncClose without a callback does not explode. Without a 118 * callback the shutdown is not actually observable, so we run this test last 119 * in order to avoid weird overlaps. 120 */ 121 add_task(async function test_asyncClose_does_not_throw_without_callback() { 122 let db = await openAsyncDatabase(getTestDB()); 123 // Branch coverage: (asyncThread && mDBConn) 124 db.asyncClose(); 125 ok(true, "if we shutdown cleanly and do not crash, then we succeeded"); 126 }); 127 // OBEY SHOUTING UPPER-CASE COMMENTS. 128 // ADD TESTS ABOVE THE FORMER TEST, NOT BELOW IT.