diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /toolkit/components/places/tests/unit/test_preventive_maintenance.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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.js | 1356 |
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); +}); |