1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/extensions/cookie/test/unit/test_cookies_async_failure.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,600 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +// Test the various ways opening a cookie database can fail in an asynchronous 1.8 +// (i.e. after synchronous initialization) manner, and that the database is 1.9 +// renamed and recreated under each circumstance. These circumstances are, in no 1.10 +// particular order: 1.11 +// 1.12 +// 1) A write operation failing after the database has been read in. 1.13 +// 2) Asynchronous read failure due to a corrupt database. 1.14 +// 3) Synchronous read failure due to a corrupt database, when reading: 1.15 +// a) a single base domain; 1.16 +// b) the entire database. 1.17 +// 4) Asynchronous read failure, followed by another failure during INSERT but 1.18 +// before the database closes for rebuilding. (The additional error should be 1.19 +// ignored.) 1.20 +// 5) Asynchronous read failure, followed by an INSERT failure during rebuild. 1.21 +// This should result in an abort of the database rebuild; the partially- 1.22 +// built database should be moved to 'cookies.sqlite.bak-rebuild'. 1.23 + 1.24 +let test_generator = do_run_test(); 1.25 + 1.26 +function run_test() { 1.27 + do_test_pending(); 1.28 + do_run_generator(test_generator); 1.29 +} 1.30 + 1.31 +function finish_test() { 1.32 + do_execute_soon(function() { 1.33 + test_generator.close(); 1.34 + do_test_finished(); 1.35 + }); 1.36 +} 1.37 + 1.38 +function do_run_test() { 1.39 + // Set up a profile. 1.40 + this.profile = do_get_profile(); 1.41 + 1.42 + // Allow all cookies. 1.43 + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); 1.44 + 1.45 + // Get the cookie file and the backup file. 1.46 + do_check_false(do_get_cookie_file(profile).exists()); 1.47 + do_check_false(do_get_backup_file(profile).exists()); 1.48 + 1.49 + // Create a cookie object for testing. 1.50 + this.now = Date.now() * 1000; 1.51 + this.futureExpiry = Math.round(this.now / 1e6 + 1000); 1.52 + this.cookie = new Cookie("oh", "hai", "bar.com", "/", this.futureExpiry, 1.53 + this.now, this.now, false, false, false); 1.54 + 1.55 + this.sub_generator = run_test_1(test_generator); 1.56 + sub_generator.next(); 1.57 + yield; 1.58 + 1.59 + this.sub_generator = run_test_2(test_generator); 1.60 + sub_generator.next(); 1.61 + yield; 1.62 + 1.63 + this.sub_generator = run_test_3(test_generator); 1.64 + sub_generator.next(); 1.65 + yield; 1.66 + 1.67 + this.sub_generator = run_test_4(test_generator); 1.68 + sub_generator.next(); 1.69 + yield; 1.70 + 1.71 + this.sub_generator = run_test_5(test_generator); 1.72 + sub_generator.next(); 1.73 + yield; 1.74 + 1.75 + finish_test(); 1.76 + return; 1.77 +} 1.78 + 1.79 +function do_get_backup_file(profile) 1.80 +{ 1.81 + let file = profile.clone(); 1.82 + file.append("cookies.sqlite.bak"); 1.83 + return file; 1.84 +} 1.85 + 1.86 +function do_get_rebuild_backup_file(profile) 1.87 +{ 1.88 + let file = profile.clone(); 1.89 + file.append("cookies.sqlite.bak-rebuild"); 1.90 + return file; 1.91 +} 1.92 + 1.93 +function do_corrupt_db(file) 1.94 +{ 1.95 + // Sanity check: the database size should be larger than 450k, since we've 1.96 + // written about 460k of data. If it's not, let's make it obvious now. 1.97 + let size = file.fileSize; 1.98 + do_check_true(size > 450e3); 1.99 + 1.100 + // Corrupt the database by writing bad data to the end of the file. We 1.101 + // assume that the important metadata -- table structure etc -- is stored 1.102 + // elsewhere, and that doing this will not cause synchronous failure when 1.103 + // initializing the database connection. This is totally empirical -- 1.104 + // overwriting between 1k and 100k of live data seems to work. (Note that the 1.105 + // database file will be larger than the actual content requires, since the 1.106 + // cookie service uses a large growth increment. So we calculate the offset 1.107 + // based on the expected size of the content, not just the file size.) 1.108 + let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. 1.109 + createInstance(Ci.nsIFileOutputStream); 1.110 + ostream.init(file, 2, -1, 0); 1.111 + let sstream = ostream.QueryInterface(Ci.nsISeekableStream); 1.112 + let n = size - 450e3 + 20e3; 1.113 + sstream.seek(Ci.nsISeekableStream.NS_SEEK_SET, size - n); 1.114 + for (let i = 0; i < n; ++i) { 1.115 + ostream.write("a", 1); 1.116 + } 1.117 + ostream.flush(); 1.118 + ostream.close(); 1.119 + 1.120 + do_check_eq(file.clone().fileSize, size); 1.121 + return size; 1.122 +} 1.123 + 1.124 +function run_test_1(generator) 1.125 +{ 1.126 + // Load the profile and populate it. 1.127 + let uri = NetUtil.newURI("http://foo.com/"); 1.128 + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); 1.129 + 1.130 + // Close the profile. 1.131 + do_close_profile(sub_generator); 1.132 + yield; 1.133 + 1.134 + // Open a database connection now, before we load the profile and begin 1.135 + // asynchronous write operations. In order to tell when the async delete 1.136 + // statement has completed, we do something tricky: open a schema 2 connection 1.137 + // and add a cookie with null baseDomain. We can then wait until we see it 1.138 + // deleted in the new database. 1.139 + let db2 = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); 1.140 + db2.db.executeSimpleSQL("INSERT INTO moz_cookies (baseDomain) VALUES (NULL)"); 1.141 + db2.close(); 1.142 + let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4); 1.143 + do_check_eq(do_count_cookies_in_db(db.db), 2); 1.144 + 1.145 + // Load the profile, and wait for async read completion... 1.146 + do_load_profile(sub_generator); 1.147 + yield; 1.148 + 1.149 + // ... and the DELETE statement to finish. 1.150 + while (do_count_cookies_in_db(db.db) == 2) { 1.151 + do_execute_soon(function() { 1.152 + do_run_generator(sub_generator); 1.153 + }); 1.154 + yield; 1.155 + } 1.156 + do_check_eq(do_count_cookies_in_db(db.db), 1); 1.157 + 1.158 + // Insert a row. 1.159 + db.insertCookie(cookie); 1.160 + db.close(); 1.161 + 1.162 + // Attempt to insert a cookie with the same (name, host, path) triplet. 1.163 + Services.cookiemgr.add(cookie.host, cookie.path, cookie.name, "hallo", 1.164 + cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry); 1.165 + 1.166 + // Check that the cookie service accepted the new cookie. 1.167 + do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1); 1.168 + 1.169 + // Wait for the cookie service to rename the old database and rebuild. 1.170 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.171 + yield; 1.172 + do_execute_soon(function() { do_run_generator(sub_generator); }); 1.173 + yield; 1.174 + 1.175 + // At this point, the cookies should still be in memory. 1.176 + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1); 1.177 + do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1); 1.178 + do_check_eq(do_count_cookies(), 2); 1.179 + 1.180 + // Close the profile. 1.181 + do_close_profile(sub_generator); 1.182 + yield; 1.183 + 1.184 + // Check that the original database was renamed, and that it contains the 1.185 + // original cookie. 1.186 + do_check_true(do_get_backup_file(profile).exists()); 1.187 + let backupdb = Services.storage.openDatabase(do_get_backup_file(profile)); 1.188 + do_check_eq(do_count_cookies_in_db(backupdb, "foo.com"), 1); 1.189 + backupdb.close(); 1.190 + 1.191 + // Load the profile, and check that it contains the new cookie. 1.192 + do_load_profile(); 1.193 + 1.194 + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1); 1.195 + let enumerator = Services.cookiemgr.getCookiesFromHost(cookie.host); 1.196 + do_check_true(enumerator.hasMoreElements()); 1.197 + let dbcookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); 1.198 + do_check_eq(dbcookie.value, "hallo"); 1.199 + do_check_false(enumerator.hasMoreElements()); 1.200 + 1.201 + // Close the profile. 1.202 + do_close_profile(sub_generator); 1.203 + yield; 1.204 + 1.205 + // Clean up. 1.206 + do_get_cookie_file(profile).remove(false); 1.207 + do_get_backup_file(profile).remove(false); 1.208 + do_check_false(do_get_cookie_file(profile).exists()); 1.209 + do_check_false(do_get_backup_file(profile).exists()); 1.210 + do_run_generator(generator); 1.211 +} 1.212 + 1.213 +function run_test_2(generator) 1.214 +{ 1.215 + // Load the profile and populate it. 1.216 + do_load_profile(); 1.217 + for (let i = 0; i < 3000; ++i) { 1.218 + let uri = NetUtil.newURI("http://" + i + ".com/"); 1.219 + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); 1.220 + } 1.221 + 1.222 + // Close the profile. 1.223 + do_close_profile(sub_generator); 1.224 + yield; 1.225 + 1.226 + // Corrupt the database file. 1.227 + let size = do_corrupt_db(do_get_cookie_file(profile)); 1.228 + 1.229 + // Load the profile. 1.230 + do_load_profile(); 1.231 + 1.232 + // At this point, the database connection should be open. Ensure that it 1.233 + // succeeded. 1.234 + do_check_false(do_get_backup_file(profile).exists()); 1.235 + 1.236 + // Synchronously read in the first cookie. This will cause it to go into the 1.237 + // cookie table, whereupon it will be written out during database rebuild. 1.238 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.239 + 1.240 + // Wait for the asynchronous read to choke, at which point the backup file 1.241 + // will be created and the database rebuilt. 1.242 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.243 + yield; 1.244 + do_execute_soon(function() { do_run_generator(sub_generator); }); 1.245 + yield; 1.246 + 1.247 + // At this point, the cookies should still be in memory. 1.248 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.249 + do_check_eq(do_count_cookies(), 1); 1.250 + 1.251 + // Close the profile. 1.252 + do_close_profile(sub_generator); 1.253 + yield; 1.254 + 1.255 + // Check that the original database was renamed. 1.256 + do_check_true(do_get_backup_file(profile).exists()); 1.257 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.258 + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); 1.259 + do_check_eq(do_count_cookies_in_db(db, "0.com"), 1); 1.260 + db.close(); 1.261 + 1.262 + // Load the profile, and check that it contains the new cookie. 1.263 + do_load_profile(); 1.264 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.265 + do_check_eq(do_count_cookies(), 1); 1.266 + 1.267 + // Close the profile. 1.268 + do_close_profile(sub_generator); 1.269 + yield; 1.270 + 1.271 + // Clean up. 1.272 + do_get_cookie_file(profile).remove(false); 1.273 + do_get_backup_file(profile).remove(false); 1.274 + do_check_false(do_get_cookie_file(profile).exists()); 1.275 + do_check_false(do_get_backup_file(profile).exists()); 1.276 + do_run_generator(generator); 1.277 +} 1.278 + 1.279 +function run_test_3(generator) 1.280 +{ 1.281 + // Set the maximum cookies per base domain limit to a large value, so that 1.282 + // corrupting the database is easier. 1.283 + Services.prefs.setIntPref("network.cookie.maxPerHost", 3000); 1.284 + 1.285 + // Load the profile and populate it. 1.286 + do_load_profile(); 1.287 + for (let i = 0; i < 10; ++i) { 1.288 + let uri = NetUtil.newURI("http://hither.com/"); 1.289 + Services.cookies.setCookieString(uri, null, "oh" + i + "=hai; max-age=1000", 1.290 + null); 1.291 + } 1.292 + for (let i = 10; i < 3000; ++i) { 1.293 + let uri = NetUtil.newURI("http://haithur.com/"); 1.294 + Services.cookies.setCookieString(uri, null, "oh" + i + "=hai; max-age=1000", 1.295 + null); 1.296 + } 1.297 + 1.298 + // Close the profile. 1.299 + do_close_profile(sub_generator); 1.300 + yield; 1.301 + 1.302 + // Corrupt the database file. 1.303 + let size = do_corrupt_db(do_get_cookie_file(profile)); 1.304 + 1.305 + // Load the profile. 1.306 + do_load_profile(); 1.307 + 1.308 + // At this point, the database connection should be open. Ensure that it 1.309 + // succeeded. 1.310 + do_check_false(do_get_backup_file(profile).exists()); 1.311 + 1.312 + // Synchronously read in the cookies for our two domains. The first should 1.313 + // succeed, but the second should fail midway through, resulting in none of 1.314 + // those cookies being present. 1.315 + do_check_eq(Services.cookiemgr.countCookiesFromHost("hither.com"), 10); 1.316 + do_check_eq(Services.cookiemgr.countCookiesFromHost("haithur.com"), 0); 1.317 + 1.318 + // Wait for the backup file to be created and the database rebuilt. 1.319 + do_check_false(do_get_backup_file(profile).exists()); 1.320 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.321 + yield; 1.322 + do_execute_soon(function() { do_run_generator(sub_generator); }); 1.323 + yield; 1.324 + 1.325 + // Close the profile. 1.326 + do_close_profile(sub_generator); 1.327 + yield; 1.328 + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); 1.329 + do_check_eq(do_count_cookies_in_db(db, "hither.com"), 10); 1.330 + do_check_eq(do_count_cookies_in_db(db), 10); 1.331 + db.close(); 1.332 + 1.333 + // Check that the original database was renamed. 1.334 + do_check_true(do_get_backup_file(profile).exists()); 1.335 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.336 + 1.337 + // Rename it back, and try loading the entire database synchronously. 1.338 + do_get_backup_file(profile).moveTo(null, "cookies.sqlite"); 1.339 + do_load_profile(); 1.340 + 1.341 + // At this point, the database connection should be open. Ensure that it 1.342 + // succeeded. 1.343 + do_check_false(do_get_backup_file(profile).exists()); 1.344 + 1.345 + // Synchronously read in everything. 1.346 + do_check_eq(do_count_cookies(), 0); 1.347 + 1.348 + // Wait for the backup file to be created and the database rebuilt. 1.349 + do_check_false(do_get_backup_file(profile).exists()); 1.350 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.351 + yield; 1.352 + do_execute_soon(function() { do_run_generator(sub_generator); }); 1.353 + yield; 1.354 + 1.355 + // Close the profile. 1.356 + do_close_profile(sub_generator); 1.357 + yield; 1.358 + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); 1.359 + do_check_eq(do_count_cookies_in_db(db), 0); 1.360 + db.close(); 1.361 + 1.362 + // Check that the original database was renamed. 1.363 + do_check_true(do_get_backup_file(profile).exists()); 1.364 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.365 + 1.366 + // Clean up. 1.367 + do_get_cookie_file(profile).remove(false); 1.368 + do_get_backup_file(profile).remove(false); 1.369 + do_check_false(do_get_cookie_file(profile).exists()); 1.370 + do_check_false(do_get_backup_file(profile).exists()); 1.371 + do_run_generator(generator); 1.372 +} 1.373 + 1.374 +function run_test_4(generator) 1.375 +{ 1.376 + // Load the profile and populate it. 1.377 + do_load_profile(); 1.378 + for (let i = 0; i < 3000; ++i) { 1.379 + let uri = NetUtil.newURI("http://" + i + ".com/"); 1.380 + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); 1.381 + } 1.382 + 1.383 + // Close the profile. 1.384 + do_close_profile(sub_generator); 1.385 + yield; 1.386 + 1.387 + // Corrupt the database file. 1.388 + let size = do_corrupt_db(do_get_cookie_file(profile)); 1.389 + 1.390 + // Load the profile. 1.391 + do_load_profile(); 1.392 + 1.393 + // At this point, the database connection should be open. Ensure that it 1.394 + // succeeded. 1.395 + do_check_false(do_get_backup_file(profile).exists()); 1.396 + 1.397 + // Synchronously read in the first cookie. This will cause it to go into the 1.398 + // cookie table, whereupon it will be written out during database rebuild. 1.399 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.400 + 1.401 + // Queue up an INSERT for the same base domain. This should also go into 1.402 + // memory and be written out during database rebuild. 1.403 + let uri = NetUtil.newURI("http://0.com/"); 1.404 + Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null); 1.405 + 1.406 + // Wait for the asynchronous read to choke and the insert to fail shortly 1.407 + // thereafter, at which point the backup file will be created and the database 1.408 + // rebuilt. 1.409 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.410 + yield; 1.411 + do_execute_soon(function() { do_run_generator(sub_generator); }); 1.412 + yield; 1.413 + 1.414 + // Close the profile. 1.415 + do_close_profile(sub_generator); 1.416 + yield; 1.417 + 1.418 + // Check that the original database was renamed. 1.419 + do_check_true(do_get_backup_file(profile).exists()); 1.420 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.421 + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); 1.422 + do_check_eq(do_count_cookies_in_db(db, "0.com"), 2); 1.423 + db.close(); 1.424 + 1.425 + // Load the profile, and check that it contains the new cookie. 1.426 + do_load_profile(); 1.427 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2); 1.428 + do_check_eq(do_count_cookies(), 2); 1.429 + 1.430 + // Close the profile. 1.431 + do_close_profile(sub_generator); 1.432 + yield; 1.433 + 1.434 + // Clean up. 1.435 + do_get_cookie_file(profile).remove(false); 1.436 + do_get_backup_file(profile).remove(false); 1.437 + do_check_false(do_get_cookie_file(profile).exists()); 1.438 + do_check_false(do_get_backup_file(profile).exists()); 1.439 + do_run_generator(generator); 1.440 +} 1.441 + 1.442 +function run_test_4(generator) 1.443 +{ 1.444 + // Load the profile and populate it. 1.445 + do_load_profile(); 1.446 + for (let i = 0; i < 3000; ++i) { 1.447 + let uri = NetUtil.newURI("http://" + i + ".com/"); 1.448 + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); 1.449 + } 1.450 + 1.451 + // Close the profile. 1.452 + do_close_profile(sub_generator); 1.453 + yield; 1.454 + 1.455 + // Corrupt the database file. 1.456 + let size = do_corrupt_db(do_get_cookie_file(profile)); 1.457 + 1.458 + // Load the profile. 1.459 + do_load_profile(); 1.460 + 1.461 + // At this point, the database connection should be open. Ensure that it 1.462 + // succeeded. 1.463 + do_check_false(do_get_backup_file(profile).exists()); 1.464 + 1.465 + // Synchronously read in the first cookie. This will cause it to go into the 1.466 + // cookie table, whereupon it will be written out during database rebuild. 1.467 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.468 + 1.469 + // Queue up an INSERT for the same base domain. This should also go into 1.470 + // memory and be written out during database rebuild. 1.471 + let uri = NetUtil.newURI("http://0.com/"); 1.472 + Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null); 1.473 + 1.474 + // Wait for the asynchronous read to choke and the insert to fail shortly 1.475 + // thereafter, at which point the backup file will be created and the database 1.476 + // rebuilt. 1.477 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.478 + yield; 1.479 + do_execute_soon(function() { do_run_generator(sub_generator); }); 1.480 + yield; 1.481 + 1.482 + // At this point, the cookies should still be in memory. 1.483 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2); 1.484 + do_check_eq(do_count_cookies(), 2); 1.485 + 1.486 + // Close the profile. 1.487 + do_close_profile(sub_generator); 1.488 + yield; 1.489 + 1.490 + // Check that the original database was renamed. 1.491 + do_check_true(do_get_backup_file(profile).exists()); 1.492 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.493 + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); 1.494 + do_check_eq(do_count_cookies_in_db(db, "0.com"), 2); 1.495 + db.close(); 1.496 + 1.497 + // Load the profile, and check that it contains the new cookie. 1.498 + do_load_profile(); 1.499 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2); 1.500 + do_check_eq(do_count_cookies(), 2); 1.501 + 1.502 + // Close the profile. 1.503 + do_close_profile(sub_generator); 1.504 + yield; 1.505 + 1.506 + // Clean up. 1.507 + do_get_cookie_file(profile).remove(false); 1.508 + do_get_backup_file(profile).remove(false); 1.509 + do_check_false(do_get_cookie_file(profile).exists()); 1.510 + do_check_false(do_get_backup_file(profile).exists()); 1.511 + do_run_generator(generator); 1.512 +} 1.513 + 1.514 +function run_test_5(generator) 1.515 +{ 1.516 + // Load the profile and populate it. 1.517 + do_load_profile(); 1.518 + let uri = NetUtil.newURI("http://bar.com/"); 1.519 + Services.cookies.setCookieString(uri, null, "oh=hai; path=/; max-age=1000", 1.520 + null); 1.521 + for (let i = 0; i < 3000; ++i) { 1.522 + let uri = NetUtil.newURI("http://" + i + ".com/"); 1.523 + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); 1.524 + } 1.525 + 1.526 + // Close the profile. 1.527 + do_close_profile(sub_generator); 1.528 + yield; 1.529 + 1.530 + // Corrupt the database file. 1.531 + let size = do_corrupt_db(do_get_cookie_file(profile)); 1.532 + 1.533 + // Load the profile. 1.534 + do_load_profile(); 1.535 + 1.536 + // At this point, the database connection should be open. Ensure that it 1.537 + // succeeded. 1.538 + do_check_false(do_get_backup_file(profile).exists()); 1.539 + 1.540 + // Synchronously read in the first two cookies. This will cause them to go 1.541 + // into the cookie table, whereupon it will be written out during database 1.542 + // rebuild. 1.543 + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1); 1.544 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.545 + 1.546 + // Wait for the asynchronous read to choke, at which point the backup file 1.547 + // will be created and a new connection opened. 1.548 + new _observer(sub_generator, "cookie-db-rebuilding"); 1.549 + yield; 1.550 + 1.551 + // At this point, the cookies should still be in memory. (Note that these 1.552 + // calls are re-entrant into the cookie service, but it's OK!) 1.553 + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1); 1.554 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.555 + do_check_eq(do_count_cookies(), 2); 1.556 + do_check_true(do_get_backup_file(profile).exists()); 1.557 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.558 + do_check_false(do_get_rebuild_backup_file(profile).exists()); 1.559 + 1.560 + // Open a database connection, and write a row that will trigger a constraint 1.561 + // violation. 1.562 + let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4); 1.563 + db.insertCookie(cookie); 1.564 + do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1); 1.565 + do_check_eq(do_count_cookies_in_db(db.db), 1); 1.566 + db.close(); 1.567 + 1.568 + // Wait for the rebuild to bail and the database to be closed. 1.569 + new _observer(sub_generator, "cookie-db-closed"); 1.570 + yield; 1.571 + 1.572 + // Check that the original backup and the database itself are gone. 1.573 + do_check_true(do_get_rebuild_backup_file(profile).exists()); 1.574 + do_check_true(do_get_backup_file(profile).exists()); 1.575 + do_check_eq(do_get_backup_file(profile).fileSize, size); 1.576 + do_check_false(do_get_cookie_file(profile).exists()); 1.577 + 1.578 + // Check that the rebuild backup has the original bar.com cookie, and possibly 1.579 + // a 0.com cookie depending on whether it got written out first or second. 1.580 + db = new CookieDatabaseConnection(do_get_rebuild_backup_file(profile), 4); 1.581 + do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1); 1.582 + let count = do_count_cookies_in_db(db.db); 1.583 + do_check_true(count == 1 || 1.584 + count == 2 && do_count_cookies_in_db(db.db, "0.com") == 1); 1.585 + db.close(); 1.586 + 1.587 + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1); 1.588 + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); 1.589 + do_check_eq(do_count_cookies(), 2); 1.590 + 1.591 + // Close the profile. We do not need to wait for completion, because the 1.592 + // database has already been closed. 1.593 + do_close_profile(); 1.594 + 1.595 + // Clean up. 1.596 + do_get_backup_file(profile).remove(false); 1.597 + do_get_rebuild_backup_file(profile).remove(false); 1.598 + do_check_false(do_get_cookie_file(profile).exists()); 1.599 + do_check_false(do_get_backup_file(profile).exists()); 1.600 + do_check_false(do_get_rebuild_backup_file(profile).exists()); 1.601 + do_run_generator(generator); 1.602 +} 1.603 +