1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/tests/migration/test_current_from_v10.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,368 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +/** 1.8 + * This file tests migration invariants from schema version 10 to the current 1.9 + * schema version. 1.10 + */ 1.11 + 1.12 +//////////////////////////////////////////////////////////////////////////////// 1.13 +//// Constants 1.14 + 1.15 +const kGuidAnnotationName = "sync/guid"; 1.16 +const kExpectedAnnotations = 5; 1.17 +const kExpectedValidGuids = 2; 1.18 + 1.19 +//////////////////////////////////////////////////////////////////////////////// 1.20 +//// Globals 1.21 + 1.22 +// Set in test_initial_state to the value in the database. 1.23 +var gItemGuid = []; 1.24 +var gItemId = []; 1.25 +var gPlaceGuid = []; 1.26 +var gPlaceId = []; 1.27 + 1.28 +//////////////////////////////////////////////////////////////////////////////// 1.29 +//// Helpers 1.30 + 1.31 +/** 1.32 + * Determines if a guid is valid or not. 1.33 + * 1.34 + * @return true if it is a valid guid, false otherwise. 1.35 + */ 1.36 +function isValidGuid(aGuid) 1.37 +{ 1.38 + return /^[a-zA-Z0-9\-_]{12}$/.test(aGuid); 1.39 +} 1.40 + 1.41 +//////////////////////////////////////////////////////////////////////////////// 1.42 +//// Test Functions 1.43 + 1.44 +function test_initial_state() 1.45 +{ 1.46 + // Mostly sanity checks our starting DB to make sure it's setup as we expect 1.47 + // it to be. 1.48 + let dbFile = gProfD.clone(); 1.49 + dbFile.append(kDBName); 1.50 + let db = Services.storage.openUnsharedDatabase(dbFile); 1.51 + 1.52 + let stmt = db.createStatement("PRAGMA journal_mode"); 1.53 + do_check_true(stmt.executeStep()); 1.54 + // WAL journal mode should not be set on this database. 1.55 + do_check_neq(stmt.getString(0).toLowerCase(), "wal"); 1.56 + stmt.finalize(); 1.57 + 1.58 + do_check_false(db.indexExists("moz_bookmarks_guid_uniqueindex")); 1.59 + do_check_false(db.indexExists("moz_places_guid_uniqueindex")); 1.60 + 1.61 + // There should be five item annotations for a bookmark guid. 1.62 + stmt = db.createStatement( 1.63 + "SELECT content AS guid, item_id " 1.64 + + "FROM moz_items_annos " 1.65 + + "WHERE anno_attribute_id = ( " 1.66 + + "SELECT id " 1.67 + + "FROM moz_anno_attributes " 1.68 + + "WHERE name = :attr_name " 1.69 + + ") " 1.70 + ); 1.71 + stmt.params.attr_name = kGuidAnnotationName; 1.72 + while (stmt.executeStep()) { 1.73 + gItemGuid.push(stmt.row.guid); 1.74 + gItemId.push(stmt.row.item_id) 1.75 + } 1.76 + do_check_eq(gItemGuid.length, gItemId.length); 1.77 + do_check_eq(gItemGuid.length, kExpectedAnnotations); 1.78 + stmt.finalize(); 1.79 + 1.80 + // There should be five item annotations for a place guid. 1.81 + stmt = db.createStatement( 1.82 + "SELECT content AS guid, place_id " 1.83 + + "FROM moz_annos " 1.84 + + "WHERE anno_attribute_id = ( " 1.85 + + "SELECT id " 1.86 + + "FROM moz_anno_attributes " 1.87 + + "WHERE name = :attr_name " 1.88 + + ") " 1.89 + ); 1.90 + stmt.params.attr_name = kGuidAnnotationName; 1.91 + while (stmt.executeStep()) { 1.92 + gPlaceGuid.push(stmt.row.guid); 1.93 + gPlaceId.push(stmt.row.place_id) 1.94 + } 1.95 + do_check_eq(gPlaceGuid.length, gPlaceId.length); 1.96 + do_check_eq(gPlaceGuid.length, kExpectedAnnotations); 1.97 + stmt.finalize(); 1.98 + 1.99 + // Check our schema version to make sure it is actually at 10. 1.100 + do_check_eq(db.schemaVersion, 10); 1.101 + 1.102 + db.close(); 1.103 + run_next_test(); 1.104 +} 1.105 + 1.106 +function test_moz_bookmarks_guid_exists() 1.107 +{ 1.108 + // This will throw if the column does not exist 1.109 + let stmt = DBConn().createStatement( 1.110 + "SELECT guid " 1.111 + + "FROM moz_bookmarks " 1.112 + ); 1.113 + stmt.finalize(); 1.114 + 1.115 + run_next_test(); 1.116 +} 1.117 + 1.118 +function test_bookmark_guids_non_null() 1.119 +{ 1.120 + // First, sanity check that we have a non-zero amount of bookmarks. 1.121 + let stmt = DBConn().createStatement( 1.122 + "SELECT COUNT(1) " 1.123 + + "FROM moz_bookmarks " 1.124 + ); 1.125 + do_check_true(stmt.executeStep()); 1.126 + do_check_neq(stmt.getInt32(0), 0); 1.127 + stmt.finalize(); 1.128 + 1.129 + // Now, make sure we have no NULL guid entry. 1.130 + stmt = DBConn().createStatement( 1.131 + "SELECT guid " 1.132 + + "FROM moz_bookmarks " 1.133 + + "WHERE guid IS NULL " 1.134 + ); 1.135 + do_check_false(stmt.executeStep()); 1.136 + stmt.finalize(); 1.137 + run_next_test(); 1.138 +} 1.139 + 1.140 +function test_bookmark_guid_annotation_imported() 1.141 +{ 1.142 + // Make sure we have the imported guid; not a newly generated one. 1.143 + let stmt = DBConn().createStatement( 1.144 + "SELECT id " 1.145 + + "FROM moz_bookmarks " 1.146 + + "WHERE guid = :guid " 1.147 + + "AND id = :item_id " 1.148 + ); 1.149 + let validGuids = 0; 1.150 + let seenGuids = []; 1.151 + for (let i = 0; i < gItemGuid.length; i++) { 1.152 + let guid = gItemGuid[i]; 1.153 + stmt.params.guid = guid; 1.154 + stmt.params.item_id = gItemId[i]; 1.155 + 1.156 + // Check that it is a valid guid that we expect, and that it is not a 1.157 + // duplicate (which would violate the unique constraint). 1.158 + let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1; 1.159 + seenGuids.push(guid); 1.160 + 1.161 + if (valid) { 1.162 + validGuids++; 1.163 + do_check_true(stmt.executeStep()); 1.164 + } 1.165 + else { 1.166 + do_check_false(stmt.executeStep()); 1.167 + } 1.168 + stmt.reset(); 1.169 + } 1.170 + do_check_eq(validGuids, kExpectedValidGuids); 1.171 + stmt.finalize(); 1.172 + 1.173 + run_next_test(); 1.174 +} 1.175 + 1.176 +function test_bookmark_guid_annotation_removed() 1.177 +{ 1.178 + let stmt = DBConn().createStatement( 1.179 + "SELECT COUNT(1) " 1.180 + + "FROM moz_items_annos " 1.181 + + "WHERE anno_attribute_id = ( " 1.182 + + "SELECT id " 1.183 + + "FROM moz_anno_attributes " 1.184 + + "WHERE name = :attr_name " 1.185 + + ") " 1.186 + ); 1.187 + stmt.params.attr_name = kGuidAnnotationName; 1.188 + do_check_true(stmt.executeStep()); 1.189 + do_check_eq(stmt.getInt32(0), 0); 1.190 + stmt.finalize(); 1.191 + 1.192 + run_next_test(); 1.193 +} 1.194 + 1.195 +function test_moz_places_guid_exists() 1.196 +{ 1.197 + // This will throw if the column does not exist 1.198 + let stmt = DBConn().createStatement( 1.199 + "SELECT guid " 1.200 + + "FROM moz_places " 1.201 + ); 1.202 + stmt.finalize(); 1.203 + 1.204 + run_next_test(); 1.205 +} 1.206 + 1.207 +function test_place_guids_non_null() 1.208 +{ 1.209 + // First, sanity check that we have a non-zero amount of places. 1.210 + let stmt = DBConn().createStatement( 1.211 + "SELECT COUNT(1) " 1.212 + + "FROM moz_places " 1.213 + ); 1.214 + do_check_true(stmt.executeStep()); 1.215 + do_check_neq(stmt.getInt32(0), 0); 1.216 + stmt.finalize(); 1.217 + 1.218 + // Now, make sure we have no NULL guid entry. 1.219 + stmt = DBConn().createStatement( 1.220 + "SELECT guid " 1.221 + + "FROM moz_places " 1.222 + + "WHERE guid IS NULL " 1.223 + ); 1.224 + do_check_false(stmt.executeStep()); 1.225 + stmt.finalize(); 1.226 + run_next_test(); 1.227 +} 1.228 + 1.229 +function test_place_guid_annotation_imported() 1.230 +{ 1.231 + // Make sure we have the imported guid; not a newly generated one. 1.232 + let stmt = DBConn().createStatement( 1.233 + "SELECT id " 1.234 + + "FROM moz_places " 1.235 + + "WHERE guid = :guid " 1.236 + + "AND id = :item_id " 1.237 + ); 1.238 + let validGuids = 0; 1.239 + let seenGuids = []; 1.240 + for (let i = 0; i < gPlaceGuid.length; i++) { 1.241 + let guid = gPlaceGuid[i]; 1.242 + stmt.params.guid = guid; 1.243 + stmt.params.item_id = gPlaceId[i]; 1.244 + 1.245 + // Check that it is a valid guid that we expect, and that it is not a 1.246 + // duplicate (which would violate the unique constraint). 1.247 + let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1; 1.248 + seenGuids.push(guid); 1.249 + 1.250 + if (valid) { 1.251 + validGuids++; 1.252 + do_check_true(stmt.executeStep()); 1.253 + } 1.254 + else { 1.255 + do_check_false(stmt.executeStep()); 1.256 + } 1.257 + stmt.reset(); 1.258 + } 1.259 + do_check_eq(validGuids, kExpectedValidGuids); 1.260 + stmt.finalize(); 1.261 + 1.262 + run_next_test(); 1.263 +} 1.264 + 1.265 +function test_place_guid_annotation_removed() 1.266 +{ 1.267 + let stmt = DBConn().createStatement( 1.268 + "SELECT COUNT(1) " 1.269 + + "FROM moz_annos " 1.270 + + "WHERE anno_attribute_id = ( " 1.271 + + "SELECT id " 1.272 + + "FROM moz_anno_attributes " 1.273 + + "WHERE name = :attr_name " 1.274 + + ") " 1.275 + ); 1.276 + stmt.params.attr_name = kGuidAnnotationName; 1.277 + do_check_true(stmt.executeStep()); 1.278 + do_check_eq(stmt.getInt32(0), 0); 1.279 + stmt.finalize(); 1.280 + 1.281 + run_next_test(); 1.282 +} 1.283 + 1.284 +function test_moz_hosts() 1.285 +{ 1.286 + // This will throw if the column does not exist 1.287 + let stmt = DBConn().createStatement( 1.288 + "SELECT host, frecency, typed, prefix " 1.289 + + "FROM moz_hosts " 1.290 + ); 1.291 + stmt.finalize(); 1.292 + 1.293 + // moz_hosts is populated asynchronously, so query asynchronously to serialize 1.294 + // to that. 1.295 + // check the number of entries in moz_hosts equals the number of 1.296 + // unique rev_host in moz_places 1.297 + stmt = DBConn().createAsyncStatement( 1.298 + "SELECT (SELECT COUNT(host) FROM moz_hosts), " + 1.299 + "(SELECT COUNT(DISTINCT rev_host) " + 1.300 + "FROM moz_places " + 1.301 + "WHERE LENGTH(rev_host) > 1)"); 1.302 + try { 1.303 + stmt.executeAsync({ 1.304 + handleResult: function (aResult) { 1.305 + this._hasResults = true; 1.306 + let row = aResult.getNextRow(); 1.307 + let mozHostsCount = row.getResultByIndex(0); 1.308 + let mozPlacesCount = row.getResultByIndex(1); 1.309 + do_check_true(mozPlacesCount > 0); 1.310 + do_check_eq(mozPlacesCount, mozHostsCount); 1.311 + }, 1.312 + handleError: function () {}, 1.313 + handleCompletion: function (aReason) { 1.314 + do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); 1.315 + do_check_true(this._hasResults); 1.316 + run_next_test(); 1.317 + } 1.318 + }); 1.319 + } 1.320 + finally { 1.321 + stmt.finalize(); 1.322 + } 1.323 +} 1.324 + 1.325 +function test_final_state() 1.326 +{ 1.327 + // We open a new database mostly so that we can check that the settings were 1.328 + // actually saved. 1.329 + let dbFile = gProfD.clone(); 1.330 + dbFile.append(kDBName); 1.331 + let db = Services.storage.openUnsharedDatabase(dbFile); 1.332 + 1.333 + let (stmt = db.createStatement("PRAGMA journal_mode")) { 1.334 + do_check_true(stmt.executeStep()); 1.335 + // WAL journal mode should be set on this database. 1.336 + do_check_eq(stmt.getString(0).toLowerCase(), "wal"); 1.337 + stmt.finalize(); 1.338 + } 1.339 + 1.340 + do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex")); 1.341 + do_check_true(db.indexExists("moz_places_guid_uniqueindex")); 1.342 + do_check_true(db.indexExists("moz_favicons_guid_uniqueindex")); 1.343 + 1.344 + do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION); 1.345 + 1.346 + db.close(); 1.347 + run_next_test(); 1.348 +} 1.349 + 1.350 +//////////////////////////////////////////////////////////////////////////////// 1.351 +//// Test Runner 1.352 + 1.353 +[ 1.354 + test_initial_state, 1.355 + test_moz_bookmarks_guid_exists, 1.356 + test_bookmark_guids_non_null, 1.357 + test_bookmark_guid_annotation_imported, 1.358 + test_bookmark_guid_annotation_removed, 1.359 + test_moz_places_guid_exists, 1.360 + test_place_guids_non_null, 1.361 + test_place_guid_annotation_imported, 1.362 + test_place_guid_annotation_removed, 1.363 + test_moz_hosts, 1.364 + test_final_state, 1.365 +].forEach(add_test); 1.366 + 1.367 +function run_test() 1.368 +{ 1.369 + setPlacesDatabase("places_v10.sqlite"); 1.370 + run_next_test(); 1.371 +}