michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://gre/modules/PlacesUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Log.jsm"); michael@0: Cu.import("resource://services-sync/engines.js"); michael@0: Cu.import("resource://services-sync/engines/bookmarks.js"); michael@0: Cu.import("resource://services-sync/service.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://testing-common/services/sync/utils.js"); michael@0: michael@0: const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; michael@0: var IOService = Cc["@mozilla.org/network/io-service;1"] michael@0: .getService(Ci.nsIIOService); michael@0: ("http://www.mozilla.com", null, null); michael@0: michael@0: michael@0: Service.engineManager.register(BookmarksEngine); michael@0: let engine = Service.engineManager.get("bookmarks"); michael@0: let store = engine._store; michael@0: michael@0: // Clean up after other tests. Only necessary in XULRunner. michael@0: store.wipe(); michael@0: michael@0: function newSmartBookmark(parent, uri, position, title, queryID) { michael@0: let id = PlacesUtils.bookmarks.insertBookmark(parent, uri, position, title); michael@0: PlacesUtils.annotations.setItemAnnotation(id, SMART_BOOKMARKS_ANNO, michael@0: queryID, 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: return id; michael@0: } michael@0: michael@0: function smartBookmarkCount() { michael@0: // We do it this way because PlacesUtils.annotations.getItemsWithAnnotation michael@0: // doesn't work the same (or at all?) between 3.6 and 4.0. michael@0: let out = {}; michael@0: PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO, out); michael@0: return out.value; michael@0: } michael@0: michael@0: function clearBookmarks() { michael@0: _("Cleaning up existing items."); michael@0: PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.bookmarksMenuFolder); michael@0: PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.tagsFolder); michael@0: PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.toolbarFolder); michael@0: PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.unfiledBookmarksFolder); michael@0: startCount = smartBookmarkCount(); michael@0: } michael@0: michael@0: function serverForFoo(engine) { michael@0: return serverForUsers({"foo": "password"}, { michael@0: meta: {global: {engines: {bookmarks: {version: engine.version, michael@0: syncID: engine.syncID}}}}, michael@0: bookmarks: {} michael@0: }); michael@0: } michael@0: michael@0: // Verify that Places smart bookmarks have their annotation uploaded and michael@0: // handled locally. michael@0: add_test(function test_annotation_uploaded() { michael@0: let server = serverForFoo(engine); michael@0: new SyncTestingInfrastructure(server.server); michael@0: michael@0: let startCount = smartBookmarkCount(); michael@0: michael@0: _("Start count is " + startCount); michael@0: michael@0: if (startCount > 0) { michael@0: // This can happen in XULRunner. michael@0: clearBookmarks(); michael@0: _("Start count is now " + startCount); michael@0: } michael@0: michael@0: _("Create a smart bookmark in the toolbar."); michael@0: let parent = PlacesUtils.toolbarFolderId; michael@0: let uri = michael@0: Utils.makeURI("place:sort=" + michael@0: Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + michael@0: "&maxResults=10"); michael@0: let title = "Most Visited"; michael@0: michael@0: let mostVisitedID = newSmartBookmark(parent, uri, -1, title, "MostVisited"); michael@0: michael@0: _("New item ID: " + mostVisitedID); michael@0: do_check_true(!!mostVisitedID); michael@0: michael@0: let annoValue = PlacesUtils.annotations.getItemAnnotation(mostVisitedID, michael@0: SMART_BOOKMARKS_ANNO); michael@0: _("Anno: " + annoValue); michael@0: do_check_eq("MostVisited", annoValue); michael@0: michael@0: let guid = store.GUIDForId(mostVisitedID); michael@0: _("GUID: " + guid); michael@0: do_check_true(!!guid); michael@0: michael@0: _("Create record object and verify that it's sane."); michael@0: let record = store.createRecord(guid); michael@0: do_check_true(record instanceof Bookmark); michael@0: do_check_true(record instanceof BookmarkQuery); michael@0: michael@0: do_check_eq(record.bmkUri, uri.spec); michael@0: michael@0: _("Make sure the new record carries with it the annotation."); michael@0: do_check_eq("MostVisited", record.queryId); michael@0: michael@0: _("Our count has increased since we started."); michael@0: do_check_eq(smartBookmarkCount(), startCount + 1); michael@0: michael@0: _("Sync record to the server."); michael@0: let collection = server.user("foo").collection("bookmarks"); michael@0: michael@0: try { michael@0: engine.sync(); michael@0: let wbos = collection.keys(function (id) { michael@0: return ["menu", "toolbar", "mobile"].indexOf(id) == -1; michael@0: }); michael@0: do_check_eq(wbos.length, 1); michael@0: michael@0: _("Verify that the server WBO has the annotation."); michael@0: let serverGUID = wbos[0]; michael@0: do_check_eq(serverGUID, guid); michael@0: let serverWBO = collection.wbo(serverGUID); michael@0: do_check_true(!!serverWBO); michael@0: let body = JSON.parse(JSON.parse(serverWBO.payload).ciphertext); michael@0: do_check_eq(body.queryId, "MostVisited"); michael@0: michael@0: _("We still have the right count."); michael@0: do_check_eq(smartBookmarkCount(), startCount + 1); michael@0: michael@0: _("Clear local records; now we can't find it."); michael@0: michael@0: // "Clear" by changing attributes: if we delete it, apparently it sticks michael@0: // around as a deleted record... michael@0: PlacesUtils.bookmarks.setItemTitle(mostVisitedID, "Not Most Visited"); michael@0: PlacesUtils.bookmarks.changeBookmarkURI( michael@0: mostVisitedID, Utils.makeURI("http://something/else")); michael@0: PlacesUtils.annotations.removeItemAnnotation(mostVisitedID, michael@0: SMART_BOOKMARKS_ANNO); michael@0: store.wipe(); michael@0: engine.resetClient(); michael@0: do_check_eq(smartBookmarkCount(), startCount); michael@0: michael@0: _("Sync. Verify that the downloaded record carries the annotation."); michael@0: engine.sync(); michael@0: michael@0: _("Verify that the Places DB now has an annotated bookmark."); michael@0: _("Our count has increased again."); michael@0: do_check_eq(smartBookmarkCount(), startCount + 1); michael@0: michael@0: _("Find by GUID and verify that it's annotated."); michael@0: let newID = store.idForGUID(serverGUID); michael@0: let newAnnoValue = PlacesUtils.annotations.getItemAnnotation( michael@0: newID, SMART_BOOKMARKS_ANNO); michael@0: do_check_eq(newAnnoValue, "MostVisited"); michael@0: do_check_eq(PlacesUtils.bookmarks.getBookmarkURI(newID).spec, uri.spec); michael@0: michael@0: _("Test updating."); michael@0: let newRecord = store.createRecord(serverGUID); michael@0: do_check_eq(newRecord.queryId, newAnnoValue); michael@0: newRecord.queryId = "LeastVisited"; michael@0: store.update(newRecord); michael@0: do_check_eq("LeastVisited", PlacesUtils.annotations.getItemAnnotation( michael@0: newID, SMART_BOOKMARKS_ANNO)); michael@0: michael@0: michael@0: } finally { michael@0: // Clean up. michael@0: store.wipe(); michael@0: Svc.Prefs.resetBranch(""); michael@0: Service.recordManager.clearCache(); michael@0: server.stop(run_next_test); michael@0: } michael@0: }); michael@0: michael@0: add_test(function test_smart_bookmarks_duped() { michael@0: let server = serverForFoo(engine); michael@0: new SyncTestingInfrastructure(server.server); michael@0: michael@0: let parent = PlacesUtils.toolbarFolderId; michael@0: let uri = michael@0: Utils.makeURI("place:sort=" + michael@0: Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + michael@0: "&maxResults=10"); michael@0: let title = "Most Visited"; michael@0: let mostVisitedID = newSmartBookmark(parent, uri, -1, title, "MostVisited"); michael@0: let mostVisitedGUID = store.GUIDForId(mostVisitedID); michael@0: michael@0: let record = store.createRecord(mostVisitedGUID); michael@0: michael@0: _("Prepare sync."); michael@0: let collection = server.user("foo").collection("bookmarks"); michael@0: michael@0: try { michael@0: engine._syncStartup(); michael@0: michael@0: _("Verify that mapDupe uses the anno, discovering a dupe regardless of URI."); michael@0: do_check_eq(mostVisitedGUID, engine._mapDupe(record)); michael@0: michael@0: record.bmkUri = "http://foo/"; michael@0: do_check_eq(mostVisitedGUID, engine._mapDupe(record)); michael@0: do_check_neq(PlacesUtils.bookmarks.getBookmarkURI(mostVisitedID).spec, michael@0: record.bmkUri); michael@0: michael@0: _("Verify that different annos don't dupe."); michael@0: let other = new BookmarkQuery("bookmarks", "abcdefabcdef"); michael@0: other.queryId = "LeastVisited"; michael@0: other.parentName = "Bookmarks Toolbar"; michael@0: other.bmkUri = "place:foo"; michael@0: other.title = ""; michael@0: do_check_eq(undefined, engine._findDupe(other)); michael@0: michael@0: _("Handle records without a queryId entry."); michael@0: record.bmkUri = uri; michael@0: delete record.queryId; michael@0: do_check_eq(mostVisitedGUID, engine._mapDupe(record)); michael@0: michael@0: engine._syncFinish(); michael@0: michael@0: } finally { michael@0: // Clean up. michael@0: store.wipe(); michael@0: server.stop(do_test_finished); michael@0: Svc.Prefs.resetBranch(""); michael@0: Service.recordManager.clearCache(); michael@0: } michael@0: }); michael@0: michael@0: function run_test() { michael@0: initTestLogging("Trace"); michael@0: Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace; michael@0: michael@0: generateNewKeys(Service.collectionKeys); michael@0: michael@0: run_next_test(); michael@0: }