--- a/services/sync/tests/unit/test_bookmark_duping.js
+++ b/services/sync/tests/unit/test_bookmark_duping.js
@@ -127,67 +127,217 @@ async function validate(collection, expe
// All server records and the entire bookmark tree.
do_print("Server records:\n" + JSON.stringify(collection.payloads(), undefined, 2));
let tree = await PlacesUtils.promiseBookmarksTree("", { includeItemIds: true });
do_print("Local bookmark tree:\n" + JSON.stringify(tree, undefined, 2));
ok(false);
}
}
+function nodesToInfos(nodes) {
+ return nodes.map(node => {
+ let info = {
+ guid: node.guid,
+ index: node.index,
+ };
+ if (node.children) {
+ info.children = nodesToInfos(node.children);
+ }
+ return info;
+ });
+}
+
+async function assertTreeMatches(rootGuid, expected, message) {
+ let root = await PlacesUtils.promiseBookmarksTree(rootGuid);
+ let actual = nodesToInfos(root.children);
+
+ _(`Expected structure for ${rootGuid}`, JSON.stringify(expected));
+ _(`Actual structure for ${rootGuid}`, JSON.stringify(actual));
+ deepEqual(actual, expected, message);
+}
+
+add_task(async function test_dedupe_subtrees() {
+ _("Ensure that congruent subtrees are deduped");
+
+ let { server, collection } = await this.setup();
+
+ let parentFolderGuid = Utils.makeGUID();
+ let fxGuid = Utils.makeGUID();
+ let tbGuid = Utils.makeGUID();
+ let startFolderGuid = Utils.makeGUID();
+ let bzGuid = Utils.makeGUID();
+ let nightlyGuid = Utils.makeGUID();
+
+ let serverRecords = [{
+ id: parentFolderGuid,
+ type: "folder",
+ title: "Parent folder",
+ parentid: "menu",
+ children: [tbGuid, fxGuid, startFolderGuid],
+ }, {
+ id: fxGuid,
+ type: "bookmark",
+ bmkUri: "http://getfirefox.com/",
+ title: "Get Firefox!",
+ parentid: parentFolderGuid,
+ }, {
+ id: tbGuid,
+ type: "bookmark",
+ bmkUri: "http://getthunderbird.com/",
+ title: "Get Thunderbird!",
+ parentid: parentFolderGuid,
+ }, {
+ id: startFolderGuid,
+ type: "folder",
+ title: "Getting Started",
+ parentid: parentFolderGuid,
+ children: [nightlyGuid, bzGuid],
+ }, {
+ id: bzGuid,
+ type: "bookmark",
+ bmkUri: "https://bugzilla.mozilla.org/",
+ title: "Bugzilla",
+ parentid: startFolderGuid,
+ }, {
+ id: nightlyGuid,
+ type: "bookmark",
+ bmkUri: "https://nightly.mozilla.org/",
+ title: "Nightly",
+ parentid: startFolderGuid,
+ }];
+
+ try {
+ _("Create subtree on server");
+ for (let record of serverRecords) {
+ collection.insert(record.id, encryptPayload(record), Date.now() / 1000 + 500);
+ }
+
+ // The local subtree has different GUIDs and child bookmarks in a different
+ // order.
+ let supportGuid = PlacesUtils.history.makeGuid();
+ _("Create local subtree");
+ await PlacesUtils.bookmarks.insertTree({
+ guid: PlacesUtils.bookmarks.menuGuid,
+ children: [{
+ type: PlacesUtils.bookmarks.TYPE_FOLDER,
+ title: "Parent folder",
+ children: [{
+ title: "Get Firefox!",
+ url: "http://getfirefox.com/",
+ }, {
+ type: PlacesUtils.bookmarks.TYPE_FOLDER,
+ title: "Getting Started",
+ children: [{
+ title: "Bugzilla",
+ url: "https://bugzilla.mozilla.org/",
+ }, {
+ // A local bookmark that's not on the server, and that we should
+ // move into the collapsed folder.
+ guid: supportGuid,
+ title: "Help and Tutorials",
+ url: "https://support.mozilla.org",
+ }, {
+ title: "Nightly",
+ url: "https://nightly.mozilla.org/",
+ }],
+ }, {
+ title: "Get Thunderbird!",
+ url: "http://getthunderbird.com/",
+ }],
+ }],
+ });
+
+ _("Sync to flatten dupes");
+ engine.sync();
+
+ assertTreeMatches(PlacesUtils.bookmarks.menuGuid, [{
+ guid: parentFolderGuid,
+ index: 0,
+ children: [{
+ guid: tbGuid,
+ index: 0,
+ }, {
+ guid: fxGuid,
+ index: 1,
+ }, {
+ guid: startFolderGuid,
+ index: 2,
+ children: [{
+ guid: nightlyGuid,
+ index: 0,
+ }, {
+ guid: bzGuid,
+ index: 1,
+ }, {
+ guid: supportGuid,
+ index: 2,
+ }],
+ }],
+ }], "New local dupes should be merged with existing remote items");
+
+ // and a final sanity check - use the validator
+ await validate(collection);
+ } finally {
+ await cleanup(server);
+ }
+});
+
add_task(async function test_dupe_bookmark() {
_("Ensure that a bookmark we consider a dupe is handled correctly.");
let { server, collection } = await this.setup();
try {
// The parent folder and one bookmark in it.
let {id: folder1_id, guid: folder1_guid } = await createFolder(bms.toolbarFolder, "Folder 1");
let {guid: bmk1_guid} = await createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
- engine.sync();
-
- // We've added the bookmark, its parent (folder1) plus "menu", "toolbar", "unfiled", and "mobile".
- equal(collection.count(), 6);
- equal((await getFolderChildrenIDs(folder1_id)).length, 1);
-
- // Now create a new incoming record that looks alot like a dupe.
+ // Create a remote bookmark that's a dupe of the local bookmark.
let newGUID = Utils.makeGUID();
let to_apply = {
id: newGUID,
bmkUri: "http://getfirefox.com/",
type: "bookmark",
title: "Get Firefox!",
parentName: "Folder 1",
parentid: folder1_guid,
};
+ collection.insert(newGUID, encryptPayload(to_apply), Date.now() / 1000 + 500);
- collection.insert(newGUID, encryptPayload(to_apply), Date.now() / 1000 + 500);
- _("Syncing so new dupe record is processed");
- engine.lastSync = engine.lastSync - 5;
engine.sync();
- // We should have logically deleted the dupe record.
- equal(collection.count(), 7);
- ok(getServerRecord(collection, bmk1_guid).deleted);
- // and physically removed from the local store.
+ // We've added the bookmark's parent (folder1) plus "menu", "toolbar", "unfiled", and "mobile".
+ equal(collection.count(), 6);
+ // and physically removed the local dupe.
await promiseNoLocalItem(bmk1_guid);
- // Parent should still only have 1 item.
+ // Parent should only have 1 item.
equal((await getFolderChildrenIDs(folder1_id)).length, 1);
// The parent record on the server should now reference the new GUID and not the old.
let serverRecord = getServerRecord(collection, folder1_guid);
ok(!serverRecord.children.includes(bmk1_guid));
ok(serverRecord.children.includes(newGUID));
// and a final sanity check - use the validator
await validate(collection);
} finally {
await cleanup(server);
}
});
+add_task(async function test_dedupe_keeps_explicit_dupes() {
+ // The "multiple empty folders with different contents" test from bug 1213369.
+ // Make sure we use local and remote indices to match these explicit dupes.
+});
+
+/* TODO: These semantics are no longer correct. We'll never dedupe across
+ parents; reparenting a similar item *will* result in a dupe. Tests that
+ insert dupes into remote collections are also wrong, since they don't
+ update the parent and will cause validation failures. */
+
+/*
add_task(async function test_dupe_reparented_bookmark() {
_("Ensure that a bookmark we consider a dupe from a different parent is handled correctly");
let { server, collection } = await this.setup();
try {
// The parent folder and one bookmark in it.
let {id: folder1_id, guid: folder1_guid } = await createFolder(bms.toolbarFolder, "Folder 1");
@@ -641,8 +791,9 @@ add_task(async function test_dupe_empty_
// original folder should be logically deleted.
ok(getServerRecord(collection, folder1_guid).deleted);
await promiseNoLocalItem(folder1_guid);
} finally {
await cleanup(server);
}
});
// XXX - TODO - folders with children. Bug 1293163
+*/