1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/tests/unit/test_preventive_maintenance.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1367 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 + /** 1.11 + * Test preventive maintenance 1.12 + * For every maintenance query create an uncoherent db and check that we take 1.13 + * correct fix steps, without polluting valid data. 1.14 + */ 1.15 + 1.16 +// Include PlacesDBUtils module 1.17 +Components.utils.import("resource://gre/modules/PlacesDBUtils.jsm"); 1.18 + 1.19 +const FINISHED_MAINTENANCE_NOTIFICATION_TOPIC = "places-maintenance-finished"; 1.20 + 1.21 +// Get services and database connection 1.22 +let hs = PlacesUtils.history; 1.23 +let bs = PlacesUtils.bookmarks; 1.24 +let ts = PlacesUtils.tagging; 1.25 +let as = PlacesUtils.annotations; 1.26 +let fs = PlacesUtils.favicons; 1.27 + 1.28 +let mDBConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; 1.29 + 1.30 +//------------------------------------------------------------------------------ 1.31 +// Helpers 1.32 + 1.33 +let defaultBookmarksMaxId = 0; 1.34 +function cleanDatabase() { 1.35 + mDBConn.executeSimpleSQL("DELETE FROM moz_places"); 1.36 + mDBConn.executeSimpleSQL("DELETE FROM moz_historyvisits"); 1.37 + mDBConn.executeSimpleSQL("DELETE FROM moz_anno_attributes"); 1.38 + mDBConn.executeSimpleSQL("DELETE FROM moz_annos"); 1.39 + mDBConn.executeSimpleSQL("DELETE FROM moz_items_annos"); 1.40 + mDBConn.executeSimpleSQL("DELETE FROM moz_inputhistory"); 1.41 + mDBConn.executeSimpleSQL("DELETE FROM moz_keywords"); 1.42 + mDBConn.executeSimpleSQL("DELETE FROM moz_favicons"); 1.43 + mDBConn.executeSimpleSQL("DELETE FROM moz_bookmarks WHERE id > " + defaultBookmarksMaxId); 1.44 +} 1.45 + 1.46 +function addPlace(aUrl, aFavicon) { 1.47 + let stmt = mDBConn.createStatement( 1.48 + "INSERT INTO moz_places (url, favicon_id) VALUES (:url, :favicon)"); 1.49 + stmt.params["url"] = aUrl || "http://www.mozilla.org"; 1.50 + stmt.params["favicon"] = aFavicon || null; 1.51 + stmt.execute(); 1.52 + stmt.finalize(); 1.53 + return mDBConn.lastInsertRowID; 1.54 +} 1.55 + 1.56 +function addBookmark(aPlaceId, aType, aParent, aKeywordId, aFolderType, aTitle) { 1.57 + let stmt = mDBConn.createStatement( 1.58 + "INSERT INTO moz_bookmarks (fk, type, parent, keyword_id, folder_type, " 1.59 + + "title, guid) " 1.60 + + "VALUES (:place_id, :type, :parent, :keyword_id, :folder_type, :title, " 1.61 + + "GENERATE_GUID())"); 1.62 + stmt.params["place_id"] = aPlaceId || null; 1.63 + stmt.params["type"] = aType || bs.TYPE_BOOKMARK; 1.64 + stmt.params["parent"] = aParent || bs.unfiledBookmarksFolder; 1.65 + stmt.params["keyword_id"] = aKeywordId || null; 1.66 + stmt.params["folder_type"] = aFolderType || null; 1.67 + stmt.params["title"] = typeof(aTitle) == "string" ? aTitle : null; 1.68 + stmt.execute(); 1.69 + stmt.finalize(); 1.70 + return mDBConn.lastInsertRowID; 1.71 +} 1.72 + 1.73 +//------------------------------------------------------------------------------ 1.74 +// Tests 1.75 + 1.76 +let tests = []; 1.77 + 1.78 +//------------------------------------------------------------------------------ 1.79 + 1.80 +tests.push({ 1.81 + name: "A.1", 1.82 + desc: "Remove obsolete annotations from moz_annos", 1.83 + 1.84 + _obsoleteWeaveAttribute: "weave/test", 1.85 + _placeId: null, 1.86 + 1.87 + setup: function() { 1.88 + // Add a place to ensure place_id = 1 is valid. 1.89 + this._placeId = addPlace(); 1.90 + // Add an obsolete attribute. 1.91 + let stmt = mDBConn.createStatement( 1.92 + "INSERT INTO moz_anno_attributes (name) VALUES (:anno)" 1.93 + ); 1.94 + stmt.params['anno'] = this._obsoleteWeaveAttribute; 1.95 + stmt.execute(); 1.96 + stmt.finalize(); 1.97 + stmt = mDBConn.createStatement( 1.98 + "INSERT INTO moz_annos (place_id, anno_attribute_id) " 1.99 + + "VALUES (:place_id, " 1.100 + + "(SELECT id FROM moz_anno_attributes WHERE name = :anno)" 1.101 + + ")" 1.102 + ); 1.103 + stmt.params['place_id'] = this._placeId; 1.104 + stmt.params['anno'] = this._obsoleteWeaveAttribute; 1.105 + stmt.execute(); 1.106 + stmt.finalize(); 1.107 + }, 1.108 + 1.109 + check: function() { 1.110 + // Check that the obsolete annotation has been removed. 1.111 + let stmt = mDBConn.createStatement( 1.112 + "SELECT id FROM moz_anno_attributes WHERE name = :anno" 1.113 + ); 1.114 + stmt.params['anno'] = this._obsoleteWeaveAttribute; 1.115 + do_check_false(stmt.executeStep()); 1.116 + stmt.finalize(); 1.117 + } 1.118 +}); 1.119 + 1.120 +tests.push({ 1.121 + name: "A.2", 1.122 + desc: "Remove obsolete annotations from moz_items_annos", 1.123 + 1.124 + _obsoleteSyncAttribute: "sync/children", 1.125 + _obsoleteGuidAttribute: "placesInternal/GUID", 1.126 + _obsoleteWeaveAttribute: "weave/test", 1.127 + _placeId: null, 1.128 + _bookmarkId: null, 1.129 + 1.130 + setup: function() { 1.131 + // Add a place to ensure place_id = 1 is valid. 1.132 + this._placeId = addPlace(); 1.133 + // Add a bookmark. 1.134 + this._bookmarkId = addBookmark(this._placeId); 1.135 + // Add an obsolete attribute. 1.136 + let stmt = mDBConn.createStatement( 1.137 + "INSERT INTO moz_anno_attributes (name) " 1.138 + + "VALUES (:anno1), (:anno2), (:anno3)" 1.139 + ); 1.140 + stmt.params['anno1'] = this._obsoleteSyncAttribute; 1.141 + stmt.params['anno2'] = this._obsoleteGuidAttribute; 1.142 + stmt.params['anno3'] = this._obsoleteWeaveAttribute; 1.143 + stmt.execute(); 1.144 + stmt.finalize(); 1.145 + stmt = mDBConn.createStatement( 1.146 + "INSERT INTO moz_items_annos (item_id, anno_attribute_id) " 1.147 + + "SELECT :item_id, id " 1.148 + + "FROM moz_anno_attributes " 1.149 + + "WHERE name IN (:anno1, :anno2, :anno3)" 1.150 + ); 1.151 + stmt.params['item_id'] = this._bookmarkId; 1.152 + stmt.params['anno1'] = this._obsoleteSyncAttribute; 1.153 + stmt.params['anno2'] = this._obsoleteGuidAttribute; 1.154 + stmt.params['anno3'] = this._obsoleteWeaveAttribute; 1.155 + stmt.execute(); 1.156 + stmt.finalize(); 1.157 + }, 1.158 + 1.159 + check: function() { 1.160 + // Check that the obsolete annotations have been removed. 1.161 + let stmt = mDBConn.createStatement( 1.162 + "SELECT id FROM moz_anno_attributes " 1.163 + + "WHERE name IN (:anno1, :anno2, :anno3)" 1.164 + ); 1.165 + stmt.params['anno1'] = this._obsoleteSyncAttribute; 1.166 + stmt.params['anno2'] = this._obsoleteGuidAttribute; 1.167 + stmt.params['anno3'] = this._obsoleteWeaveAttribute; 1.168 + do_check_false(stmt.executeStep()); 1.169 + stmt.finalize(); 1.170 + } 1.171 +}); 1.172 + 1.173 +tests.push({ 1.174 + name: "A.3", 1.175 + desc: "Remove unused attributes", 1.176 + 1.177 + _usedPageAttribute: "usedPage", 1.178 + _usedItemAttribute: "usedItem", 1.179 + _unusedAttribute: "unused", 1.180 + _placeId: null, 1.181 + _bookmarkId: null, 1.182 + 1.183 + setup: function() { 1.184 + // Add a place to ensure place_id = 1 is valid 1.185 + this._placeId = addPlace(); 1.186 + // add a bookmark 1.187 + this._bookmarkId = addBookmark(this._placeId); 1.188 + // Add a used attribute and an unused one. 1.189 + let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)"); 1.190 + stmt.params['anno'] = this._usedPageAttribute; 1.191 + stmt.execute(); 1.192 + stmt.reset(); 1.193 + stmt.params['anno'] = this._usedItemAttribute; 1.194 + stmt.execute(); 1.195 + stmt.reset(); 1.196 + stmt.params['anno'] = this._unusedAttribute; 1.197 + stmt.execute(); 1.198 + stmt.finalize(); 1.199 + 1.200 + stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))"); 1.201 + stmt.params['place_id'] = this._placeId; 1.202 + stmt.params['anno'] = this._usedPageAttribute; 1.203 + stmt.execute(); 1.204 + stmt.finalize(); 1.205 + stmt = mDBConn.createStatement("INSERT INTO moz_items_annos (item_id, anno_attribute_id) VALUES(:item_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))"); 1.206 + stmt.params['item_id'] = this._bookmarkId; 1.207 + stmt.params['anno'] = this._usedItemAttribute; 1.208 + stmt.execute(); 1.209 + stmt.finalize(); 1.210 + }, 1.211 + 1.212 + check: function() { 1.213 + // Check that used attributes are still there 1.214 + let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno"); 1.215 + stmt.params['anno'] = this._usedPageAttribute; 1.216 + do_check_true(stmt.executeStep()); 1.217 + stmt.reset(); 1.218 + stmt.params['anno'] = this._usedItemAttribute; 1.219 + do_check_true(stmt.executeStep()); 1.220 + stmt.reset(); 1.221 + // Check that unused attribute has been removed 1.222 + stmt.params['anno'] = this._unusedAttribute; 1.223 + do_check_false(stmt.executeStep()); 1.224 + stmt.finalize(); 1.225 + } 1.226 +}); 1.227 + 1.228 +//------------------------------------------------------------------------------ 1.229 + 1.230 +tests.push({ 1.231 + name: "B.1", 1.232 + desc: "Remove annotations with an invalid attribute", 1.233 + 1.234 + _usedPageAttribute: "usedPage", 1.235 + _placeId: null, 1.236 + 1.237 + setup: function() { 1.238 + // Add a place to ensure place_id = 1 is valid 1.239 + this._placeId = addPlace(); 1.240 + // Add a used attribute. 1.241 + let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)"); 1.242 + stmt.params['anno'] = this._usedPageAttribute; 1.243 + stmt.execute(); 1.244 + stmt.finalize(); 1.245 + stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))"); 1.246 + stmt.params['place_id'] = this._placeId; 1.247 + stmt.params['anno'] = this._usedPageAttribute; 1.248 + stmt.execute(); 1.249 + stmt.finalize(); 1.250 + // Add an annotation with a nonexistent attribute 1.251 + stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, 1337)"); 1.252 + stmt.params['place_id'] = this._placeId; 1.253 + stmt.execute(); 1.254 + stmt.finalize(); 1.255 + }, 1.256 + 1.257 + check: function() { 1.258 + // Check that used attribute is still there 1.259 + let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno"); 1.260 + stmt.params['anno'] = this._usedPageAttribute; 1.261 + do_check_true(stmt.executeStep()); 1.262 + stmt.finalize(); 1.263 + // check that annotation with valid attribute is still there 1.264 + stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)"); 1.265 + stmt.params['anno'] = this._usedPageAttribute; 1.266 + do_check_true(stmt.executeStep()); 1.267 + stmt.finalize(); 1.268 + // Check that annotation with bogus attribute has been removed 1.269 + stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE anno_attribute_id = 1337"); 1.270 + do_check_false(stmt.executeStep()); 1.271 + stmt.finalize(); 1.272 + } 1.273 +}); 1.274 + 1.275 +//------------------------------------------------------------------------------ 1.276 + 1.277 +tests.push({ 1.278 + name: "B.2", 1.279 + desc: "Remove orphan page annotations", 1.280 + 1.281 + _usedPageAttribute: "usedPage", 1.282 + _placeId: null, 1.283 + 1.284 + setup: function() { 1.285 + // Add a place to ensure place_id = 1 is valid 1.286 + this._placeId = addPlace(); 1.287 + // Add a used attribute. 1.288 + let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)"); 1.289 + stmt.params['anno'] = this._usedPageAttribute; 1.290 + stmt.execute(); 1.291 + stmt.finalize(); 1.292 + stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))"); 1.293 + stmt.params['place_id'] = this._placeId; 1.294 + stmt.params['anno'] = this._usedPageAttribute; 1.295 + stmt.execute(); 1.296 + stmt.reset(); 1.297 + // Add an annotation to a nonexistent page 1.298 + stmt.params['place_id'] = 1337; 1.299 + stmt.params['anno'] = this._usedPageAttribute; 1.300 + stmt.execute(); 1.301 + stmt.finalize(); 1.302 + }, 1.303 + 1.304 + check: function() { 1.305 + // Check that used attribute is still there 1.306 + let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno"); 1.307 + stmt.params['anno'] = this._usedPageAttribute; 1.308 + do_check_true(stmt.executeStep()); 1.309 + stmt.finalize(); 1.310 + // check that annotation with valid attribute is still there 1.311 + stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)"); 1.312 + stmt.params['anno'] = this._usedPageAttribute; 1.313 + do_check_true(stmt.executeStep()); 1.314 + stmt.finalize(); 1.315 + // Check that an annotation to a nonexistent page has been removed 1.316 + stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE place_id = 1337"); 1.317 + do_check_false(stmt.executeStep()); 1.318 + stmt.finalize(); 1.319 + } 1.320 +}); 1.321 + 1.322 +//------------------------------------------------------------------------------ 1.323 +tests.push({ 1.324 + name: "C.1", 1.325 + desc: "fix missing Places root", 1.326 + 1.327 + setup: function() { 1.328 + // Sanity check: ensure that roots are intact. 1.329 + do_check_eq(bs.getFolderIdForItem(bs.placesRoot), 0); 1.330 + do_check_eq(bs.getFolderIdForItem(bs.bookmarksMenuFolder), bs.placesRoot); 1.331 + do_check_eq(bs.getFolderIdForItem(bs.tagsFolder), bs.placesRoot); 1.332 + do_check_eq(bs.getFolderIdForItem(bs.unfiledBookmarksFolder), bs.placesRoot); 1.333 + do_check_eq(bs.getFolderIdForItem(bs.toolbarFolder), bs.placesRoot); 1.334 + 1.335 + // Remove the root. 1.336 + mDBConn.executeSimpleSQL("DELETE FROM moz_bookmarks WHERE parent = 0"); 1.337 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE parent = 0"); 1.338 + do_check_false(stmt.executeStep()); 1.339 + stmt.finalize(); 1.340 + }, 1.341 + 1.342 + check: function() { 1.343 + // Ensure the roots have been correctly restored. 1.344 + do_check_eq(bs.getFolderIdForItem(bs.placesRoot), 0); 1.345 + do_check_eq(bs.getFolderIdForItem(bs.bookmarksMenuFolder), bs.placesRoot); 1.346 + do_check_eq(bs.getFolderIdForItem(bs.tagsFolder), bs.placesRoot); 1.347 + do_check_eq(bs.getFolderIdForItem(bs.unfiledBookmarksFolder), bs.placesRoot); 1.348 + do_check_eq(bs.getFolderIdForItem(bs.toolbarFolder), bs.placesRoot); 1.349 + } 1.350 +}); 1.351 + 1.352 +//------------------------------------------------------------------------------ 1.353 +tests.push({ 1.354 + name: "C.2", 1.355 + desc: "Fix roots titles", 1.356 + 1.357 + setup: function() { 1.358 + // Sanity check: ensure that roots titles are correct. We can use our check. 1.359 + this.check(); 1.360 + // Change some roots' titles. 1.361 + bs.setItemTitle(bs.placesRoot, "bad title"); 1.362 + do_check_eq(bs.getItemTitle(bs.placesRoot), "bad title"); 1.363 + bs.setItemTitle(bs.unfiledBookmarksFolder, "bad title"); 1.364 + do_check_eq(bs.getItemTitle(bs.unfiledBookmarksFolder), "bad title"); 1.365 + }, 1.366 + 1.367 + check: function() { 1.368 + // Ensure all roots titles are correct. 1.369 + do_check_eq(bs.getItemTitle(bs.placesRoot), ""); 1.370 + do_check_eq(bs.getItemTitle(bs.bookmarksMenuFolder), 1.371 + PlacesUtils.getString("BookmarksMenuFolderTitle")); 1.372 + do_check_eq(bs.getItemTitle(bs.tagsFolder), 1.373 + PlacesUtils.getString("TagsFolderTitle")); 1.374 + do_check_eq(bs.getItemTitle(bs.unfiledBookmarksFolder), 1.375 + PlacesUtils.getString("UnsortedBookmarksFolderTitle")); 1.376 + do_check_eq(bs.getItemTitle(bs.toolbarFolder), 1.377 + PlacesUtils.getString("BookmarksToolbarFolderTitle")); 1.378 + } 1.379 +}); 1.380 + 1.381 +//------------------------------------------------------------------------------ 1.382 + 1.383 +tests.push({ 1.384 + name: "D.1", 1.385 + desc: "Remove items without a valid place", 1.386 + 1.387 + _validItemId: null, 1.388 + _invalidItemId: null, 1.389 + _placeId: null, 1.390 + 1.391 + setup: function() { 1.392 + // Add a place to ensure place_id = 1 is valid 1.393 + this.placeId = addPlace(); 1.394 + // Insert a valid bookmark 1.395 + this._validItemId = addBookmark(this.placeId); 1.396 + // Insert a bookmark with an invalid place 1.397 + this._invalidItemId = addBookmark(1337); 1.398 + }, 1.399 + 1.400 + check: function() { 1.401 + // Check that valid bookmark is still there 1.402 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id"); 1.403 + stmt.params["item_id"] = this._validItemId; 1.404 + do_check_true(stmt.executeStep()); 1.405 + stmt.reset(); 1.406 + // Check that invalid bookmark has been removed 1.407 + stmt.params["item_id"] = this._invalidItemId; 1.408 + do_check_false(stmt.executeStep()); 1.409 + stmt.finalize(); 1.410 + } 1.411 +}); 1.412 + 1.413 +//------------------------------------------------------------------------------ 1.414 + 1.415 +tests.push({ 1.416 + name: "D.2", 1.417 + desc: "Remove items that are not uri bookmarks from tag containers", 1.418 + 1.419 + _tagId: null, 1.420 + _bookmarkId: null, 1.421 + _separatorId: null, 1.422 + _folderId: null, 1.423 + _placeId: null, 1.424 + 1.425 + setup: function() { 1.426 + // Add a place to ensure place_id = 1 is valid 1.427 + this._placeId = addPlace(); 1.428 + // Create a tag 1.429 + this._tagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder); 1.430 + // Insert a bookmark in the tag 1.431 + this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._tagId); 1.432 + // Insert a separator in the tag 1.433 + this._separatorId = addBookmark(null, bs.TYPE_SEPARATOR, this._tagId); 1.434 + // Insert a folder in the tag 1.435 + this._folderId = addBookmark(null, bs.TYPE_FOLDER, this._tagId); 1.436 + }, 1.437 + 1.438 + check: function() { 1.439 + // Check that valid bookmark is still there 1.440 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE type = :type AND parent = :parent"); 1.441 + stmt.params["type"] = bs.TYPE_BOOKMARK; 1.442 + stmt.params["parent"] = this._tagId; 1.443 + do_check_true(stmt.executeStep()); 1.444 + stmt.reset(); 1.445 + // Check that separator is no more there 1.446 + stmt.params["type"] = bs.TYPE_SEPARATOR; 1.447 + stmt.params["parent"] = this._tagId; 1.448 + do_check_false(stmt.executeStep()); 1.449 + stmt.reset(); 1.450 + // Check that folder is no more there 1.451 + stmt.params["type"] = bs.TYPE_FOLDER; 1.452 + stmt.params["parent"] = this._tagId; 1.453 + do_check_false(stmt.executeStep()); 1.454 + stmt.finalize(); 1.455 + } 1.456 +}); 1.457 + 1.458 +//------------------------------------------------------------------------------ 1.459 + 1.460 +tests.push({ 1.461 + name: "D.3", 1.462 + desc: "Remove empty tags", 1.463 + 1.464 + _tagId: null, 1.465 + _bookmarkId: null, 1.466 + _emptyTagId: null, 1.467 + _placeId: null, 1.468 + 1.469 + setup: function() { 1.470 + // Add a place to ensure place_id = 1 is valid 1.471 + this._placeId = addPlace(); 1.472 + // Create a tag 1.473 + this._tagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder); 1.474 + // Insert a bookmark in the tag 1.475 + this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._tagId); 1.476 + // Create another tag (empty) 1.477 + this._emptyTagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder); 1.478 + }, 1.479 + 1.480 + check: function() { 1.481 + // Check that valid bookmark is still there 1.482 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :id AND type = :type AND parent = :parent"); 1.483 + stmt.params["id"] = this._bookmarkId; 1.484 + stmt.params["type"] = bs.TYPE_BOOKMARK; 1.485 + stmt.params["parent"] = this._tagId; 1.486 + do_check_true(stmt.executeStep()); 1.487 + stmt.reset(); 1.488 + stmt.params["id"] = this._tagId; 1.489 + stmt.params["type"] = bs.TYPE_FOLDER; 1.490 + stmt.params["parent"] = bs.tagsFolder; 1.491 + do_check_true(stmt.executeStep()); 1.492 + stmt.reset(); 1.493 + stmt.params["id"] = this._emptyTagId; 1.494 + stmt.params["type"] = bs.TYPE_FOLDER; 1.495 + stmt.params["parent"] = bs.tagsFolder; 1.496 + do_check_false(stmt.executeStep()); 1.497 + stmt.finalize(); 1.498 + } 1.499 +}); 1.500 + 1.501 +//------------------------------------------------------------------------------ 1.502 + 1.503 +tests.push({ 1.504 + name: "D.4", 1.505 + desc: "Move orphan items to unsorted folder", 1.506 + 1.507 + _orphanBookmarkId: null, 1.508 + _orphanSeparatorId: null, 1.509 + _orphanFolderId: null, 1.510 + _bookmarkId: null, 1.511 + _placeId: null, 1.512 + 1.513 + setup: function() { 1.514 + // Add a place to ensure place_id = 1 is valid 1.515 + this._placeId = addPlace(); 1.516 + // Insert an orphan bookmark 1.517 + this._orphanBookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, 8888); 1.518 + // Insert an orphan separator 1.519 + this._orphanSeparatorId = addBookmark(null, bs.TYPE_SEPARATOR, 8888); 1.520 + // Insert a orphan folder 1.521 + this._orphanFolderId = addBookmark(null, bs.TYPE_FOLDER, 8888); 1.522 + // Create a child of the last created folder 1.523 + this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._orphanFolderId); 1.524 + }, 1.525 + 1.526 + check: function() { 1.527 + // Check that bookmarks are now children of a real folder (unsorted) 1.528 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND parent = :parent"); 1.529 + stmt.params["item_id"] = this._orphanBookmarkId; 1.530 + stmt.params["parent"] = bs.unfiledBookmarksFolder; 1.531 + do_check_true(stmt.executeStep()); 1.532 + stmt.reset(); 1.533 + stmt.params["item_id"] = this._orphanSeparatorId; 1.534 + stmt.params["parent"] = bs.unfiledBookmarksFolder; 1.535 + do_check_true(stmt.executeStep()); 1.536 + stmt.reset(); 1.537 + stmt.params["item_id"] = this._orphanFolderId; 1.538 + stmt.params["parent"] = bs.unfiledBookmarksFolder; 1.539 + do_check_true(stmt.executeStep()); 1.540 + stmt.reset(); 1.541 + stmt.params["item_id"] = this._bookmarkId; 1.542 + stmt.params["parent"] = this._orphanFolderId; 1.543 + do_check_true(stmt.executeStep()); 1.544 + stmt.finalize(); 1.545 + } 1.546 +}); 1.547 + 1.548 +//------------------------------------------------------------------------------ 1.549 + 1.550 +tests.push({ 1.551 + name: "D.5", 1.552 + desc: "Fix wrong keywords", 1.553 + 1.554 + _validKeywordItemId: null, 1.555 + _invalidKeywordItemId: null, 1.556 + _validKeywordId: 1, 1.557 + _invalidKeywordId: 8888, 1.558 + _placeId: null, 1.559 + 1.560 + setup: function() { 1.561 + // Insert a keyword 1.562 + let stmt = mDBConn.createStatement("INSERT INTO moz_keywords (id, keyword) VALUES(:id, :keyword)"); 1.563 + stmt.params["id"] = this._validKeywordId; 1.564 + stmt.params["keyword"] = "used"; 1.565 + stmt.execute(); 1.566 + stmt.finalize(); 1.567 + // Add a place to ensure place_id = 1 is valid 1.568 + this._placeId = addPlace(); 1.569 + // Add a bookmark using the keyword 1.570 + this._validKeywordItemId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, bs.unfiledBookmarksFolder, this._validKeywordId); 1.571 + // Add a bookmark using a nonexistent keyword 1.572 + this._invalidKeywordItemId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, bs.unfiledBookmarksFolder, this._invalidKeywordId); 1.573 + }, 1.574 + 1.575 + check: function() { 1.576 + // Check that item with valid keyword is there 1.577 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND keyword_id = :keyword"); 1.578 + stmt.params["item_id"] = this._validKeywordItemId; 1.579 + stmt.params["keyword"] = this._validKeywordId; 1.580 + do_check_true(stmt.executeStep()); 1.581 + stmt.reset(); 1.582 + // Check that item with invalid keyword has been corrected 1.583 + stmt.params["item_id"] = this._invalidKeywordItemId; 1.584 + stmt.params["keyword"] = this._invalidKeywordId; 1.585 + do_check_false(stmt.executeStep()); 1.586 + stmt.finalize(); 1.587 + // Check that item with invalid keyword has not been removed 1.588 + stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id"); 1.589 + stmt.params["item_id"] = this._invalidKeywordItemId; 1.590 + do_check_true(stmt.executeStep()); 1.591 + stmt.finalize(); 1.592 + } 1.593 +}); 1.594 + 1.595 +//------------------------------------------------------------------------------ 1.596 + 1.597 +tests.push({ 1.598 + name: "D.6", 1.599 + desc: "Fix wrong item types | bookmarks", 1.600 + 1.601 + _separatorId: null, 1.602 + _folderId: null, 1.603 + _placeId: null, 1.604 + 1.605 + setup: function() { 1.606 + // Add a place to ensure place_id = 1 is valid 1.607 + this._placeId = addPlace(); 1.608 + // Add a separator with a fk 1.609 + this._separatorId = addBookmark(this._placeId, bs.TYPE_SEPARATOR); 1.610 + // Add a folder with a fk 1.611 + this._folderId = addBookmark(this._placeId, bs.TYPE_FOLDER); 1.612 + }, 1.613 + 1.614 + check: function() { 1.615 + // Check that items with an fk have been converted to bookmarks 1.616 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND type = :type"); 1.617 + stmt.params["item_id"] = this._separatorId; 1.618 + stmt.params["type"] = bs.TYPE_BOOKMARK; 1.619 + do_check_true(stmt.executeStep()); 1.620 + stmt.reset(); 1.621 + stmt.params["item_id"] = this._folderId; 1.622 + stmt.params["type"] = bs.TYPE_BOOKMARK; 1.623 + do_check_true(stmt.executeStep()); 1.624 + stmt.finalize(); 1.625 + } 1.626 +}); 1.627 + 1.628 +//------------------------------------------------------------------------------ 1.629 + 1.630 +tests.push({ 1.631 + name: "D.7", 1.632 + desc: "Fix wrong item types | bookmarks", 1.633 + 1.634 + _validBookmarkId: null, 1.635 + _invalidBookmarkId: null, 1.636 + _placeId: null, 1.637 + 1.638 + setup: function() { 1.639 + // Add a place to ensure place_id = 1 is valid 1.640 + this._placeId = addPlace(); 1.641 + // Add a bookmark with a valid place id 1.642 + this._validBookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK); 1.643 + // Add a bookmark with a null place id 1.644 + this._invalidBookmarkId = addBookmark(null, bs.TYPE_BOOKMARK); 1.645 + }, 1.646 + 1.647 + check: function() { 1.648 + // Check valid bookmark 1.649 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND type = :type"); 1.650 + stmt.params["item_id"] = this._validBookmarkId; 1.651 + stmt.params["type"] = bs.TYPE_BOOKMARK; 1.652 + do_check_true(stmt.executeStep()); 1.653 + stmt.reset(); 1.654 + // Check invalid bookmark has been converted to a folder 1.655 + stmt.params["item_id"] = this._invalidBookmarkId; 1.656 + stmt.params["type"] = bs.TYPE_FOLDER; 1.657 + do_check_true(stmt.executeStep()); 1.658 + stmt.finalize(); 1.659 + } 1.660 +}); 1.661 + 1.662 +//------------------------------------------------------------------------------ 1.663 + 1.664 +tests.push({ 1.665 + name: "D.9", 1.666 + desc: "Fix wrong parents", 1.667 + 1.668 + _bookmarkId: null, 1.669 + _separatorId: null, 1.670 + _bookmarkId1: null, 1.671 + _bookmarkId2: null, 1.672 + _placeId: null, 1.673 + 1.674 + setup: function() { 1.675 + // Add a place to ensure place_id = 1 is valid 1.676 + this._placeId = addPlace(); 1.677 + // Insert a bookmark 1.678 + this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK); 1.679 + // Insert a separator 1.680 + this._separatorId = addBookmark(null, bs.TYPE_SEPARATOR); 1.681 + // Create 3 children of these items 1.682 + this._bookmarkId1 = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._bookmarkId); 1.683 + this._bookmarkId2 = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._separatorId); 1.684 + }, 1.685 + 1.686 + check: function() { 1.687 + // Check that bookmarks are now children of a real folder (unsorted) 1.688 + let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND parent = :parent"); 1.689 + stmt.params["item_id"] = this._bookmarkId1; 1.690 + stmt.params["parent"] = bs.unfiledBookmarksFolder; 1.691 + do_check_true(stmt.executeStep()); 1.692 + stmt.reset(); 1.693 + stmt.params["item_id"] = this._bookmarkId2; 1.694 + stmt.params["parent"] = bs.unfiledBookmarksFolder; 1.695 + do_check_true(stmt.executeStep()); 1.696 + stmt.finalize(); 1.697 + } 1.698 +}); 1.699 + 1.700 +//------------------------------------------------------------------------------ 1.701 + 1.702 +tests.push({ 1.703 + name: "D.10", 1.704 + desc: "Recalculate positions", 1.705 + 1.706 + _unfiledBookmarks: [], 1.707 + _toolbarBookmarks: [], 1.708 + 1.709 + setup: function() { 1.710 + const NUM_BOOKMARKS = 20; 1.711 + bs.runInBatchMode({ 1.712 + runBatched: function (aUserData) { 1.713 + // Add bookmarks to two folders to better perturbate the table. 1.714 + for (let i = 0; i < NUM_BOOKMARKS; i++) { 1.715 + bs.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, 1.716 + NetUtil.newURI("http://example.com/"), 1.717 + bs.DEFAULT_INDEX, "testbookmark"); 1.718 + } 1.719 + for (let i = 0; i < NUM_BOOKMARKS; i++) { 1.720 + bs.insertBookmark(PlacesUtils.toolbarFolderId, 1.721 + NetUtil.newURI("http://example.com/"), 1.722 + bs.DEFAULT_INDEX, "testbookmark"); 1.723 + } 1.724 + } 1.725 + }, null); 1.726 + 1.727 + function randomize_positions(aParent, aResultArray) { 1.728 + let stmt = mDBConn.createStatement( 1.729 + "UPDATE moz_bookmarks SET position = :rand " + 1.730 + "WHERE id IN ( " + 1.731 + "SELECT id FROM moz_bookmarks WHERE parent = :parent " + 1.732 + "ORDER BY RANDOM() LIMIT 1 " + 1.733 + ") " 1.734 + ); 1.735 + for (let i = 0; i < (NUM_BOOKMARKS / 2); i++) { 1.736 + stmt.params["parent"] = aParent; 1.737 + stmt.params["rand"] = Math.round(Math.random() * (NUM_BOOKMARKS - 1)); 1.738 + stmt.execute(); 1.739 + stmt.reset(); 1.740 + } 1.741 + stmt.finalize(); 1.742 + 1.743 + // Build the expected ordered list of bookmarks. 1.744 + stmt = mDBConn.createStatement( 1.745 + "SELECT id, position " + 1.746 + "FROM moz_bookmarks WHERE parent = :parent " + 1.747 + "ORDER BY position ASC, ROWID ASC " 1.748 + ); 1.749 + stmt.params["parent"] = aParent; 1.750 + while (stmt.executeStep()) { 1.751 + aResultArray.push(stmt.row.id); 1.752 + print(stmt.row.id + "\t" + stmt.row.position + "\t" + 1.753 + (aResultArray.length - 1)); 1.754 + } 1.755 + stmt.finalize(); 1.756 + } 1.757 + 1.758 + // Set random positions for the added bookmarks. 1.759 + randomize_positions(PlacesUtils.unfiledBookmarksFolderId, 1.760 + this._unfiledBookmarks); 1.761 + randomize_positions(PlacesUtils.toolbarFolderId, this._toolbarBookmarks); 1.762 + }, 1.763 + 1.764 + check: function() { 1.765 + function check_order(aParent, aResultArray) { 1.766 + // Build the expected ordered list of bookmarks. 1.767 + let stmt = mDBConn.createStatement( 1.768 + "SELECT id, position FROM moz_bookmarks WHERE parent = :parent " + 1.769 + "ORDER BY position ASC" 1.770 + ); 1.771 + stmt.params["parent"] = aParent; 1.772 + let pass = true; 1.773 + while (stmt.executeStep()) { 1.774 + print(stmt.row.id + "\t" + stmt.row.position); 1.775 + if (aResultArray.indexOf(stmt.row.id) != stmt.row.position) { 1.776 + pass = false; 1.777 + } 1.778 + } 1.779 + stmt.finalize(); 1.780 + if (!pass) { 1.781 + dump_table("moz_bookmarks"); 1.782 + do_throw("Unexpected unfiled bookmarks order."); 1.783 + } 1.784 + } 1.785 + 1.786 + check_order(PlacesUtils.unfiledBookmarksFolderId, this._unfiledBookmarks); 1.787 + check_order(PlacesUtils.toolbarFolderId, this._toolbarBookmarks); 1.788 + } 1.789 +}); 1.790 + 1.791 +//------------------------------------------------------------------------------ 1.792 + 1.793 +tests.push({ 1.794 + name: "D.12", 1.795 + desc: "Fix empty-named tags", 1.796 + 1.797 + setup: function() { 1.798 + // Add a place to ensure place_id = 1 is valid 1.799 + let placeId = addPlace(); 1.800 + // Create a empty-named tag. 1.801 + this._untitledTagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder, null, null, ""); 1.802 + // Insert a bookmark in the tag, otherwise it will be removed. 1.803 + addBookmark(placeId, bs.TYPE_BOOKMARK, this._untitledTagId); 1.804 + // Create a empty-named folder. 1.805 + this._untitledFolderId = addBookmark(null, bs.TYPE_FOLDER, bs.toolbarFolder, null, null, ""); 1.806 + // Create a titled tag. 1.807 + this._titledTagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder, null, null, "titledTag"); 1.808 + // Insert a bookmark in the tag, otherwise it will be removed. 1.809 + addBookmark(placeId, bs.TYPE_BOOKMARK, this._titledTagId); 1.810 + // Create a titled folder. 1.811 + this._titledFolderId = addBookmark(null, bs.TYPE_FOLDER, bs.toolbarFolder, null, null, "titledFolder"); 1.812 + }, 1.813 + 1.814 + check: function() { 1.815 + // Check that valid bookmark is still there 1.816 + let stmt = mDBConn.createStatement( 1.817 + "SELECT title FROM moz_bookmarks WHERE id = :id" 1.818 + ); 1.819 + stmt.params["id"] = this._untitledTagId; 1.820 + do_check_true(stmt.executeStep()); 1.821 + do_check_eq(stmt.row.title, "(notitle)"); 1.822 + stmt.reset(); 1.823 + stmt.params["id"] = this._untitledFolderId; 1.824 + do_check_true(stmt.executeStep()); 1.825 + do_check_eq(stmt.row.title, ""); 1.826 + stmt.reset(); 1.827 + stmt.params["id"] = this._titledTagId; 1.828 + do_check_true(stmt.executeStep()); 1.829 + do_check_eq(stmt.row.title, "titledTag"); 1.830 + stmt.reset(); 1.831 + stmt.params["id"] = this._titledFolderId; 1.832 + do_check_true(stmt.executeStep()); 1.833 + do_check_eq(stmt.row.title, "titledFolder"); 1.834 + stmt.finalize(); 1.835 + } 1.836 +}); 1.837 + 1.838 +//------------------------------------------------------------------------------ 1.839 + 1.840 +tests.push({ 1.841 + name: "E.1", 1.842 + desc: "Remove orphan icons", 1.843 + 1.844 + _placeId: null, 1.845 + 1.846 + setup: function() { 1.847 + // Insert favicon entries 1.848 + let stmt = mDBConn.createStatement("INSERT INTO moz_favicons (id, url) VALUES(:favicon_id, :url)"); 1.849 + stmt.params["favicon_id"] = 1; 1.850 + stmt.params["url"] = "http://www1.mozilla.org/favicon.ico"; 1.851 + stmt.execute(); 1.852 + stmt.reset(); 1.853 + stmt.params["favicon_id"] = 2; 1.854 + stmt.params["url"] = "http://www2.mozilla.org/favicon.ico"; 1.855 + stmt.execute(); 1.856 + stmt.finalize(); 1.857 + // Insert a place using the existing favicon entry 1.858 + this._placeId = addPlace("http://www.mozilla.org", 1); 1.859 + }, 1.860 + 1.861 + check: function() { 1.862 + // Check that used icon is still there 1.863 + let stmt = mDBConn.createStatement("SELECT id FROM moz_favicons WHERE id = :favicon_id"); 1.864 + stmt.params["favicon_id"] = 1; 1.865 + do_check_true(stmt.executeStep()); 1.866 + stmt.reset(); 1.867 + // Check that unused icon has been removed 1.868 + stmt.params["favicon_id"] = 2; 1.869 + do_check_false(stmt.executeStep()); 1.870 + stmt.finalize(); 1.871 + } 1.872 +}); 1.873 + 1.874 +//------------------------------------------------------------------------------ 1.875 + 1.876 +tests.push({ 1.877 + name: "F.1", 1.878 + desc: "Remove orphan visits", 1.879 + 1.880 + _placeId: null, 1.881 + _invalidPlaceId: 1337, 1.882 + 1.883 + setup: function() { 1.884 + // Add a place to ensure place_id = 1 is valid 1.885 + this._placeId = addPlace(); 1.886 + // Add a valid visit and an invalid one 1.887 + stmt = mDBConn.createStatement("INSERT INTO moz_historyvisits(place_id) VALUES (:place_id)"); 1.888 + stmt.params["place_id"] = this._placeId; 1.889 + stmt.execute(); 1.890 + stmt.reset(); 1.891 + stmt.params["place_id"] = this._invalidPlaceId; 1.892 + stmt.execute(); 1.893 + stmt.finalize(); 1.894 + }, 1.895 + 1.896 + check: function() { 1.897 + // Check that valid visit is still there 1.898 + let stmt = mDBConn.createStatement("SELECT id FROM moz_historyvisits WHERE place_id = :place_id"); 1.899 + stmt.params["place_id"] = this._placeId; 1.900 + do_check_true(stmt.executeStep()); 1.901 + stmt.reset(); 1.902 + // Check that invalid visit has been removed 1.903 + stmt.params["place_id"] = this._invalidPlaceId; 1.904 + do_check_false(stmt.executeStep()); 1.905 + stmt.finalize(); 1.906 + } 1.907 +}); 1.908 + 1.909 +//------------------------------------------------------------------------------ 1.910 + 1.911 +tests.push({ 1.912 + name: "G.1", 1.913 + desc: "Remove orphan input history", 1.914 + 1.915 + _placeId: null, 1.916 + _invalidPlaceId: 1337, 1.917 + 1.918 + setup: function() { 1.919 + // Add a place to ensure place_id = 1 is valid 1.920 + this._placeId = addPlace(); 1.921 + // Add input history entries 1.922 + let stmt = mDBConn.createStatement("INSERT INTO moz_inputhistory (place_id, input) VALUES (:place_id, :input)"); 1.923 + stmt.params["place_id"] = this._placeId; 1.924 + stmt.params["input"] = "moz"; 1.925 + stmt.execute(); 1.926 + stmt.reset(); 1.927 + stmt.params["place_id"] = this._invalidPlaceId; 1.928 + stmt.params["input"] = "moz"; 1.929 + stmt.execute(); 1.930 + stmt.finalize(); 1.931 + }, 1.932 + 1.933 + check: function() { 1.934 + // Check that inputhistory on valid place is still there 1.935 + let stmt = mDBConn.createStatement("SELECT place_id FROM moz_inputhistory WHERE place_id = :place_id"); 1.936 + stmt.params["place_id"] = this._placeId; 1.937 + do_check_true(stmt.executeStep()); 1.938 + stmt.reset(); 1.939 + // Check that inputhistory on invalid place has gone 1.940 + stmt.params["place_id"] = this._invalidPlaceId; 1.941 + do_check_false(stmt.executeStep()); 1.942 + stmt.finalize(); 1.943 + } 1.944 +}); 1.945 + 1.946 +//------------------------------------------------------------------------------ 1.947 + 1.948 +tests.push({ 1.949 + name: "H.1", 1.950 + desc: "Remove item annos with an invalid attribute", 1.951 + 1.952 + _usedItemAttribute: "usedItem", 1.953 + _bookmarkId: null, 1.954 + _placeId: null, 1.955 + 1.956 + setup: function() { 1.957 + // Add a place to ensure place_id = 1 is valid 1.958 + this._placeId = addPlace(); 1.959 + // Insert a bookmark 1.960 + this._bookmarkId = addBookmark(this._placeId); 1.961 + // Add a used attribute. 1.962 + let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)"); 1.963 + stmt.params['anno'] = this._usedItemAttribute; 1.964 + stmt.execute(); 1.965 + stmt.finalize(); 1.966 + stmt = mDBConn.createStatement("INSERT INTO moz_items_annos (item_id, anno_attribute_id) VALUES(:item_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))"); 1.967 + stmt.params['item_id'] = this._bookmarkId; 1.968 + stmt.params['anno'] = this._usedItemAttribute; 1.969 + stmt.execute(); 1.970 + stmt.finalize(); 1.971 + // Add an annotation with a nonexistent attribute 1.972 + stmt = mDBConn.createStatement("INSERT INTO moz_items_annos (item_id, anno_attribute_id) VALUES(:item_id, 1337)"); 1.973 + stmt.params['item_id'] = this._bookmarkId; 1.974 + stmt.execute(); 1.975 + stmt.finalize(); 1.976 + }, 1.977 + 1.978 + check: function() { 1.979 + // Check that used attribute is still there 1.980 + let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno"); 1.981 + stmt.params['anno'] = this._usedItemAttribute; 1.982 + do_check_true(stmt.executeStep()); 1.983 + stmt.finalize(); 1.984 + // check that annotation with valid attribute is still there 1.985 + stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)"); 1.986 + stmt.params['anno'] = this._usedItemAttribute; 1.987 + do_check_true(stmt.executeStep()); 1.988 + stmt.finalize(); 1.989 + // Check that annotation with bogus attribute has been removed 1.990 + stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE anno_attribute_id = 1337"); 1.991 + do_check_false(stmt.executeStep()); 1.992 + stmt.finalize(); 1.993 + } 1.994 +}); 1.995 + 1.996 +//------------------------------------------------------------------------------ 1.997 + 1.998 +tests.push({ 1.999 + name: "H.2", 1.1000 + desc: "Remove orphan item annotations", 1.1001 + 1.1002 + _usedItemAttribute: "usedItem", 1.1003 + _bookmarkId: null, 1.1004 + _invalidBookmarkId: 8888, 1.1005 + _placeId: null, 1.1006 + 1.1007 + setup: function() { 1.1008 + // Add a place to ensure place_id = 1 is valid 1.1009 + this._placeId = addPlace(); 1.1010 + // Insert a bookmark 1.1011 + this._bookmarkId = addBookmark(this._placeId); 1.1012 + // Add a used attribute. 1.1013 + stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)"); 1.1014 + stmt.params['anno'] = this._usedItemAttribute; 1.1015 + stmt.execute(); 1.1016 + stmt.finalize(); 1.1017 + stmt = mDBConn.createStatement("INSERT INTO moz_items_annos (item_id, anno_attribute_id) VALUES (:item_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))"); 1.1018 + stmt.params["item_id"] = this._bookmarkId; 1.1019 + stmt.params["anno"] = this._usedItemAttribute; 1.1020 + stmt.execute(); 1.1021 + stmt.reset(); 1.1022 + // Add an annotation to a nonexistent item 1.1023 + stmt.params["item_id"] = this._invalidBookmarkId; 1.1024 + stmt.params["anno"] = this._usedItemAttribute; 1.1025 + stmt.execute(); 1.1026 + stmt.finalize(); 1.1027 + }, 1.1028 + 1.1029 + check: function() { 1.1030 + // Check that used attribute is still there 1.1031 + let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno"); 1.1032 + stmt.params['anno'] = this._usedItemAttribute; 1.1033 + do_check_true(stmt.executeStep()); 1.1034 + stmt.finalize(); 1.1035 + // check that annotation with valid attribute is still there 1.1036 + stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)"); 1.1037 + stmt.params['anno'] = this._usedItemAttribute; 1.1038 + do_check_true(stmt.executeStep()); 1.1039 + stmt.finalize(); 1.1040 + // Check that an annotation to a nonexistent page has been removed 1.1041 + stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE item_id = 8888"); 1.1042 + do_check_false(stmt.executeStep()); 1.1043 + stmt.finalize(); 1.1044 + } 1.1045 +}); 1.1046 + 1.1047 + 1.1048 +//------------------------------------------------------------------------------ 1.1049 + 1.1050 +tests.push({ 1.1051 + name: "I.1", 1.1052 + desc: "Remove unused keywords", 1.1053 + 1.1054 + _bookmarkId: null, 1.1055 + _placeId: null, 1.1056 + 1.1057 + setup: function() { 1.1058 + // Insert 2 keywords 1.1059 + let stmt = mDBConn.createStatement("INSERT INTO moz_keywords (id, keyword) VALUES(:id, :keyword)"); 1.1060 + stmt.params["id"] = 1; 1.1061 + stmt.params["keyword"] = "used"; 1.1062 + stmt.execute(); 1.1063 + stmt.reset(); 1.1064 + stmt.params["id"] = 2; 1.1065 + stmt.params["keyword"] = "unused"; 1.1066 + stmt.execute(); 1.1067 + stmt.finalize(); 1.1068 + // Add a place to ensure place_id = 1 is valid 1.1069 + this._placeId = addPlace(); 1.1070 + // Insert a bookmark using the "used" keyword 1.1071 + this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, bs.unfiledBookmarksFolder, 1); 1.1072 + }, 1.1073 + 1.1074 + check: function() { 1.1075 + // Check that "used" keyword is still there 1.1076 + let stmt = mDBConn.createStatement("SELECT id FROM moz_keywords WHERE keyword = :keyword"); 1.1077 + stmt.params["keyword"] = "used"; 1.1078 + do_check_true(stmt.executeStep()); 1.1079 + stmt.reset(); 1.1080 + // Check that "unused" keyword has gone 1.1081 + stmt.params["keyword"] = "unused"; 1.1082 + do_check_false(stmt.executeStep()); 1.1083 + stmt.finalize(); 1.1084 + } 1.1085 +}); 1.1086 + 1.1087 + 1.1088 +//------------------------------------------------------------------------------ 1.1089 + 1.1090 +tests.push({ 1.1091 + name: "L.1", 1.1092 + desc: "Fix wrong favicon ids", 1.1093 + 1.1094 + _validIconPlaceId: null, 1.1095 + _invalidIconPlaceId: null, 1.1096 + 1.1097 + setup: function() { 1.1098 + // Insert a favicon entry 1.1099 + let stmt = mDBConn.createStatement("INSERT INTO moz_favicons (id, url) VALUES(1, :url)"); 1.1100 + stmt.params["url"] = "http://www.mozilla.org/favicon.ico"; 1.1101 + stmt.execute(); 1.1102 + stmt.finalize(); 1.1103 + // Insert a place using the existing favicon entry 1.1104 + this._validIconPlaceId = addPlace("http://www1.mozilla.org", 1); 1.1105 + 1.1106 + // Insert a place using a nonexistent favicon entry 1.1107 + this._invalidIconPlaceId = addPlace("http://www2.mozilla.org", 1337); 1.1108 + }, 1.1109 + 1.1110 + check: function() { 1.1111 + // Check that bogus favicon is not there 1.1112 + let stmt = mDBConn.createStatement("SELECT id FROM moz_places WHERE favicon_id = :favicon_id"); 1.1113 + stmt.params["favicon_id"] = 1337; 1.1114 + do_check_false(stmt.executeStep()); 1.1115 + stmt.reset(); 1.1116 + // Check that valid favicon is still there 1.1117 + stmt.params["favicon_id"] = 1; 1.1118 + do_check_true(stmt.executeStep()); 1.1119 + stmt.finalize(); 1.1120 + // Check that place entries are there 1.1121 + stmt = mDBConn.createStatement("SELECT id FROM moz_places WHERE id = :place_id"); 1.1122 + stmt.params["place_id"] = this._validIconPlaceId; 1.1123 + do_check_true(stmt.executeStep()); 1.1124 + stmt.reset(); 1.1125 + stmt.params["place_id"] = this._invalidIconPlaceId; 1.1126 + do_check_true(stmt.executeStep()); 1.1127 + stmt.finalize(); 1.1128 + } 1.1129 +}); 1.1130 + 1.1131 +//------------------------------------------------------------------------------ 1.1132 + 1.1133 +tests.push({ 1.1134 + name: "L.2", 1.1135 + desc: "Recalculate visit_count and last_visit_date", 1.1136 + 1.1137 + setup: function() { 1.1138 + function setVisitCount(aURL, aValue) { 1.1139 + let stmt = mDBConn.createStatement( 1.1140 + "UPDATE moz_places SET visit_count = :count WHERE url = :url" 1.1141 + ); 1.1142 + stmt.params.count = aValue; 1.1143 + stmt.params.url = aURL; 1.1144 + stmt.execute(); 1.1145 + stmt.finalize(); 1.1146 + } 1.1147 + function setLastVisitDate(aURL, aValue) { 1.1148 + let stmt = mDBConn.createStatement( 1.1149 + "UPDATE moz_places SET last_visit_date = :date WHERE url = :url" 1.1150 + ); 1.1151 + stmt.params.date = aValue; 1.1152 + stmt.params.url = aURL; 1.1153 + stmt.execute(); 1.1154 + stmt.finalize(); 1.1155 + } 1.1156 + 1.1157 + let now = Date.now() * 1000; 1.1158 + // Add a page with 1 visit. 1.1159 + let url = "http://1.moz.org/"; 1.1160 + yield promiseAddVisits({ uri: uri(url), visitDate: now++ }); 1.1161 + // Add a page with 1 visit and set wrong visit_count. 1.1162 + url = "http://2.moz.org/"; 1.1163 + yield promiseAddVisits({ uri: uri(url), visitDate: now++ }); 1.1164 + setVisitCount(url, 10); 1.1165 + // Add a page with 1 visit and set wrong last_visit_date. 1.1166 + url = "http://3.moz.org/"; 1.1167 + yield promiseAddVisits({ uri: uri(url), visitDate: now++ }); 1.1168 + setLastVisitDate(url, now++); 1.1169 + // Add a page with 1 visit and set wrong stats. 1.1170 + url = "http://4.moz.org/"; 1.1171 + yield promiseAddVisits({ uri: uri(url), visitDate: now++ }); 1.1172 + setVisitCount(url, 10); 1.1173 + setLastVisitDate(url, now++); 1.1174 + 1.1175 + // Add a page without visits. 1.1176 + let url = "http://5.moz.org/"; 1.1177 + addPlace(url); 1.1178 + // Add a page without visits and set wrong visit_count. 1.1179 + url = "http://6.moz.org/"; 1.1180 + addPlace(url); 1.1181 + setVisitCount(url, 10); 1.1182 + // Add a page without visits and set wrong last_visit_date. 1.1183 + url = "http://7.moz.org/"; 1.1184 + addPlace(url); 1.1185 + setLastVisitDate(url, now++); 1.1186 + // Add a page without visits and set wrong stats. 1.1187 + url = "http://8.moz.org/"; 1.1188 + addPlace(url); 1.1189 + setVisitCount(url, 10); 1.1190 + setLastVisitDate(url, now++); 1.1191 + }, 1.1192 + 1.1193 + check: function() { 1.1194 + let stmt = mDBConn.createStatement( 1.1195 + "SELECT h.id FROM moz_places h " + 1.1196 + "JOIN moz_historyvisits v ON v.place_id = h.id AND visit_type NOT IN (0,4,7,8) " + 1.1197 + "GROUP BY h.id HAVING h.visit_count <> count(*) " + 1.1198 + "UNION ALL " + 1.1199 + "SELECT h.id FROM moz_places h " + 1.1200 + "JOIN moz_historyvisits v ON v.place_id = h.id " + 1.1201 + "GROUP BY h.id HAVING h.last_visit_date <> MAX(v.visit_date) " 1.1202 + ); 1.1203 + do_check_false(stmt.executeStep()); 1.1204 + stmt.finalize(); 1.1205 + } 1.1206 +}); 1.1207 + 1.1208 +//------------------------------------------------------------------------------ 1.1209 + 1.1210 +tests.push({ 1.1211 + name: "L.3", 1.1212 + desc: "recalculate hidden for redirects.", 1.1213 + 1.1214 + setup: function() { 1.1215 + promiseAddVisits([ 1.1216 + { uri: NetUtil.newURI("http://l3.moz.org/"), 1.1217 + transition: TRANSITION_TYPED }, 1.1218 + { uri: NetUtil.newURI("http://l3.moz.org/redirecting/"), 1.1219 + transition: TRANSITION_TYPED }, 1.1220 + { uri: NetUtil.newURI("http://l3.moz.org/redirecting2/"), 1.1221 + transition: TRANSITION_REDIRECT_TEMPORARY, 1.1222 + referrer: NetUtil.newURI("http://l3.moz.org/redirecting/") }, 1.1223 + { uri: NetUtil.newURI("http://l3.moz.org/target/"), 1.1224 + transition: TRANSITION_REDIRECT_PERMANENT, 1.1225 + referrer: NetUtil.newURI("http://l3.moz.org/redirecting2/") }, 1.1226 + ]); 1.1227 + }, 1.1228 + 1.1229 + asyncCheck: function(aCallback) { 1.1230 + let stmt = mDBConn.createAsyncStatement( 1.1231 + "SELECT h.url FROM moz_places h WHERE h.hidden = 1" 1.1232 + ); 1.1233 + stmt.executeAsync({ 1.1234 + _count: 0, 1.1235 + handleResult: function(aResultSet) { 1.1236 + for (let row; (row = aResultSet.getNextRow());) { 1.1237 + let url = row.getResultByIndex(0); 1.1238 + do_check_true(/redirecting/.test(url)); 1.1239 + this._count++; 1.1240 + } 1.1241 + }, 1.1242 + handleError: function(aError) { 1.1243 + }, 1.1244 + handleCompletion: function(aReason) { 1.1245 + dump_table("moz_places"); 1.1246 + dump_table("moz_historyvisits"); 1.1247 + do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED); 1.1248 + do_check_eq(this._count, 2); 1.1249 + aCallback(); 1.1250 + } 1.1251 + }); 1.1252 + stmt.finalize(); 1.1253 + } 1.1254 +}); 1.1255 + 1.1256 +//------------------------------------------------------------------------------ 1.1257 + 1.1258 +tests.push({ 1.1259 + name: "Z", 1.1260 + desc: "Sanity: Preventive maintenance does not touch valid items", 1.1261 + 1.1262 + _uri1: uri("http://www1.mozilla.org"), 1.1263 + _uri2: uri("http://www2.mozilla.org"), 1.1264 + _folderId: null, 1.1265 + _bookmarkId: null, 1.1266 + _separatorId: null, 1.1267 + 1.1268 + setup: function() { 1.1269 + // use valid api calls to create a bunch of items 1.1270 + yield promiseAddVisits([ 1.1271 + { uri: this._uri1 }, 1.1272 + { uri: this._uri2 }, 1.1273 + ]); 1.1274 + 1.1275 + this._folderId = bs.createFolder(bs.toolbarFolder, "testfolder", 1.1276 + bs.DEFAULT_INDEX); 1.1277 + do_check_true(this._folderId > 0); 1.1278 + this._bookmarkId = bs.insertBookmark(this._folderId, this._uri1, 1.1279 + bs.DEFAULT_INDEX, "testbookmark"); 1.1280 + do_check_true(this._bookmarkId > 0); 1.1281 + this._separatorId = bs.insertSeparator(bs.unfiledBookmarksFolder, 1.1282 + bs.DEFAULT_INDEX); 1.1283 + do_check_true(this._separatorId > 0); 1.1284 + ts.tagURI(this._uri1, ["testtag"]); 1.1285 + fs.setAndFetchFaviconForPage(this._uri2, SMALLPNG_DATA_URI, false, 1.1286 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE); 1.1287 + bs.setKeywordForBookmark(this._bookmarkId, "testkeyword"); 1.1288 + as.setPageAnnotation(this._uri2, "anno", "anno", 0, as.EXPIRE_NEVER); 1.1289 + as.setItemAnnotation(this._bookmarkId, "anno", "anno", 0, as.EXPIRE_NEVER); 1.1290 + }, 1.1291 + 1.1292 + asyncCheck: function (aCallback) { 1.1293 + // Check that all items are correct 1.1294 + PlacesUtils.asyncHistory.isURIVisited(this._uri1, function(aURI, aIsVisited) { 1.1295 + do_check_true(aIsVisited); 1.1296 + PlacesUtils.asyncHistory.isURIVisited(this._uri2, function(aURI, aIsVisited) { 1.1297 + do_check_true(aIsVisited); 1.1298 + 1.1299 + do_check_eq(bs.getBookmarkURI(this._bookmarkId).spec, this._uri1.spec); 1.1300 + do_check_eq(bs.getItemIndex(this._folderId), 0); 1.1301 + 1.1302 + do_check_eq(bs.getItemType(this._folderId), bs.TYPE_FOLDER); 1.1303 + do_check_eq(bs.getItemType(this._separatorId), bs.TYPE_SEPARATOR); 1.1304 + 1.1305 + do_check_eq(ts.getTagsForURI(this._uri1).length, 1); 1.1306 + do_check_eq(bs.getKeywordForBookmark(this._bookmarkId), "testkeyword"); 1.1307 + do_check_eq(as.getPageAnnotation(this._uri2, "anno"), "anno"); 1.1308 + do_check_eq(as.getItemAnnotation(this._bookmarkId, "anno"), "anno"); 1.1309 + 1.1310 + fs.getFaviconURLForPage(this._uri2, function (aFaviconURI) { 1.1311 + do_check_true(aFaviconURI.equals(SMALLPNG_DATA_URI)); 1.1312 + aCallback(); 1.1313 + }); 1.1314 + }.bind(this)); 1.1315 + }.bind(this)); 1.1316 + } 1.1317 +}); 1.1318 + 1.1319 +//------------------------------------------------------------------------------ 1.1320 + 1.1321 +// main 1.1322 +function run_test() 1.1323 +{ 1.1324 + run_next_test(); 1.1325 +} 1.1326 + 1.1327 +add_task(function test_preventive_maintenance() 1.1328 +{ 1.1329 + // Force initialization of the bookmarks hash. This test could cause 1.1330 + // it to go out of sync due to direct queries on the database. 1.1331 + yield promiseAddVisits(uri("http://force.bookmarks.hash")); 1.1332 + do_check_false(bs.isBookmarked(uri("http://force.bookmarks.hash"))); 1.1333 + 1.1334 + // Get current bookmarks max ID for cleanup 1.1335 + let stmt = mDBConn.createStatement("SELECT MAX(id) FROM moz_bookmarks"); 1.1336 + stmt.executeStep(); 1.1337 + defaultBookmarksMaxId = stmt.getInt32(0); 1.1338 + stmt.finalize(); 1.1339 + do_check_true(defaultBookmarksMaxId > 0); 1.1340 + 1.1341 + for ([, test] in Iterator(tests)) { 1.1342 + dump("\nExecuting test: " + test.name + "\n" + "*** " + test.desc + "\n"); 1.1343 + yield test.setup(); 1.1344 + 1.1345 + let promiseMaintenanceFinished = 1.1346 + promiseTopicObserved(FINISHED_MAINTENANCE_NOTIFICATION_TOPIC); 1.1347 + PlacesDBUtils.maintenanceOnIdle(); 1.1348 + yield promiseMaintenanceFinished; 1.1349 + 1.1350 + // Check the lastMaintenance time has been saved. 1.1351 + do_check_neq(Services.prefs.getIntPref("places.database.lastMaintenance"), null); 1.1352 + 1.1353 + if (test.asyncCheck) { 1.1354 + let deferred = Promise.defer(); 1.1355 + test.asyncCheck(deferred.resolve); 1.1356 + yield deferred.promise; 1.1357 + } else { 1.1358 + test.check(); 1.1359 + } 1.1360 + 1.1361 + cleanDatabase(); 1.1362 + } 1.1363 + 1.1364 + // Sanity check: all roots should be intact 1.1365 + do_check_eq(bs.getFolderIdForItem(bs.placesRoot), 0); 1.1366 + do_check_eq(bs.getFolderIdForItem(bs.bookmarksMenuFolder), bs.placesRoot); 1.1367 + do_check_eq(bs.getFolderIdForItem(bs.tagsFolder), bs.placesRoot); 1.1368 + do_check_eq(bs.getFolderIdForItem(bs.unfiledBookmarksFolder), bs.placesRoot); 1.1369 + do_check_eq(bs.getFolderIdForItem(bs.toolbarFolder), bs.placesRoot); 1.1370 +});