Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | /** |
michael@0 | 5 | * This file tests migration invariants from schema version 10 to the current |
michael@0 | 6 | * schema version. |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 10 | //// Constants |
michael@0 | 11 | |
michael@0 | 12 | const kGuidAnnotationName = "sync/guid"; |
michael@0 | 13 | const kExpectedAnnotations = 5; |
michael@0 | 14 | const kExpectedValidGuids = 2; |
michael@0 | 15 | |
michael@0 | 16 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 17 | //// Globals |
michael@0 | 18 | |
michael@0 | 19 | // Set in test_initial_state to the value in the database. |
michael@0 | 20 | var gItemGuid = []; |
michael@0 | 21 | var gItemId = []; |
michael@0 | 22 | var gPlaceGuid = []; |
michael@0 | 23 | var gPlaceId = []; |
michael@0 | 24 | |
michael@0 | 25 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 26 | //// Helpers |
michael@0 | 27 | |
michael@0 | 28 | /** |
michael@0 | 29 | * Determines if a guid is valid or not. |
michael@0 | 30 | * |
michael@0 | 31 | * @return true if it is a valid guid, false otherwise. |
michael@0 | 32 | */ |
michael@0 | 33 | function isValidGuid(aGuid) |
michael@0 | 34 | { |
michael@0 | 35 | return /^[a-zA-Z0-9\-_]{12}$/.test(aGuid); |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 39 | //// Test Functions |
michael@0 | 40 | |
michael@0 | 41 | function test_initial_state() |
michael@0 | 42 | { |
michael@0 | 43 | // Mostly sanity checks our starting DB to make sure it's setup as we expect |
michael@0 | 44 | // it to be. |
michael@0 | 45 | let dbFile = gProfD.clone(); |
michael@0 | 46 | dbFile.append(kDBName); |
michael@0 | 47 | let db = Services.storage.openUnsharedDatabase(dbFile); |
michael@0 | 48 | |
michael@0 | 49 | let stmt = db.createStatement("PRAGMA journal_mode"); |
michael@0 | 50 | do_check_true(stmt.executeStep()); |
michael@0 | 51 | // WAL journal mode should not be set on this database. |
michael@0 | 52 | do_check_neq(stmt.getString(0).toLowerCase(), "wal"); |
michael@0 | 53 | stmt.finalize(); |
michael@0 | 54 | |
michael@0 | 55 | do_check_false(db.indexExists("moz_bookmarks_guid_uniqueindex")); |
michael@0 | 56 | do_check_false(db.indexExists("moz_places_guid_uniqueindex")); |
michael@0 | 57 | |
michael@0 | 58 | // There should be five item annotations for a bookmark guid. |
michael@0 | 59 | stmt = db.createStatement( |
michael@0 | 60 | "SELECT content AS guid, item_id " |
michael@0 | 61 | + "FROM moz_items_annos " |
michael@0 | 62 | + "WHERE anno_attribute_id = ( " |
michael@0 | 63 | + "SELECT id " |
michael@0 | 64 | + "FROM moz_anno_attributes " |
michael@0 | 65 | + "WHERE name = :attr_name " |
michael@0 | 66 | + ") " |
michael@0 | 67 | ); |
michael@0 | 68 | stmt.params.attr_name = kGuidAnnotationName; |
michael@0 | 69 | while (stmt.executeStep()) { |
michael@0 | 70 | gItemGuid.push(stmt.row.guid); |
michael@0 | 71 | gItemId.push(stmt.row.item_id) |
michael@0 | 72 | } |
michael@0 | 73 | do_check_eq(gItemGuid.length, gItemId.length); |
michael@0 | 74 | do_check_eq(gItemGuid.length, kExpectedAnnotations); |
michael@0 | 75 | stmt.finalize(); |
michael@0 | 76 | |
michael@0 | 77 | // There should be five item annotations for a place guid. |
michael@0 | 78 | stmt = db.createStatement( |
michael@0 | 79 | "SELECT content AS guid, place_id " |
michael@0 | 80 | + "FROM moz_annos " |
michael@0 | 81 | + "WHERE anno_attribute_id = ( " |
michael@0 | 82 | + "SELECT id " |
michael@0 | 83 | + "FROM moz_anno_attributes " |
michael@0 | 84 | + "WHERE name = :attr_name " |
michael@0 | 85 | + ") " |
michael@0 | 86 | ); |
michael@0 | 87 | stmt.params.attr_name = kGuidAnnotationName; |
michael@0 | 88 | while (stmt.executeStep()) { |
michael@0 | 89 | gPlaceGuid.push(stmt.row.guid); |
michael@0 | 90 | gPlaceId.push(stmt.row.place_id) |
michael@0 | 91 | } |
michael@0 | 92 | do_check_eq(gPlaceGuid.length, gPlaceId.length); |
michael@0 | 93 | do_check_eq(gPlaceGuid.length, kExpectedAnnotations); |
michael@0 | 94 | stmt.finalize(); |
michael@0 | 95 | |
michael@0 | 96 | // Check our schema version to make sure it is actually at 10. |
michael@0 | 97 | do_check_eq(db.schemaVersion, 10); |
michael@0 | 98 | |
michael@0 | 99 | db.close(); |
michael@0 | 100 | run_next_test(); |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | function test_moz_bookmarks_guid_exists() |
michael@0 | 104 | { |
michael@0 | 105 | // This will throw if the column does not exist |
michael@0 | 106 | let stmt = DBConn().createStatement( |
michael@0 | 107 | "SELECT guid " |
michael@0 | 108 | + "FROM moz_bookmarks " |
michael@0 | 109 | ); |
michael@0 | 110 | stmt.finalize(); |
michael@0 | 111 | |
michael@0 | 112 | run_next_test(); |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | function test_bookmark_guids_non_null() |
michael@0 | 116 | { |
michael@0 | 117 | // First, sanity check that we have a non-zero amount of bookmarks. |
michael@0 | 118 | let stmt = DBConn().createStatement( |
michael@0 | 119 | "SELECT COUNT(1) " |
michael@0 | 120 | + "FROM moz_bookmarks " |
michael@0 | 121 | ); |
michael@0 | 122 | do_check_true(stmt.executeStep()); |
michael@0 | 123 | do_check_neq(stmt.getInt32(0), 0); |
michael@0 | 124 | stmt.finalize(); |
michael@0 | 125 | |
michael@0 | 126 | // Now, make sure we have no NULL guid entry. |
michael@0 | 127 | stmt = DBConn().createStatement( |
michael@0 | 128 | "SELECT guid " |
michael@0 | 129 | + "FROM moz_bookmarks " |
michael@0 | 130 | + "WHERE guid IS NULL " |
michael@0 | 131 | ); |
michael@0 | 132 | do_check_false(stmt.executeStep()); |
michael@0 | 133 | stmt.finalize(); |
michael@0 | 134 | run_next_test(); |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | function test_bookmark_guid_annotation_imported() |
michael@0 | 138 | { |
michael@0 | 139 | // Make sure we have the imported guid; not a newly generated one. |
michael@0 | 140 | let stmt = DBConn().createStatement( |
michael@0 | 141 | "SELECT id " |
michael@0 | 142 | + "FROM moz_bookmarks " |
michael@0 | 143 | + "WHERE guid = :guid " |
michael@0 | 144 | + "AND id = :item_id " |
michael@0 | 145 | ); |
michael@0 | 146 | let validGuids = 0; |
michael@0 | 147 | let seenGuids = []; |
michael@0 | 148 | for (let i = 0; i < gItemGuid.length; i++) { |
michael@0 | 149 | let guid = gItemGuid[i]; |
michael@0 | 150 | stmt.params.guid = guid; |
michael@0 | 151 | stmt.params.item_id = gItemId[i]; |
michael@0 | 152 | |
michael@0 | 153 | // Check that it is a valid guid that we expect, and that it is not a |
michael@0 | 154 | // duplicate (which would violate the unique constraint). |
michael@0 | 155 | let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1; |
michael@0 | 156 | seenGuids.push(guid); |
michael@0 | 157 | |
michael@0 | 158 | if (valid) { |
michael@0 | 159 | validGuids++; |
michael@0 | 160 | do_check_true(stmt.executeStep()); |
michael@0 | 161 | } |
michael@0 | 162 | else { |
michael@0 | 163 | do_check_false(stmt.executeStep()); |
michael@0 | 164 | } |
michael@0 | 165 | stmt.reset(); |
michael@0 | 166 | } |
michael@0 | 167 | do_check_eq(validGuids, kExpectedValidGuids); |
michael@0 | 168 | stmt.finalize(); |
michael@0 | 169 | |
michael@0 | 170 | run_next_test(); |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | function test_bookmark_guid_annotation_removed() |
michael@0 | 174 | { |
michael@0 | 175 | let stmt = DBConn().createStatement( |
michael@0 | 176 | "SELECT COUNT(1) " |
michael@0 | 177 | + "FROM moz_items_annos " |
michael@0 | 178 | + "WHERE anno_attribute_id = ( " |
michael@0 | 179 | + "SELECT id " |
michael@0 | 180 | + "FROM moz_anno_attributes " |
michael@0 | 181 | + "WHERE name = :attr_name " |
michael@0 | 182 | + ") " |
michael@0 | 183 | ); |
michael@0 | 184 | stmt.params.attr_name = kGuidAnnotationName; |
michael@0 | 185 | do_check_true(stmt.executeStep()); |
michael@0 | 186 | do_check_eq(stmt.getInt32(0), 0); |
michael@0 | 187 | stmt.finalize(); |
michael@0 | 188 | |
michael@0 | 189 | run_next_test(); |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | function test_moz_places_guid_exists() |
michael@0 | 193 | { |
michael@0 | 194 | // This will throw if the column does not exist |
michael@0 | 195 | let stmt = DBConn().createStatement( |
michael@0 | 196 | "SELECT guid " |
michael@0 | 197 | + "FROM moz_places " |
michael@0 | 198 | ); |
michael@0 | 199 | stmt.finalize(); |
michael@0 | 200 | |
michael@0 | 201 | run_next_test(); |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | function test_place_guids_non_null() |
michael@0 | 205 | { |
michael@0 | 206 | // First, sanity check that we have a non-zero amount of places. |
michael@0 | 207 | let stmt = DBConn().createStatement( |
michael@0 | 208 | "SELECT COUNT(1) " |
michael@0 | 209 | + "FROM moz_places " |
michael@0 | 210 | ); |
michael@0 | 211 | do_check_true(stmt.executeStep()); |
michael@0 | 212 | do_check_neq(stmt.getInt32(0), 0); |
michael@0 | 213 | stmt.finalize(); |
michael@0 | 214 | |
michael@0 | 215 | // Now, make sure we have no NULL guid entry. |
michael@0 | 216 | stmt = DBConn().createStatement( |
michael@0 | 217 | "SELECT guid " |
michael@0 | 218 | + "FROM moz_places " |
michael@0 | 219 | + "WHERE guid IS NULL " |
michael@0 | 220 | ); |
michael@0 | 221 | do_check_false(stmt.executeStep()); |
michael@0 | 222 | stmt.finalize(); |
michael@0 | 223 | run_next_test(); |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | function test_place_guid_annotation_imported() |
michael@0 | 227 | { |
michael@0 | 228 | // Make sure we have the imported guid; not a newly generated one. |
michael@0 | 229 | let stmt = DBConn().createStatement( |
michael@0 | 230 | "SELECT id " |
michael@0 | 231 | + "FROM moz_places " |
michael@0 | 232 | + "WHERE guid = :guid " |
michael@0 | 233 | + "AND id = :item_id " |
michael@0 | 234 | ); |
michael@0 | 235 | let validGuids = 0; |
michael@0 | 236 | let seenGuids = []; |
michael@0 | 237 | for (let i = 0; i < gPlaceGuid.length; i++) { |
michael@0 | 238 | let guid = gPlaceGuid[i]; |
michael@0 | 239 | stmt.params.guid = guid; |
michael@0 | 240 | stmt.params.item_id = gPlaceId[i]; |
michael@0 | 241 | |
michael@0 | 242 | // Check that it is a valid guid that we expect, and that it is not a |
michael@0 | 243 | // duplicate (which would violate the unique constraint). |
michael@0 | 244 | let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1; |
michael@0 | 245 | seenGuids.push(guid); |
michael@0 | 246 | |
michael@0 | 247 | if (valid) { |
michael@0 | 248 | validGuids++; |
michael@0 | 249 | do_check_true(stmt.executeStep()); |
michael@0 | 250 | } |
michael@0 | 251 | else { |
michael@0 | 252 | do_check_false(stmt.executeStep()); |
michael@0 | 253 | } |
michael@0 | 254 | stmt.reset(); |
michael@0 | 255 | } |
michael@0 | 256 | do_check_eq(validGuids, kExpectedValidGuids); |
michael@0 | 257 | stmt.finalize(); |
michael@0 | 258 | |
michael@0 | 259 | run_next_test(); |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | function test_place_guid_annotation_removed() |
michael@0 | 263 | { |
michael@0 | 264 | let stmt = DBConn().createStatement( |
michael@0 | 265 | "SELECT COUNT(1) " |
michael@0 | 266 | + "FROM moz_annos " |
michael@0 | 267 | + "WHERE anno_attribute_id = ( " |
michael@0 | 268 | + "SELECT id " |
michael@0 | 269 | + "FROM moz_anno_attributes " |
michael@0 | 270 | + "WHERE name = :attr_name " |
michael@0 | 271 | + ") " |
michael@0 | 272 | ); |
michael@0 | 273 | stmt.params.attr_name = kGuidAnnotationName; |
michael@0 | 274 | do_check_true(stmt.executeStep()); |
michael@0 | 275 | do_check_eq(stmt.getInt32(0), 0); |
michael@0 | 276 | stmt.finalize(); |
michael@0 | 277 | |
michael@0 | 278 | run_next_test(); |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | function test_moz_hosts() |
michael@0 | 282 | { |
michael@0 | 283 | // This will throw if the column does not exist |
michael@0 | 284 | let stmt = DBConn().createStatement( |
michael@0 | 285 | "SELECT host, frecency, typed, prefix " |
michael@0 | 286 | + "FROM moz_hosts " |
michael@0 | 287 | ); |
michael@0 | 288 | stmt.finalize(); |
michael@0 | 289 | |
michael@0 | 290 | // moz_hosts is populated asynchronously, so query asynchronously to serialize |
michael@0 | 291 | // to that. |
michael@0 | 292 | // check the number of entries in moz_hosts equals the number of |
michael@0 | 293 | // unique rev_host in moz_places |
michael@0 | 294 | stmt = DBConn().createAsyncStatement( |
michael@0 | 295 | "SELECT (SELECT COUNT(host) FROM moz_hosts), " + |
michael@0 | 296 | "(SELECT COUNT(DISTINCT rev_host) " + |
michael@0 | 297 | "FROM moz_places " + |
michael@0 | 298 | "WHERE LENGTH(rev_host) > 1)"); |
michael@0 | 299 | try { |
michael@0 | 300 | stmt.executeAsync({ |
michael@0 | 301 | handleResult: function (aResult) { |
michael@0 | 302 | this._hasResults = true; |
michael@0 | 303 | let row = aResult.getNextRow(); |
michael@0 | 304 | let mozHostsCount = row.getResultByIndex(0); |
michael@0 | 305 | let mozPlacesCount = row.getResultByIndex(1); |
michael@0 | 306 | do_check_true(mozPlacesCount > 0); |
michael@0 | 307 | do_check_eq(mozPlacesCount, mozHostsCount); |
michael@0 | 308 | }, |
michael@0 | 309 | handleError: function () {}, |
michael@0 | 310 | handleCompletion: function (aReason) { |
michael@0 | 311 | do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); |
michael@0 | 312 | do_check_true(this._hasResults); |
michael@0 | 313 | run_next_test(); |
michael@0 | 314 | } |
michael@0 | 315 | }); |
michael@0 | 316 | } |
michael@0 | 317 | finally { |
michael@0 | 318 | stmt.finalize(); |
michael@0 | 319 | } |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | function test_final_state() |
michael@0 | 323 | { |
michael@0 | 324 | // We open a new database mostly so that we can check that the settings were |
michael@0 | 325 | // actually saved. |
michael@0 | 326 | let dbFile = gProfD.clone(); |
michael@0 | 327 | dbFile.append(kDBName); |
michael@0 | 328 | let db = Services.storage.openUnsharedDatabase(dbFile); |
michael@0 | 329 | |
michael@0 | 330 | let (stmt = db.createStatement("PRAGMA journal_mode")) { |
michael@0 | 331 | do_check_true(stmt.executeStep()); |
michael@0 | 332 | // WAL journal mode should be set on this database. |
michael@0 | 333 | do_check_eq(stmt.getString(0).toLowerCase(), "wal"); |
michael@0 | 334 | stmt.finalize(); |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex")); |
michael@0 | 338 | do_check_true(db.indexExists("moz_places_guid_uniqueindex")); |
michael@0 | 339 | do_check_true(db.indexExists("moz_favicons_guid_uniqueindex")); |
michael@0 | 340 | |
michael@0 | 341 | do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION); |
michael@0 | 342 | |
michael@0 | 343 | db.close(); |
michael@0 | 344 | run_next_test(); |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 348 | //// Test Runner |
michael@0 | 349 | |
michael@0 | 350 | [ |
michael@0 | 351 | test_initial_state, |
michael@0 | 352 | test_moz_bookmarks_guid_exists, |
michael@0 | 353 | test_bookmark_guids_non_null, |
michael@0 | 354 | test_bookmark_guid_annotation_imported, |
michael@0 | 355 | test_bookmark_guid_annotation_removed, |
michael@0 | 356 | test_moz_places_guid_exists, |
michael@0 | 357 | test_place_guids_non_null, |
michael@0 | 358 | test_place_guid_annotation_imported, |
michael@0 | 359 | test_place_guid_annotation_removed, |
michael@0 | 360 | test_moz_hosts, |
michael@0 | 361 | test_final_state, |
michael@0 | 362 | ].forEach(add_test); |
michael@0 | 363 | |
michael@0 | 364 | function run_test() |
michael@0 | 365 | { |
michael@0 | 366 | setPlacesDatabase("places_v10.sqlite"); |
michael@0 | 367 | run_next_test(); |
michael@0 | 368 | } |