test_cookies_sync_failure.js (8790B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 // Test the various ways opening a cookie database can fail in a synchronous 5 // (i.e. immediate) manner, and that the database is renamed and recreated 6 // under each circumstance. These circumstances are, in no particular order: 7 // 8 // 1) A corrupt database, such that opening the connection fails. 9 // 2) The 'moz_cookies' table doesn't exist. 10 // 3) Not all of the expected columns exist, and statement creation fails when: 11 // a) The schema version is larger than the current version. 12 // b) The schema version is less than or equal to the current version. 13 // 4) Migration fails. This will have different modes depending on the initial 14 // version: 15 // a) Schema 1: the 'lastAccessed' column already exists. 16 // b) Schema 2: the 'baseDomain' column already exists; or 'baseDomain' 17 // cannot be computed for a particular host. 18 // c) Schema 3: the 'creationTime' column already exists; or the 19 // 'moz_uniqueid' index already exists. 20 21 "use strict"; 22 23 let profile; 24 let cookieFile; 25 let backupFile; 26 let sub_generator; 27 let now; 28 let futureExpiry; 29 let cookie; 30 31 var COOKIE_DATABASE_SCHEMA_CURRENT = 17; 32 33 var test_generator = do_run_test(); 34 35 function run_test() { 36 do_test_pending(); 37 do_run_generator(test_generator); 38 } 39 40 function finish_test() { 41 executeSoon(function () { 42 test_generator.return(); 43 do_test_finished(); 44 }); 45 } 46 47 function* do_run_test() { 48 // Set up a profile. 49 profile = do_get_profile(); 50 51 // Allow all cookies. 52 Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); 53 Services.prefs.setBoolPref( 54 "network.cookieJarSettings.unblocked_for_testing", 55 true 56 ); 57 58 // Get the cookie file and the backup file. 59 cookieFile = profile.clone(); 60 cookieFile.append("cookies.sqlite"); 61 backupFile = profile.clone(); 62 backupFile.append("cookies.sqlite.bak"); 63 Assert.ok(!cookieFile.exists()); 64 Assert.ok(!backupFile.exists()); 65 66 // Create a cookie object for testing. 67 now = Date.now() * 1000; 68 futureExpiry = Math.round(now / 1e6 + 1000); 69 cookie = new Cookie( 70 "oh", 71 "hai", 72 "bar.com", 73 "/", 74 futureExpiry, 75 now, 76 now, 77 false, 78 false, 79 false 80 ); 81 82 sub_generator = run_test_1(test_generator); 83 sub_generator.next(); 84 yield; 85 86 sub_generator = run_test_2(test_generator); 87 sub_generator.next(); 88 yield; 89 90 sub_generator = run_test_3(test_generator, 99); 91 sub_generator.next(); 92 yield; 93 94 sub_generator = run_test_3(test_generator, COOKIE_DATABASE_SCHEMA_CURRENT); 95 sub_generator.next(); 96 yield; 97 98 sub_generator = run_test_3(test_generator, 4); 99 sub_generator.next(); 100 yield; 101 102 sub_generator = run_test_3(test_generator, 3); 103 sub_generator.next(); 104 yield; 105 106 sub_generator = run_test_4_exists( 107 test_generator, 108 1, 109 "ALTER TABLE moz_cookies ADD lastAccessed INTEGER" 110 ); 111 sub_generator.next(); 112 yield; 113 114 sub_generator = run_test_4_exists( 115 test_generator, 116 2, 117 "ALTER TABLE moz_cookies ADD baseDomain TEXT" 118 ); 119 sub_generator.next(); 120 yield; 121 122 sub_generator = run_test_4_baseDomain(test_generator); 123 sub_generator.next(); 124 yield; 125 126 sub_generator = run_test_4_exists( 127 test_generator, 128 3, 129 "ALTER TABLE moz_cookies ADD creationTime INTEGER" 130 ); 131 sub_generator.next(); 132 yield; 133 134 sub_generator = run_test_4_exists( 135 test_generator, 136 3, 137 "CREATE UNIQUE INDEX moz_uniqueid ON moz_cookies (name, host, path)" 138 ); 139 sub_generator.next(); 140 yield; 141 142 finish_test(); 143 } 144 145 const garbage = "hello thar!"; 146 147 function create_garbage_file(file) { 148 // Create an empty database file. 149 file.create(Ci.nsIFile.NORMAL_FILE_TYPE, -1); 150 Assert.ok(file.exists()); 151 Assert.equal(file.fileSize, 0); 152 153 // Write some garbage to it. 154 let ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance( 155 Ci.nsIFileOutputStream 156 ); 157 ostream.init(file, -1, -1, 0); 158 ostream.write(garbage, garbage.length); 159 ostream.flush(); 160 ostream.close(); 161 162 file = file.clone(); // Windows maintains a stat cache. It's lame. 163 Assert.equal(file.fileSize, garbage.length); 164 } 165 166 function check_garbage_file(file) { 167 Assert.ok(file.exists()); 168 Assert.equal(file.fileSize, garbage.length); 169 file.remove(false); 170 Assert.ok(!file.exists()); 171 } 172 173 function* run_test_1(generator) { 174 // Create a garbage database file. 175 create_garbage_file(cookieFile); 176 177 let uri = NetUtil.newURI("http://foo.com/"); 178 const channel = NetUtil.newChannel({ 179 uri, 180 loadUsingSystemPrincipal: true, 181 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 182 }); 183 184 // Load the profile and populate it. 185 Services.cookies.setCookieStringFromHttp( 186 uri, 187 "oh=hai; max-age=1000", 188 channel 189 ); 190 191 // Fake a profile change. 192 do_close_profile(sub_generator); 193 yield; 194 do_load_profile(); 195 196 // Check that the new database contains the cookie, and the old file was 197 // renamed. 198 Assert.equal(do_count_cookies(), 1); 199 check_garbage_file(backupFile); 200 201 // Close the profile. 202 do_close_profile(sub_generator); 203 yield; 204 205 // Clean up. 206 cookieFile.remove(false); 207 Assert.ok(!cookieFile.exists()); 208 do_run_generator(generator); 209 } 210 211 function* run_test_2(generator) { 212 // Load the profile and populate it. 213 do_load_profile(); 214 let uri = NetUtil.newURI("http://foo.com/"); 215 const channel = NetUtil.newChannel({ 216 uri, 217 loadUsingSystemPrincipal: true, 218 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 219 }); 220 Services.cookies.setCookieStringFromHttp( 221 uri, 222 "oh=hai; max-age=1000", 223 channel 224 ); 225 226 // Fake a profile change. 227 do_close_profile(sub_generator); 228 yield; 229 230 // Drop the table. 231 let db = Services.storage.openDatabase(cookieFile); 232 db.executeSimpleSQL("DROP TABLE moz_cookies"); 233 db.close(); 234 235 // Load the profile and check that the table is recreated in-place. 236 do_load_profile(); 237 Assert.equal(do_count_cookies(), 0); 238 Assert.ok(!backupFile.exists()); 239 240 // Close the profile. 241 do_close_profile(sub_generator); 242 yield; 243 244 // Clean up. 245 cookieFile.remove(false); 246 Assert.ok(!cookieFile.exists()); 247 do_run_generator(generator); 248 } 249 250 function* run_test_3(generator, schema) { 251 // Manually create a schema 2 database, populate it, and set the schema 252 // version to the desired number. 253 let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); 254 schema2db.insertCookie(cookie); 255 schema2db.db.schemaVersion = schema; 256 schema2db.close(); 257 258 // Load the profile and check that the column existence test fails. 259 do_load_profile(); 260 Assert.equal(do_count_cookies(), 0); 261 262 // Close the profile. 263 do_close_profile(sub_generator); 264 yield; 265 266 // Check that the schema version has been reset. 267 let db = Services.storage.openDatabase(cookieFile); 268 Assert.equal(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); 269 db.close(); 270 271 // Clean up. 272 cookieFile.remove(false); 273 Assert.ok(!cookieFile.exists()); 274 do_run_generator(generator); 275 } 276 277 function* run_test_4_exists(generator, schema, stmt) { 278 // Manually create a database, populate it, and add the desired column. 279 let db = new CookieDatabaseConnection(do_get_cookie_file(profile), schema); 280 db.insertCookie(cookie); 281 db.db.executeSimpleSQL(stmt); 282 db.close(); 283 284 // Load the profile and check that migration fails. 285 do_load_profile(); 286 Assert.equal(do_count_cookies(), 0); 287 288 // Close the profile. 289 do_close_profile(sub_generator); 290 yield; 291 292 // Check that the schema version has been reset and the backup file exists. 293 db = Services.storage.openDatabase(cookieFile); 294 Assert.equal(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); 295 db.close(); 296 Assert.ok(backupFile.exists()); 297 298 // Clean up. 299 cookieFile.remove(false); 300 backupFile.remove(false); 301 Assert.ok(!cookieFile.exists()); 302 Assert.ok(!backupFile.exists()); 303 do_run_generator(generator); 304 } 305 306 function* run_test_4_baseDomain(generator) { 307 // Manually create a database and populate it with a bad host. 308 let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); 309 let badCookie = new Cookie( 310 "oh", 311 "hai", 312 ".", 313 "/", 314 futureExpiry, 315 now, 316 now, 317 false, 318 false, 319 false 320 ); 321 db.insertCookie(badCookie); 322 db.close(); 323 324 // Load the profile and check that migration fails. 325 do_load_profile(); 326 Assert.equal(do_count_cookies(), 0); 327 328 // Close the profile. 329 do_close_profile(sub_generator); 330 yield; 331 332 // Check that the schema version has been reset and the backup file exists. 333 db = Services.storage.openDatabase(cookieFile); 334 Assert.equal(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); 335 db.close(); 336 Assert.ok(backupFile.exists()); 337 338 // Clean up. 339 cookieFile.remove(false); 340 backupFile.remove(false); 341 Assert.ok(!cookieFile.exists()); 342 Assert.ok(!backupFile.exists()); 343 do_run_generator(generator); 344 }