summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/unit/test_preventive_maintenance.js
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /toolkit/components/places/tests/unit/test_preventive_maintenance.js
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/components/places/tests/unit/test_preventive_maintenance.js')
-rw-r--r--toolkit/components/places/tests/unit/test_preventive_maintenance.js1356
1 files changed, 1356 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/unit/test_preventive_maintenance.js b/toolkit/components/places/tests/unit/test_preventive_maintenance.js
new file mode 100644
index 000000000..a533c8295
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_preventive_maintenance.js
@@ -0,0 +1,1356 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ /**
+ * Test preventive maintenance
+ * For every maintenance query create an uncoherent db and check that we take
+ * correct fix steps, without polluting valid data.
+ */
+
+// Include PlacesDBUtils module
+Components.utils.import("resource://gre/modules/PlacesDBUtils.jsm");
+
+const FINISHED_MAINTENANCE_NOTIFICATION_TOPIC = "places-maintenance-finished";
+
+// Get services and database connection
+var hs = PlacesUtils.history;
+var bs = PlacesUtils.bookmarks;
+var ts = PlacesUtils.tagging;
+var as = PlacesUtils.annotations;
+var fs = PlacesUtils.favicons;
+
+var mDBConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
+
+// ------------------------------------------------------------------------------
+// Helpers
+
+var defaultBookmarksMaxId = 0;
+function cleanDatabase() {
+ mDBConn.executeSimpleSQL("DELETE FROM moz_places");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_historyvisits");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_anno_attributes");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_annos");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_items_annos");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_inputhistory");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_keywords");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_favicons");
+ mDBConn.executeSimpleSQL("DELETE FROM moz_bookmarks WHERE id > " + defaultBookmarksMaxId);
+}
+
+function addPlace(aUrl, aFavicon) {
+ let stmt = mDBConn.createStatement(
+ "INSERT INTO moz_places (url, url_hash, favicon_id) VALUES (:url, hash(:url), :favicon)");
+ stmt.params["url"] = aUrl || "http://www.mozilla.org";
+ stmt.params["favicon"] = aFavicon || null;
+ stmt.execute();
+ stmt.finalize();
+ return mDBConn.lastInsertRowID;
+}
+
+function addBookmark(aPlaceId, aType, aParent, aKeywordId, aFolderType, aTitle) {
+ let stmt = mDBConn.createStatement(
+ `INSERT INTO moz_bookmarks (fk, type, parent, keyword_id, folder_type,
+ title, guid)
+ VALUES (:place_id, :type, :parent, :keyword_id, :folder_type, :title,
+ GENERATE_GUID())`);
+ stmt.params["place_id"] = aPlaceId || null;
+ stmt.params["type"] = aType || bs.TYPE_BOOKMARK;
+ stmt.params["parent"] = aParent || bs.unfiledBookmarksFolder;
+ stmt.params["keyword_id"] = aKeywordId || null;
+ stmt.params["folder_type"] = aFolderType || null;
+ stmt.params["title"] = typeof(aTitle) == "string" ? aTitle : null;
+ stmt.execute();
+ stmt.finalize();
+ return mDBConn.lastInsertRowID;
+}
+
+// ------------------------------------------------------------------------------
+// Tests
+
+var tests = [];
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "A.1",
+ desc: "Remove obsolete annotations from moz_annos",
+
+ _obsoleteWeaveAttribute: "weave/test",
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid.
+ this._placeId = addPlace();
+ // Add an obsolete attribute.
+ let stmt = mDBConn.createStatement(
+ "INSERT INTO moz_anno_attributes (name) VALUES (:anno)"
+ );
+ stmt.params['anno'] = this._obsoleteWeaveAttribute;
+ stmt.execute();
+ stmt.finalize();
+ stmt = mDBConn.createStatement(
+ `INSERT INTO moz_annos (place_id, anno_attribute_id)
+ VALUES (:place_id,
+ (SELECT id FROM moz_anno_attributes WHERE name = :anno)
+ )`
+ );
+ stmt.params['place_id'] = this._placeId;
+ stmt.params['anno'] = this._obsoleteWeaveAttribute;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that the obsolete annotation has been removed.
+ let stmt = mDBConn.createStatement(
+ "SELECT id FROM moz_anno_attributes WHERE name = :anno"
+ );
+ stmt.params['anno'] = this._obsoleteWeaveAttribute;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+tests.push({
+ name: "A.2",
+ desc: "Remove obsolete annotations from moz_items_annos",
+
+ _obsoleteSyncAttribute: "sync/children",
+ _obsoleteGuidAttribute: "placesInternal/GUID",
+ _obsoleteWeaveAttribute: "weave/test",
+ _placeId: null,
+ _bookmarkId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid.
+ this._placeId = addPlace();
+ // Add a bookmark.
+ this._bookmarkId = addBookmark(this._placeId);
+ // Add an obsolete attribute.
+ let stmt = mDBConn.createStatement(
+ `INSERT INTO moz_anno_attributes (name)
+ VALUES (:anno1), (:anno2), (:anno3)`
+ );
+ stmt.params['anno1'] = this._obsoleteSyncAttribute;
+ stmt.params['anno2'] = this._obsoleteGuidAttribute;
+ stmt.params['anno3'] = this._obsoleteWeaveAttribute;
+ stmt.execute();
+ stmt.finalize();
+ stmt = mDBConn.createStatement(
+ `INSERT INTO moz_items_annos (item_id, anno_attribute_id)
+ SELECT :item_id, id
+ FROM moz_anno_attributes
+ WHERE name IN (:anno1, :anno2, :anno3)`
+ );
+ stmt.params['item_id'] = this._bookmarkId;
+ stmt.params['anno1'] = this._obsoleteSyncAttribute;
+ stmt.params['anno2'] = this._obsoleteGuidAttribute;
+ stmt.params['anno3'] = this._obsoleteWeaveAttribute;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that the obsolete annotations have been removed.
+ let stmt = mDBConn.createStatement(
+ `SELECT id FROM moz_anno_attributes
+ WHERE name IN (:anno1, :anno2, :anno3)`
+ );
+ stmt.params['anno1'] = this._obsoleteSyncAttribute;
+ stmt.params['anno2'] = this._obsoleteGuidAttribute;
+ stmt.params['anno3'] = this._obsoleteWeaveAttribute;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+tests.push({
+ name: "A.3",
+ desc: "Remove unused attributes",
+
+ _usedPageAttribute: "usedPage",
+ _usedItemAttribute: "usedItem",
+ _unusedAttribute: "unused",
+ _placeId: null,
+ _bookmarkId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // add a bookmark
+ this._bookmarkId = addBookmark(this._placeId);
+ // Add a used attribute and an unused one.
+ let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)");
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.reset();
+ stmt.params['anno'] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.reset();
+ stmt.params['anno'] = this._unusedAttribute;
+ stmt.execute();
+ stmt.finalize();
+
+ stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))");
+ stmt.params['place_id'] = this._placeId;
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.finalize();
+ 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))");
+ stmt.params['item_id'] = this._bookmarkId;
+ stmt.params['anno'] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that used attributes are still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno");
+ stmt.params['anno'] = this._usedPageAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params['anno'] = this._usedItemAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check that unused attribute has been removed
+ stmt.params['anno'] = this._unusedAttribute;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "B.1",
+ desc: "Remove annotations with an invalid attribute",
+
+ _usedPageAttribute: "usedPage",
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Add a used attribute.
+ let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)");
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.finalize();
+ stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))");
+ stmt.params['place_id'] = this._placeId;
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.finalize();
+ // Add an annotation with a nonexistent attribute
+ stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, 1337)");
+ stmt.params['place_id'] = this._placeId;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that used attribute is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno");
+ stmt.params['anno'] = this._usedPageAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // check that annotation with valid attribute is still there
+ stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)");
+ stmt.params['anno'] = this._usedPageAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // Check that annotation with bogus attribute has been removed
+ stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE anno_attribute_id = 1337");
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "B.2",
+ desc: "Remove orphan page annotations",
+
+ _usedPageAttribute: "usedPage",
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Add a used attribute.
+ let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)");
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.finalize();
+ stmt = mDBConn.createStatement("INSERT INTO moz_annos (place_id, anno_attribute_id) VALUES(:place_id, (SELECT id FROM moz_anno_attributes WHERE name = :anno))");
+ stmt.params['place_id'] = this._placeId;
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.reset();
+ // Add an annotation to a nonexistent page
+ stmt.params['place_id'] = 1337;
+ stmt.params['anno'] = this._usedPageAttribute;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that used attribute is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno");
+ stmt.params['anno'] = this._usedPageAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // check that annotation with valid attribute is still there
+ stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)");
+ stmt.params['anno'] = this._usedPageAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // Check that an annotation to a nonexistent page has been removed
+ stmt = mDBConn.createStatement("SELECT id FROM moz_annos WHERE place_id = 1337");
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+tests.push({
+ name: "C.1",
+ desc: "fix missing Places root",
+
+ setup: function() {
+ // Sanity check: ensure that roots are intact.
+ do_check_eq(bs.getFolderIdForItem(bs.placesRoot), 0);
+ do_check_eq(bs.getFolderIdForItem(bs.bookmarksMenuFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.tagsFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.unfiledBookmarksFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.toolbarFolder), bs.placesRoot);
+
+ // Remove the root.
+ mDBConn.executeSimpleSQL("DELETE FROM moz_bookmarks WHERE parent = 0");
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE parent = 0");
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Ensure the roots have been correctly restored.
+ do_check_eq(bs.getFolderIdForItem(bs.placesRoot), 0);
+ do_check_eq(bs.getFolderIdForItem(bs.bookmarksMenuFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.tagsFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.unfiledBookmarksFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.toolbarFolder), bs.placesRoot);
+ }
+});
+
+// ------------------------------------------------------------------------------
+tests.push({
+ name: "C.2",
+ desc: "Fix roots titles",
+
+ setup: function() {
+ // Sanity check: ensure that roots titles are correct. We can use our check.
+ this.check();
+ // Change some roots' titles.
+ bs.setItemTitle(bs.placesRoot, "bad title");
+ do_check_eq(bs.getItemTitle(bs.placesRoot), "bad title");
+ bs.setItemTitle(bs.unfiledBookmarksFolder, "bad title");
+ do_check_eq(bs.getItemTitle(bs.unfiledBookmarksFolder), "bad title");
+ },
+
+ check: function() {
+ // Ensure all roots titles are correct.
+ do_check_eq(bs.getItemTitle(bs.placesRoot), "");
+ do_check_eq(bs.getItemTitle(bs.bookmarksMenuFolder),
+ PlacesUtils.getString("BookmarksMenuFolderTitle"));
+ do_check_eq(bs.getItemTitle(bs.tagsFolder),
+ PlacesUtils.getString("TagsFolderTitle"));
+ do_check_eq(bs.getItemTitle(bs.unfiledBookmarksFolder),
+ PlacesUtils.getString("OtherBookmarksFolderTitle"));
+ do_check_eq(bs.getItemTitle(bs.toolbarFolder),
+ PlacesUtils.getString("BookmarksToolbarFolderTitle"));
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.1",
+ desc: "Remove items without a valid place",
+
+ _validItemId: null,
+ _invalidItemId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this.placeId = addPlace();
+ // Insert a valid bookmark
+ this._validItemId = addBookmark(this.placeId);
+ // Insert a bookmark with an invalid place
+ this._invalidItemId = addBookmark(1337);
+ },
+
+ check: function() {
+ // Check that valid bookmark is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id");
+ stmt.params["item_id"] = this._validItemId;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check that invalid bookmark has been removed
+ stmt.params["item_id"] = this._invalidItemId;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.2",
+ desc: "Remove items that are not uri bookmarks from tag containers",
+
+ _tagId: null,
+ _bookmarkId: null,
+ _separatorId: null,
+ _folderId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Create a tag
+ this._tagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder);
+ // Insert a bookmark in the tag
+ this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._tagId);
+ // Insert a separator in the tag
+ this._separatorId = addBookmark(null, bs.TYPE_SEPARATOR, this._tagId);
+ // Insert a folder in the tag
+ this._folderId = addBookmark(null, bs.TYPE_FOLDER, this._tagId);
+ },
+
+ check: function() {
+ // Check that valid bookmark is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE type = :type AND parent = :parent");
+ stmt.params["type"] = bs.TYPE_BOOKMARK;
+ stmt.params["parent"] = this._tagId;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check that separator is no more there
+ stmt.params["type"] = bs.TYPE_SEPARATOR;
+ stmt.params["parent"] = this._tagId;
+ do_check_false(stmt.executeStep());
+ stmt.reset();
+ // Check that folder is no more there
+ stmt.params["type"] = bs.TYPE_FOLDER;
+ stmt.params["parent"] = this._tagId;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.3",
+ desc: "Remove empty tags",
+
+ _tagId: null,
+ _bookmarkId: null,
+ _emptyTagId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Create a tag
+ this._tagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder);
+ // Insert a bookmark in the tag
+ this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._tagId);
+ // Create another tag (empty)
+ this._emptyTagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder);
+ },
+
+ check: function() {
+ // Check that valid bookmark is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :id AND type = :type AND parent = :parent");
+ stmt.params["id"] = this._bookmarkId;
+ stmt.params["type"] = bs.TYPE_BOOKMARK;
+ stmt.params["parent"] = this._tagId;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["id"] = this._tagId;
+ stmt.params["type"] = bs.TYPE_FOLDER;
+ stmt.params["parent"] = bs.tagsFolder;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["id"] = this._emptyTagId;
+ stmt.params["type"] = bs.TYPE_FOLDER;
+ stmt.params["parent"] = bs.tagsFolder;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.4",
+ desc: "Move orphan items to unsorted folder",
+
+ _orphanBookmarkId: null,
+ _orphanSeparatorId: null,
+ _orphanFolderId: null,
+ _bookmarkId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Insert an orphan bookmark
+ this._orphanBookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, 8888);
+ // Insert an orphan separator
+ this._orphanSeparatorId = addBookmark(null, bs.TYPE_SEPARATOR, 8888);
+ // Insert a orphan folder
+ this._orphanFolderId = addBookmark(null, bs.TYPE_FOLDER, 8888);
+ // Create a child of the last created folder
+ this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._orphanFolderId);
+ },
+
+ check: function() {
+ // Check that bookmarks are now children of a real folder (unsorted)
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND parent = :parent");
+ stmt.params["item_id"] = this._orphanBookmarkId;
+ stmt.params["parent"] = bs.unfiledBookmarksFolder;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["item_id"] = this._orphanSeparatorId;
+ stmt.params["parent"] = bs.unfiledBookmarksFolder;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["item_id"] = this._orphanFolderId;
+ stmt.params["parent"] = bs.unfiledBookmarksFolder;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["item_id"] = this._bookmarkId;
+ stmt.params["parent"] = this._orphanFolderId;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.6",
+ desc: "Fix wrong item types | bookmarks",
+
+ _separatorId: null,
+ _folderId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Add a separator with a fk
+ this._separatorId = addBookmark(this._placeId, bs.TYPE_SEPARATOR);
+ // Add a folder with a fk
+ this._folderId = addBookmark(this._placeId, bs.TYPE_FOLDER);
+ },
+
+ check: function() {
+ // Check that items with an fk have been converted to bookmarks
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND type = :type");
+ stmt.params["item_id"] = this._separatorId;
+ stmt.params["type"] = bs.TYPE_BOOKMARK;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["item_id"] = this._folderId;
+ stmt.params["type"] = bs.TYPE_BOOKMARK;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.7",
+ desc: "Fix wrong item types | bookmarks",
+
+ _validBookmarkId: null,
+ _invalidBookmarkId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Add a bookmark with a valid place id
+ this._validBookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK);
+ // Add a bookmark with a null place id
+ this._invalidBookmarkId = addBookmark(null, bs.TYPE_BOOKMARK);
+ },
+
+ check: function() {
+ // Check valid bookmark
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND type = :type");
+ stmt.params["item_id"] = this._validBookmarkId;
+ stmt.params["type"] = bs.TYPE_BOOKMARK;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check invalid bookmark has been converted to a folder
+ stmt.params["item_id"] = this._invalidBookmarkId;
+ stmt.params["type"] = bs.TYPE_FOLDER;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.9",
+ desc: "Fix wrong parents",
+
+ _bookmarkId: null,
+ _separatorId: null,
+ _bookmarkId1: null,
+ _bookmarkId2: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Insert a bookmark
+ this._bookmarkId = addBookmark(this._placeId, bs.TYPE_BOOKMARK);
+ // Insert a separator
+ this._separatorId = addBookmark(null, bs.TYPE_SEPARATOR);
+ // Create 3 children of these items
+ this._bookmarkId1 = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._bookmarkId);
+ this._bookmarkId2 = addBookmark(this._placeId, bs.TYPE_BOOKMARK, this._separatorId);
+ },
+
+ check: function() {
+ // Check that bookmarks are now children of a real folder (unsorted)
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_bookmarks WHERE id = :item_id AND parent = :parent");
+ stmt.params["item_id"] = this._bookmarkId1;
+ stmt.params["parent"] = bs.unfiledBookmarksFolder;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["item_id"] = this._bookmarkId2;
+ stmt.params["parent"] = bs.unfiledBookmarksFolder;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.10",
+ desc: "Recalculate positions",
+
+ _unfiledBookmarks: [],
+ _toolbarBookmarks: [],
+
+ setup: function() {
+ const NUM_BOOKMARKS = 20;
+ bs.runInBatchMode({
+ runBatched: function (aUserData) {
+ // Add bookmarks to two folders to better perturbate the table.
+ for (let i = 0; i < NUM_BOOKMARKS; i++) {
+ bs.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+ NetUtil.newURI("http://example.com/"),
+ bs.DEFAULT_INDEX, "testbookmark");
+ }
+ for (let i = 0; i < NUM_BOOKMARKS; i++) {
+ bs.insertBookmark(PlacesUtils.toolbarFolderId,
+ NetUtil.newURI("http://example.com/"),
+ bs.DEFAULT_INDEX, "testbookmark");
+ }
+ }
+ }, null);
+
+ function randomize_positions(aParent, aResultArray) {
+ let stmt = mDBConn.createStatement(
+ `UPDATE moz_bookmarks SET position = :rand
+ WHERE id IN (
+ SELECT id FROM moz_bookmarks WHERE parent = :parent
+ ORDER BY RANDOM() LIMIT 1
+ )`
+ );
+ for (let i = 0; i < (NUM_BOOKMARKS / 2); i++) {
+ stmt.params["parent"] = aParent;
+ stmt.params["rand"] = Math.round(Math.random() * (NUM_BOOKMARKS - 1));
+ stmt.execute();
+ stmt.reset();
+ }
+ stmt.finalize();
+
+ // Build the expected ordered list of bookmarks.
+ stmt = mDBConn.createStatement(
+ `SELECT id, position
+ FROM moz_bookmarks WHERE parent = :parent
+ ORDER BY position ASC, ROWID ASC`
+ );
+ stmt.params["parent"] = aParent;
+ while (stmt.executeStep()) {
+ aResultArray.push(stmt.row.id);
+ print(stmt.row.id + "\t" + stmt.row.position + "\t" +
+ (aResultArray.length - 1));
+ }
+ stmt.finalize();
+ }
+
+ // Set random positions for the added bookmarks.
+ randomize_positions(PlacesUtils.unfiledBookmarksFolderId,
+ this._unfiledBookmarks);
+ randomize_positions(PlacesUtils.toolbarFolderId, this._toolbarBookmarks);
+ },
+
+ check: function() {
+ function check_order(aParent, aResultArray) {
+ // Build the expected ordered list of bookmarks.
+ let stmt = mDBConn.createStatement(
+ `SELECT id, position FROM moz_bookmarks WHERE parent = :parent
+ ORDER BY position ASC`
+ );
+ stmt.params["parent"] = aParent;
+ let pass = true;
+ while (stmt.executeStep()) {
+ print(stmt.row.id + "\t" + stmt.row.position);
+ if (aResultArray.indexOf(stmt.row.id) != stmt.row.position) {
+ pass = false;
+ }
+ }
+ stmt.finalize();
+ if (!pass) {
+ dump_table("moz_bookmarks");
+ do_throw("Unexpected unfiled bookmarks order.");
+ }
+ }
+
+ check_order(PlacesUtils.unfiledBookmarksFolderId, this._unfiledBookmarks);
+ check_order(PlacesUtils.toolbarFolderId, this._toolbarBookmarks);
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "D.12",
+ desc: "Fix empty-named tags",
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ let placeId = addPlace();
+ // Create a empty-named tag.
+ this._untitledTagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder, null, null, "");
+ // Insert a bookmark in the tag, otherwise it will be removed.
+ addBookmark(placeId, bs.TYPE_BOOKMARK, this._untitledTagId);
+ // Create a empty-named folder.
+ this._untitledFolderId = addBookmark(null, bs.TYPE_FOLDER, bs.toolbarFolder, null, null, "");
+ // Create a titled tag.
+ this._titledTagId = addBookmark(null, bs.TYPE_FOLDER, bs.tagsFolder, null, null, "titledTag");
+ // Insert a bookmark in the tag, otherwise it will be removed.
+ addBookmark(placeId, bs.TYPE_BOOKMARK, this._titledTagId);
+ // Create a titled folder.
+ this._titledFolderId = addBookmark(null, bs.TYPE_FOLDER, bs.toolbarFolder, null, null, "titledFolder");
+ },
+
+ check: function() {
+ // Check that valid bookmark is still there
+ let stmt = mDBConn.createStatement(
+ "SELECT title FROM moz_bookmarks WHERE id = :id"
+ );
+ stmt.params["id"] = this._untitledTagId;
+ do_check_true(stmt.executeStep());
+ do_check_eq(stmt.row.title, "(notitle)");
+ stmt.reset();
+ stmt.params["id"] = this._untitledFolderId;
+ do_check_true(stmt.executeStep());
+ do_check_eq(stmt.row.title, "");
+ stmt.reset();
+ stmt.params["id"] = this._titledTagId;
+ do_check_true(stmt.executeStep());
+ do_check_eq(stmt.row.title, "titledTag");
+ stmt.reset();
+ stmt.params["id"] = this._titledFolderId;
+ do_check_true(stmt.executeStep());
+ do_check_eq(stmt.row.title, "titledFolder");
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "E.1",
+ desc: "Remove orphan icons",
+
+ _placeId: null,
+
+ setup: function() {
+ // Insert favicon entries
+ let stmt = mDBConn.createStatement("INSERT INTO moz_favicons (id, url) VALUES(:favicon_id, :url)");
+ stmt.params["favicon_id"] = 1;
+ stmt.params["url"] = "http://www1.mozilla.org/favicon.ico";
+ stmt.execute();
+ stmt.reset();
+ stmt.params["favicon_id"] = 2;
+ stmt.params["url"] = "http://www2.mozilla.org/favicon.ico";
+ stmt.execute();
+ stmt.finalize();
+ // Insert a place using the existing favicon entry
+ this._placeId = addPlace("http://www.mozilla.org", 1);
+ },
+
+ check: function() {
+ // Check that used icon is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_favicons WHERE id = :favicon_id");
+ stmt.params["favicon_id"] = 1;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check that unused icon has been removed
+ stmt.params["favicon_id"] = 2;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "F.1",
+ desc: "Remove orphan visits",
+
+ _placeId: null,
+ _invalidPlaceId: 1337,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Add a valid visit and an invalid one
+ stmt = mDBConn.createStatement("INSERT INTO moz_historyvisits(place_id) VALUES (:place_id)");
+ stmt.params["place_id"] = this._placeId;
+ stmt.execute();
+ stmt.reset();
+ stmt.params["place_id"] = this._invalidPlaceId;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that valid visit is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_historyvisits WHERE place_id = :place_id");
+ stmt.params["place_id"] = this._placeId;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check that invalid visit has been removed
+ stmt.params["place_id"] = this._invalidPlaceId;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "G.1",
+ desc: "Remove orphan input history",
+
+ _placeId: null,
+ _invalidPlaceId: 1337,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Add input history entries
+ let stmt = mDBConn.createStatement("INSERT INTO moz_inputhistory (place_id, input) VALUES (:place_id, :input)");
+ stmt.params["place_id"] = this._placeId;
+ stmt.params["input"] = "moz";
+ stmt.execute();
+ stmt.reset();
+ stmt.params["place_id"] = this._invalidPlaceId;
+ stmt.params["input"] = "moz";
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that inputhistory on valid place is still there
+ let stmt = mDBConn.createStatement("SELECT place_id FROM moz_inputhistory WHERE place_id = :place_id");
+ stmt.params["place_id"] = this._placeId;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ // Check that inputhistory on invalid place has gone
+ stmt.params["place_id"] = this._invalidPlaceId;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "H.1",
+ desc: "Remove item annos with an invalid attribute",
+
+ _usedItemAttribute: "usedItem",
+ _bookmarkId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Insert a bookmark
+ this._bookmarkId = addBookmark(this._placeId);
+ // Add a used attribute.
+ let stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)");
+ stmt.params['anno'] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.finalize();
+ 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))");
+ stmt.params['item_id'] = this._bookmarkId;
+ stmt.params['anno'] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.finalize();
+ // Add an annotation with a nonexistent attribute
+ stmt = mDBConn.createStatement("INSERT INTO moz_items_annos (item_id, anno_attribute_id) VALUES(:item_id, 1337)");
+ stmt.params['item_id'] = this._bookmarkId;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that used attribute is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno");
+ stmt.params['anno'] = this._usedItemAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // check that annotation with valid attribute is still there
+ stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)");
+ stmt.params['anno'] = this._usedItemAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // Check that annotation with bogus attribute has been removed
+ stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE anno_attribute_id = 1337");
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "H.2",
+ desc: "Remove orphan item annotations",
+
+ _usedItemAttribute: "usedItem",
+ _bookmarkId: null,
+ _invalidBookmarkId: 8888,
+ _placeId: null,
+
+ setup: function() {
+ // Add a place to ensure place_id = 1 is valid
+ this._placeId = addPlace();
+ // Insert a bookmark
+ this._bookmarkId = addBookmark(this._placeId);
+ // Add a used attribute.
+ stmt = mDBConn.createStatement("INSERT INTO moz_anno_attributes (name) VALUES (:anno)");
+ stmt.params['anno'] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.finalize();
+ 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))");
+ stmt.params["item_id"] = this._bookmarkId;
+ stmt.params["anno"] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.reset();
+ // Add an annotation to a nonexistent item
+ stmt.params["item_id"] = this._invalidBookmarkId;
+ stmt.params["anno"] = this._usedItemAttribute;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that used attribute is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_anno_attributes WHERE name = :anno");
+ stmt.params['anno'] = this._usedItemAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // check that annotation with valid attribute is still there
+ stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE anno_attribute_id = (SELECT id FROM moz_anno_attributes WHERE name = :anno)");
+ stmt.params['anno'] = this._usedItemAttribute;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // Check that an annotation to a nonexistent page has been removed
+ stmt = mDBConn.createStatement("SELECT id FROM moz_items_annos WHERE item_id = 8888");
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "I.1",
+ desc: "Remove unused keywords",
+
+ _bookmarkId: null,
+ _placeId: null,
+
+ setup: function() {
+ // Insert 2 keywords
+ let stmt = mDBConn.createStatement("INSERT INTO moz_keywords (id, keyword, place_id) VALUES(:id, :keyword, :place_id)");
+ stmt.params["id"] = 1;
+ stmt.params["keyword"] = "unused";
+ stmt.params["place_id"] = 100;
+ stmt.execute();
+ stmt.finalize();
+ },
+
+ check: function() {
+ // Check that "used" keyword is still there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_keywords WHERE keyword = :keyword");
+ // Check that "unused" keyword has gone
+ stmt.params["keyword"] = "unused";
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "L.1",
+ desc: "Fix wrong favicon ids",
+
+ _validIconPlaceId: null,
+ _invalidIconPlaceId: null,
+
+ setup: function() {
+ // Insert a favicon entry
+ let stmt = mDBConn.createStatement("INSERT INTO moz_favicons (id, url) VALUES(1, :url)");
+ stmt.params["url"] = "http://www.mozilla.org/favicon.ico";
+ stmt.execute();
+ stmt.finalize();
+ // Insert a place using the existing favicon entry
+ this._validIconPlaceId = addPlace("http://www1.mozilla.org", 1);
+
+ // Insert a place using a nonexistent favicon entry
+ this._invalidIconPlaceId = addPlace("http://www2.mozilla.org", 1337);
+ },
+
+ check: function() {
+ // Check that bogus favicon is not there
+ let stmt = mDBConn.createStatement("SELECT id FROM moz_places WHERE favicon_id = :favicon_id");
+ stmt.params["favicon_id"] = 1337;
+ do_check_false(stmt.executeStep());
+ stmt.reset();
+ // Check that valid favicon is still there
+ stmt.params["favicon_id"] = 1;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ // Check that place entries are there
+ stmt = mDBConn.createStatement("SELECT id FROM moz_places WHERE id = :place_id");
+ stmt.params["place_id"] = this._validIconPlaceId;
+ do_check_true(stmt.executeStep());
+ stmt.reset();
+ stmt.params["place_id"] = this._invalidIconPlaceId;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "L.2",
+ desc: "Recalculate visit_count and last_visit_date",
+
+ setup: function* () {
+ function setVisitCount(aURL, aValue) {
+ let stmt = mDBConn.createStatement(
+ `UPDATE moz_places SET visit_count = :count WHERE url_hash = hash(:url)
+ AND url = :url`
+ );
+ stmt.params.count = aValue;
+ stmt.params.url = aURL;
+ stmt.execute();
+ stmt.finalize();
+ }
+ function setLastVisitDate(aURL, aValue) {
+ let stmt = mDBConn.createStatement(
+ `UPDATE moz_places SET last_visit_date = :date WHERE url_hash = hash(:url)
+ AND url = :url`
+ );
+ stmt.params.date = aValue;
+ stmt.params.url = aURL;
+ stmt.execute();
+ stmt.finalize();
+ }
+
+ let now = Date.now() * 1000;
+ // Add a page with 1 visit.
+ let url = "http://1.moz.org/";
+ yield PlacesTestUtils.addVisits({ uri: uri(url), visitDate: now++ });
+ // Add a page with 1 visit and set wrong visit_count.
+ url = "http://2.moz.org/";
+ yield PlacesTestUtils.addVisits({ uri: uri(url), visitDate: now++ });
+ setVisitCount(url, 10);
+ // Add a page with 1 visit and set wrong last_visit_date.
+ url = "http://3.moz.org/";
+ yield PlacesTestUtils.addVisits({ uri: uri(url), visitDate: now++ });
+ setLastVisitDate(url, now++);
+ // Add a page with 1 visit and set wrong stats.
+ url = "http://4.moz.org/";
+ yield PlacesTestUtils.addVisits({ uri: uri(url), visitDate: now++ });
+ setVisitCount(url, 10);
+ setLastVisitDate(url, now++);
+
+ // Add a page without visits.
+ url = "http://5.moz.org/";
+ addPlace(url);
+ // Add a page without visits and set wrong visit_count.
+ url = "http://6.moz.org/";
+ addPlace(url);
+ setVisitCount(url, 10);
+ // Add a page without visits and set wrong last_visit_date.
+ url = "http://7.moz.org/";
+ addPlace(url);
+ setLastVisitDate(url, now++);
+ // Add a page without visits and set wrong stats.
+ url = "http://8.moz.org/";
+ addPlace(url);
+ setVisitCount(url, 10);
+ setLastVisitDate(url, now++);
+ },
+
+ check: function() {
+ let stmt = mDBConn.createStatement(
+ `SELECT h.id FROM moz_places h
+ JOIN moz_historyvisits v ON v.place_id = h.id AND visit_type NOT IN (0,4,7,8,9)
+ GROUP BY h.id HAVING h.visit_count <> count(*)
+ UNION ALL
+ SELECT h.id FROM moz_places h
+ JOIN moz_historyvisits v ON v.place_id = h.id
+ GROUP BY h.id HAVING h.last_visit_date <> MAX(v.visit_date)`
+ );
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "L.3",
+ desc: "recalculate hidden for redirects.",
+
+ *setup() {
+ yield PlacesTestUtils.addVisits([
+ { uri: NetUtil.newURI("http://l3.moz.org/"),
+ transition: TRANSITION_TYPED },
+ { uri: NetUtil.newURI("http://l3.moz.org/redirecting/"),
+ transition: TRANSITION_TYPED },
+ { uri: NetUtil.newURI("http://l3.moz.org/redirecting2/"),
+ transition: TRANSITION_REDIRECT_TEMPORARY,
+ referrer: NetUtil.newURI("http://l3.moz.org/redirecting/") },
+ { uri: NetUtil.newURI("http://l3.moz.org/target/"),
+ transition: TRANSITION_REDIRECT_PERMANENT,
+ referrer: NetUtil.newURI("http://l3.moz.org/redirecting2/") },
+ ]);
+ },
+
+ check: function () {
+ return new Promise(resolve => {
+ let stmt = mDBConn.createAsyncStatement(
+ "SELECT h.url FROM moz_places h WHERE h.hidden = 1"
+ );
+ stmt.executeAsync({
+ _count: 0,
+ handleResult: function(aResultSet) {
+ for (let row; (row = aResultSet.getNextRow());) {
+ let url = row.getResultByIndex(0);
+ do_check_true(/redirecting/.test(url));
+ this._count++;
+ }
+ },
+ handleError: function(aError) {
+ },
+ handleCompletion: function(aReason) {
+ dump_table("moz_places");
+ dump_table("moz_historyvisits");
+ do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED);
+ do_check_eq(this._count, 2);
+ resolve();
+ }
+ });
+ stmt.finalize();
+ });
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "L.4",
+ desc: "recalculate foreign_count.",
+
+ *setup() {
+ this._pageGuid = (yield PlacesUtils.history.insert({ url: "http://l4.moz.org/",
+ visits: [{ date: new Date() }] })).guid;
+ yield PlacesUtils.bookmarks.insert({ url: "http://l4.moz.org/",
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid});
+ yield PlacesUtils.keywords.insert({ url: "http://l4.moz.org/", keyword: "kw" });
+ Assert.equal((yield this._getForeignCount()), 2);
+ },
+
+ *_getForeignCount() {
+ let db = yield PlacesUtils.promiseDBConnection();
+ let rows = yield db.execute(`SELECT foreign_count FROM moz_places
+ WHERE guid = :guid`, { guid: this._pageGuid });
+ return rows[0].getResultByName("foreign_count");
+ },
+
+ *check() {
+ Assert.equal((yield this._getForeignCount()), 2);
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "L.5",
+ desc: "recalculate hashes when missing.",
+
+ *setup() {
+ this._pageGuid = (yield PlacesUtils.history.insert({ url: "http://l5.moz.org/",
+ visits: [{ date: new Date() }] })).guid;
+ Assert.ok((yield this._getHash()) > 0);
+ yield PlacesUtils.withConnectionWrapper("change url hash", Task.async(function* (db) {
+ yield db.execute(`UPDATE moz_places SET url_hash = 0`);
+ }));
+ Assert.equal((yield this._getHash()), 0);
+ },
+
+ *_getHash() {
+ let db = yield PlacesUtils.promiseDBConnection();
+ let rows = yield db.execute(`SELECT url_hash FROM moz_places
+ WHERE guid = :guid`, { guid: this._pageGuid });
+ return rows[0].getResultByName("url_hash");
+ },
+
+ *check() {
+ Assert.ok((yield this._getHash()) > 0);
+ }
+});
+
+// ------------------------------------------------------------------------------
+
+tests.push({
+ name: "Z",
+ desc: "Sanity: Preventive maintenance does not touch valid items",
+
+ _uri1: uri("http://www1.mozilla.org"),
+ _uri2: uri("http://www2.mozilla.org"),
+ _folderId: null,
+ _bookmarkId: null,
+ _separatorId: null,
+
+ setup: function* () {
+ // use valid api calls to create a bunch of items
+ yield PlacesTestUtils.addVisits([
+ { uri: this._uri1 },
+ { uri: this._uri2 },
+ ]);
+
+ this._folderId = bs.createFolder(bs.toolbarFolder, "testfolder",
+ bs.DEFAULT_INDEX);
+ do_check_true(this._folderId > 0);
+ this._bookmarkId = bs.insertBookmark(this._folderId, this._uri1,
+ bs.DEFAULT_INDEX, "testbookmark");
+ do_check_true(this._bookmarkId > 0);
+ this._separatorId = bs.insertSeparator(bs.unfiledBookmarksFolder,
+ bs.DEFAULT_INDEX);
+ do_check_true(this._separatorId > 0);
+ ts.tagURI(this._uri1, ["testtag"]);
+ fs.setAndFetchFaviconForPage(this._uri2, SMALLPNG_DATA_URI, false,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ null,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ yield PlacesUtils.keywords.insert({ url: this._uri1.spec, keyword: "testkeyword" });
+ as.setPageAnnotation(this._uri2, "anno", "anno", 0, as.EXPIRE_NEVER);
+ as.setItemAnnotation(this._bookmarkId, "anno", "anno", 0, as.EXPIRE_NEVER);
+ },
+
+ check: Task.async(function* () {
+ // Check that all items are correct
+ let isVisited = yield promiseIsURIVisited(this._uri1);
+ do_check_true(isVisited);
+ isVisited = yield promiseIsURIVisited(this._uri2);
+ do_check_true(isVisited);
+
+ do_check_eq(bs.getBookmarkURI(this._bookmarkId).spec, this._uri1.spec);
+ do_check_eq(bs.getItemIndex(this._folderId), 0);
+ do_check_eq(bs.getItemType(this._folderId), bs.TYPE_FOLDER);
+ do_check_eq(bs.getItemType(this._separatorId), bs.TYPE_SEPARATOR);
+
+ do_check_eq(ts.getTagsForURI(this._uri1).length, 1);
+ do_check_eq((yield PlacesUtils.keywords.fetch({ url: this._uri1.spec })).keyword, "testkeyword");
+ do_check_eq(as.getPageAnnotation(this._uri2, "anno"), "anno");
+ do_check_eq(as.getItemAnnotation(this._bookmarkId, "anno"), "anno");
+
+ yield new Promise(resolve => {
+ fs.getFaviconURLForPage(this._uri2, aFaviconURI => {
+ do_check_true(aFaviconURI.equals(SMALLPNG_DATA_URI));
+ resolve();
+ });
+ });
+ })
+});
+
+// ------------------------------------------------------------------------------
+
+add_task(function* test_preventive_maintenance()
+{
+ // Get current bookmarks max ID for cleanup
+ let stmt = mDBConn.createStatement("SELECT MAX(id) FROM moz_bookmarks");
+ stmt.executeStep();
+ defaultBookmarksMaxId = stmt.getInt32(0);
+ stmt.finalize();
+ do_check_true(defaultBookmarksMaxId > 0);
+
+ for (let test of tests) {
+ dump("\nExecuting test: " + test.name + "\n" + "*** " + test.desc + "\n");
+ yield test.setup();
+
+ let promiseMaintenanceFinished =
+ promiseTopicObserved(FINISHED_MAINTENANCE_NOTIFICATION_TOPIC);
+ Services.prefs.clearUserPref("places.database.lastMaintenance");
+ let callbackInvoked = false;
+ PlacesDBUtils.maintenanceOnIdle(() => callbackInvoked = true);
+ yield promiseMaintenanceFinished;
+ do_check_true(callbackInvoked);
+
+ // Check the lastMaintenance time has been saved.
+ do_check_neq(Services.prefs.getIntPref("places.database.lastMaintenance"), null);
+
+ yield test.check();
+
+ cleanDatabase();
+ }
+
+ // Sanity check: all roots should be intact
+ do_check_eq(bs.getFolderIdForItem(bs.placesRoot), 0);
+ do_check_eq(bs.getFolderIdForItem(bs.bookmarksMenuFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.tagsFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.unfiledBookmarksFolder), bs.placesRoot);
+ do_check_eq(bs.getFolderIdForItem(bs.toolbarFolder), bs.placesRoot);
+});