1290 lines
33 KiB
JavaScript
1290 lines
33 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
add_task(async function test_duping_local_newer() {
|
|
let mergeTelemetryCounts;
|
|
let buf = await openMirror("duping_local_newer", {
|
|
recordStepTelemetry(name, took, counts) {
|
|
if (name == "merge") {
|
|
mergeTelemetryCounts = counts.filter(({ count }) => count > 0);
|
|
}
|
|
},
|
|
});
|
|
let localModified = new Date();
|
|
|
|
info("Start with empty local and mirror with merged items");
|
|
await storeRecords(
|
|
buf,
|
|
[
|
|
{
|
|
id: "menu",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["bookmarkAAA5"],
|
|
dateAdded: localModified.getTime(),
|
|
},
|
|
{
|
|
id: "bookmarkAAA5",
|
|
parentid: "menu",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/a",
|
|
title: "A",
|
|
dateAdded: localModified.getTime(),
|
|
},
|
|
],
|
|
{ needsMerge: false }
|
|
);
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
info("Add newer local dupes");
|
|
await PlacesUtils.bookmarks.insertTree({
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
children: [
|
|
{
|
|
guid: "bookmarkAAA1",
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
{
|
|
guid: "bookmarkAAA2",
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
{
|
|
guid: "bookmarkAAA3",
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
],
|
|
});
|
|
|
|
info("Add older remote dupes");
|
|
await storeRecords(buf, [
|
|
{
|
|
id: "menu",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["bookmarkAAAA", "bookmarkAAA4", "bookmarkAAA5"],
|
|
modified: localModified / 1000 - 5,
|
|
},
|
|
{
|
|
id: "bookmarkAAAA",
|
|
parentid: "menu",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/a",
|
|
title: "A",
|
|
keyword: "kw",
|
|
tags: ["remote", "tags"],
|
|
modified: localModified / 1000 - 5,
|
|
},
|
|
{
|
|
id: "bookmarkAAA4",
|
|
parentid: "menu",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/a",
|
|
title: "A",
|
|
modified: localModified / 1000 - 5,
|
|
},
|
|
]);
|
|
|
|
info("Apply remote");
|
|
let changesToUpload = await buf.apply({
|
|
remoteTimeSeconds: localModified / 1000,
|
|
});
|
|
deepEqual(
|
|
await buf.fetchUnmergedGuids(),
|
|
["bookmarkAAA4", "bookmarkAAAA", PlacesUtils.bookmarks.menuGuid],
|
|
"Should leave A4, A, menu with new remote structure unmerged"
|
|
);
|
|
deepEqual(
|
|
mergeTelemetryCounts,
|
|
[
|
|
{ name: "items", count: 9 },
|
|
{ name: "dupes", count: 2 },
|
|
],
|
|
"Should record telemetry with dupe counts"
|
|
);
|
|
|
|
let menuInfo = await PlacesUtils.bookmarks.fetch(
|
|
PlacesUtils.bookmarks.menuGuid
|
|
);
|
|
deepEqual(
|
|
changesToUpload,
|
|
{
|
|
menu: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "menu",
|
|
type: "folder",
|
|
parentid: "places",
|
|
hasDupe: true,
|
|
parentName: "",
|
|
dateAdded: menuInfo.dateAdded.getTime(),
|
|
title: menuInfo.title,
|
|
children: [
|
|
"bookmarkAAAA",
|
|
"bookmarkAAA4",
|
|
"bookmarkAAA3",
|
|
"bookmarkAAA5",
|
|
],
|
|
},
|
|
},
|
|
// Note that we always reupload the deduped local item, because content
|
|
// matching doesn't account for attributes like keywords, synced annos, or
|
|
// tags.
|
|
bookmarkAAAA: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "bookmarkAAAA",
|
|
type: "bookmark",
|
|
parentid: "menu",
|
|
hasDupe: true,
|
|
parentName: menuInfo.title,
|
|
dateAdded: localModified.getTime(),
|
|
title: "A",
|
|
bmkUri: "http://example.com/a",
|
|
},
|
|
},
|
|
// Unchanged from local.
|
|
bookmarkAAA4: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "bookmarkAAA4",
|
|
type: "bookmark",
|
|
parentid: "menu",
|
|
hasDupe: true,
|
|
parentName: menuInfo.title,
|
|
dateAdded: localModified.getTime(),
|
|
title: "A",
|
|
bmkUri: "http://example.com/a",
|
|
},
|
|
},
|
|
bookmarkAAA3: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "bookmarkAAA3",
|
|
type: "bookmark",
|
|
parentid: "menu",
|
|
hasDupe: true,
|
|
parentName: menuInfo.title,
|
|
dateAdded: localModified.getTime(),
|
|
title: "A",
|
|
bmkUri: "http://example.com/a",
|
|
},
|
|
},
|
|
bookmarkAAA5: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "bookmarkAAA5",
|
|
type: "bookmark",
|
|
parentid: "menu",
|
|
hasDupe: true,
|
|
parentName: menuInfo.title,
|
|
dateAdded: localModified.getTime(),
|
|
title: "A",
|
|
bmkUri: "http://example.com/a",
|
|
},
|
|
},
|
|
},
|
|
"Should uploaded newer deduped local items"
|
|
);
|
|
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.menuGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: BookmarksMenuTitle,
|
|
children: [
|
|
{
|
|
guid: "bookmarkAAAA",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
{
|
|
guid: "bookmarkAAA4",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 1,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
{
|
|
guid: "bookmarkAAA3",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 2,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
{
|
|
guid: "bookmarkAAA5",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 3,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
],
|
|
},
|
|
"Should dedupe local multiple bookmarks with similar contents"
|
|
);
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|
|
|
|
add_task(async function test_duping_remote_newer() {
|
|
let buf = await openMirror("duping_remote_new");
|
|
let localModified = new Date();
|
|
|
|
info("Set up mirror");
|
|
await PlacesUtils.bookmarks.insertTree({
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
children: [
|
|
{
|
|
// Shouldn't dupe to `folderA11111` because its sync status is "NORMAL".
|
|
guid: "folderAAAAAA",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "A",
|
|
children: [
|
|
{
|
|
// Shouldn't dupe to `bookmarkG111`.
|
|
guid: "bookmarkGGGG",
|
|
url: "http://example.com/g",
|
|
title: "G",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
await storeRecords(
|
|
buf,
|
|
shuffle([
|
|
{
|
|
id: "menu",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["folderAAAAAA"],
|
|
},
|
|
{
|
|
id: "folderAAAAAA",
|
|
parentid: "menu",
|
|
type: "folder",
|
|
title: "A",
|
|
children: ["bookmarkGGGG"],
|
|
},
|
|
{
|
|
id: "bookmarkGGGG",
|
|
parentid: "folderAAAAAA",
|
|
type: "bookmark",
|
|
title: "G",
|
|
bmkUri: "http://example.com/g",
|
|
},
|
|
]),
|
|
{ needsMerge: false }
|
|
);
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
info("Insert local dupes");
|
|
await PlacesUtils.bookmarks.insertTree({
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
children: [
|
|
{
|
|
// Should dupe to `folderB11111`.
|
|
guid: "folderBBBBBB",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "B",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
children: [
|
|
{
|
|
// Should dupe to `bookmarkC222`.
|
|
guid: "bookmarkC111",
|
|
url: "http://example.com/c",
|
|
title: "C",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
{
|
|
// Should dupe to `separatorF11` because the positions are the same.
|
|
guid: "separatorFFF",
|
|
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
// Shouldn't dupe to `separatorE11`, because the positions are different.
|
|
guid: "separatorEEE",
|
|
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
{
|
|
// Shouldn't dupe to `bookmarkC222` because the parents are different.
|
|
guid: "bookmarkCCCC",
|
|
url: "http://example.com/c",
|
|
title: "C",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
{
|
|
// Should dupe to `queryD111111`.
|
|
guid: "queryDDDDDDD",
|
|
url: "place:maxResults=10&sort=8",
|
|
title: "Most Visited",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
},
|
|
],
|
|
});
|
|
|
|
// Make sure we still dedupe this even though it doesn't have SYNC_STATUS.NEW
|
|
PlacesTestUtils.setBookmarkSyncFields({
|
|
guid: "folderBBBBBB",
|
|
syncStatus: PlacesUtils.bookmarks.SYNC_STATUS.UNKNOWN,
|
|
});
|
|
|
|
// Not a candidate for `bookmarkH111` because we didn't dupe `folderAAAAAA`.
|
|
await PlacesUtils.bookmarks.insert({
|
|
parentGuid: "folderAAAAAA",
|
|
guid: "bookmarkHHHH",
|
|
url: "http://example.com/h",
|
|
title: "H",
|
|
dateAdded: localModified,
|
|
lastModified: localModified,
|
|
});
|
|
|
|
info("Make remote changes");
|
|
await storeRecords(
|
|
buf,
|
|
shuffle([
|
|
{
|
|
id: "menu",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: [
|
|
"folderAAAAAA",
|
|
"folderB11111",
|
|
"folderA11111",
|
|
"separatorE11",
|
|
"queryD111111",
|
|
],
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "folderB11111",
|
|
parentid: "menu",
|
|
type: "folder",
|
|
title: "B",
|
|
children: ["bookmarkC222", "separatorF11"],
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "bookmarkC222",
|
|
parentid: "folderB11111",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/c",
|
|
title: "C",
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "separatorF11",
|
|
parentid: "folderB11111",
|
|
type: "separator",
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "folderA11111",
|
|
parentid: "menu",
|
|
type: "folder",
|
|
title: "A",
|
|
children: ["bookmarkG111"],
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "bookmarkG111",
|
|
parentid: "folderA11111",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/g",
|
|
title: "G",
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "separatorE11",
|
|
parentid: "menu",
|
|
type: "separator",
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
{
|
|
id: "queryD111111",
|
|
parentid: "menu",
|
|
type: "query",
|
|
bmkUri: "place:maxResults=10&sort=8",
|
|
title: "Most Visited",
|
|
dateAdded: localModified.getTime(),
|
|
modified: localModified / 1000 + 5,
|
|
},
|
|
])
|
|
);
|
|
|
|
info("Apply remote");
|
|
let changesToUpload = await buf.apply({
|
|
remoteTimeSeconds: localModified / 1000,
|
|
});
|
|
deepEqual(
|
|
await buf.fetchUnmergedGuids(),
|
|
[PlacesUtils.bookmarks.menuGuid],
|
|
"Should leave menu with new remote structure unmerged"
|
|
);
|
|
|
|
let idsToUpload = inspectChangeRecords(changesToUpload);
|
|
deepEqual(
|
|
idsToUpload,
|
|
{
|
|
updated: [
|
|
"bookmarkCCCC",
|
|
"bookmarkHHHH",
|
|
"folderAAAAAA",
|
|
"menu",
|
|
"separatorEEE",
|
|
],
|
|
deleted: [],
|
|
},
|
|
"Should not upload deduped local records"
|
|
);
|
|
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.rootGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.rootGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: "",
|
|
children: [
|
|
{
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: BookmarksMenuTitle,
|
|
children: [
|
|
{
|
|
guid: "folderAAAAAA",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: "A",
|
|
children: [
|
|
{
|
|
guid: "bookmarkGGGG",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "G",
|
|
url: "http://example.com/g",
|
|
},
|
|
{
|
|
guid: "bookmarkHHHH",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 1,
|
|
title: "H",
|
|
url: "http://example.com/h",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
guid: "folderB11111",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 1,
|
|
title: "B",
|
|
children: [
|
|
{
|
|
guid: "bookmarkC222",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "C",
|
|
url: "http://example.com/c",
|
|
},
|
|
{
|
|
guid: "separatorF11",
|
|
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
|
index: 1,
|
|
title: "",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
guid: "folderA11111",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 2,
|
|
title: "A",
|
|
children: [
|
|
{
|
|
guid: "bookmarkG111",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "G",
|
|
url: "http://example.com/g",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
guid: "separatorE11",
|
|
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
|
index: 3,
|
|
title: "",
|
|
},
|
|
{
|
|
guid: "queryD111111",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 4,
|
|
title: "Most Visited",
|
|
url: "place:maxResults=10&sort=8",
|
|
},
|
|
{
|
|
guid: "separatorEEE",
|
|
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
|
index: 5,
|
|
title: "",
|
|
},
|
|
{
|
|
guid: "bookmarkCCCC",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 6,
|
|
title: "C",
|
|
url: "http://example.com/c",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
guid: PlacesUtils.bookmarks.toolbarGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 1,
|
|
title: BookmarksToolbarTitle,
|
|
},
|
|
{
|
|
guid: PlacesUtils.bookmarks.unfiledGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 3,
|
|
title: UnfiledBookmarksTitle,
|
|
},
|
|
{
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 4,
|
|
title: MobileBookmarksTitle,
|
|
},
|
|
],
|
|
},
|
|
"Should dedupe matching NEW bookmarks"
|
|
);
|
|
|
|
ok(
|
|
(
|
|
await PlacesTestUtils.fetchBookmarkSyncFields(
|
|
"menu________",
|
|
"folderB11111",
|
|
"bookmarkC222",
|
|
"separatorF11",
|
|
"folderA11111",
|
|
"bookmarkG111",
|
|
"separatorE11",
|
|
"queryD111111"
|
|
)
|
|
).every(info => info.syncStatus == PlacesUtils.bookmarks.SYNC_STATUS.NORMAL)
|
|
);
|
|
|
|
await storeChangesInMirror(buf, changesToUpload);
|
|
deepEqual(await buf.fetchUnmergedGuids(), [], "Should merge all items");
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|
|
|
|
add_task(async function test_duping_both() {
|
|
let buf = await openMirror("duping_both");
|
|
let now = Date.now();
|
|
|
|
info("Start with empty mirror");
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
info("Add local dupes");
|
|
await PlacesUtils.bookmarks.insertTree({
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
children: [
|
|
{
|
|
// `folderAAAAA1` is older than `folderAAAAAA`, but we should still flag
|
|
// it for upload because it has a new structure (`bookmarkCCCC`).
|
|
guid: "folderAAAAA1",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "A",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now - 5000),
|
|
children: [
|
|
{
|
|
// Shouldn't upload, since `bookmarkBBBB` is newer.
|
|
guid: "bookmarkBBB1",
|
|
title: "B",
|
|
url: "http://example.com/b",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now - 5000),
|
|
},
|
|
{
|
|
// Should upload, since `bookmarkCCCC` doesn't exist on the server and
|
|
// has no content matches.
|
|
guid: "bookmarkCCCC",
|
|
title: "C",
|
|
url: "http://example.com/c",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now - 5000),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
// `folderDDDDD1` should keep complete local structure, but we'll still
|
|
// flag it for reupload because it's newer than `folderDDDDDD`.
|
|
guid: "folderDDDDD1",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "D",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now + 5000),
|
|
children: [
|
|
{
|
|
guid: "bookmarkEEE1",
|
|
title: "E",
|
|
url: "http://example.com/e",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now - 5000),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
// `folderFFFFF1` should keep complete remote value and structure, so
|
|
// we shouldn't upload it or its children.
|
|
guid: "folderFFFFF1",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "F",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now - 5000),
|
|
children: [
|
|
{
|
|
guid: "bookmarkGGG1",
|
|
title: "G",
|
|
url: "http://example.com/g",
|
|
dateAdded: new Date(now - 10000),
|
|
lastModified: new Date(now - 5000),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
info("Add remote dupes");
|
|
await storeRecords(buf, [
|
|
{
|
|
id: "menu",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["folderAAAAAA", "folderDDDDDD", "folderFFFFFF"],
|
|
},
|
|
{
|
|
id: "folderAAAAAA",
|
|
parentid: "menu",
|
|
type: "folder",
|
|
title: "A",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 + 5,
|
|
children: ["bookmarkBBBB"],
|
|
},
|
|
{
|
|
id: "bookmarkBBBB",
|
|
parentid: "folderAAAAAA",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/b",
|
|
title: "B",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 + 5,
|
|
},
|
|
{
|
|
id: "folderDDDDDD",
|
|
parentid: "menu",
|
|
type: "folder",
|
|
title: "D",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 - 5,
|
|
children: ["bookmarkEEEE"],
|
|
},
|
|
{
|
|
id: "bookmarkEEEE",
|
|
parentid: "folderDDDDDD",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/e",
|
|
title: "E",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 + 5,
|
|
},
|
|
{
|
|
id: "folderFFFFFF",
|
|
parentid: "menu",
|
|
type: "folder",
|
|
title: "F",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 + 5,
|
|
children: ["bookmarkGGGG", "bookmarkHHHH"],
|
|
},
|
|
{
|
|
id: "bookmarkGGGG",
|
|
parentid: "folderFFFFFF",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/g",
|
|
title: "G",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 - 5,
|
|
},
|
|
{
|
|
id: "bookmarkHHHH",
|
|
parentid: "folderFFFFFF",
|
|
type: "bookmark",
|
|
bmkUri: "http://example.com/h",
|
|
title: "H",
|
|
dateAdded: now - 10000,
|
|
modified: now / 1000 + 5,
|
|
},
|
|
]);
|
|
|
|
info("Apply remote");
|
|
let changesToUpload = await buf.apply({
|
|
remoteTimeSeconds: now / 1000,
|
|
});
|
|
|
|
let menuInfo = await PlacesUtils.bookmarks.fetch(
|
|
PlacesUtils.bookmarks.menuGuid
|
|
);
|
|
deepEqual(
|
|
changesToUpload,
|
|
{
|
|
menu: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "menu",
|
|
type: "folder",
|
|
parentid: "places",
|
|
hasDupe: true,
|
|
parentName: "",
|
|
dateAdded: menuInfo.dateAdded.getTime(),
|
|
title: menuInfo.title,
|
|
children: ["folderAAAAAA", "folderDDDDDD", "folderFFFFFF"],
|
|
},
|
|
},
|
|
folderAAAAAA: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "folderAAAAAA",
|
|
type: "folder",
|
|
parentid: "menu",
|
|
hasDupe: true,
|
|
parentName: menuInfo.title,
|
|
dateAdded: now - 10000,
|
|
title: "A",
|
|
children: ["bookmarkBBBB", "bookmarkCCCC"],
|
|
},
|
|
},
|
|
bookmarkCCCC: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "bookmarkCCCC",
|
|
type: "bookmark",
|
|
parentid: "folderAAAAAA",
|
|
hasDupe: true,
|
|
parentName: "A",
|
|
dateAdded: now - 10000,
|
|
title: "C",
|
|
bmkUri: "http://example.com/c",
|
|
},
|
|
},
|
|
folderDDDDDD: {
|
|
tombstone: false,
|
|
counter: 1,
|
|
synced: false,
|
|
cleartext: {
|
|
id: "folderDDDDDD",
|
|
type: "folder",
|
|
parentid: "menu",
|
|
hasDupe: true,
|
|
parentName: menuInfo.title,
|
|
dateAdded: now - 10000,
|
|
title: "D",
|
|
children: ["bookmarkEEEE"],
|
|
},
|
|
},
|
|
},
|
|
"Should upload new and newer locally deduped items"
|
|
);
|
|
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.menuGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: BookmarksMenuTitle,
|
|
children: [
|
|
{
|
|
guid: "folderAAAAAA",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: "A",
|
|
children: [
|
|
{
|
|
guid: "bookmarkBBBB",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "B",
|
|
url: "http://example.com/b",
|
|
},
|
|
{
|
|
guid: "bookmarkCCCC",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 1,
|
|
title: "C",
|
|
url: "http://example.com/c",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
guid: "folderDDDDDD",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 1,
|
|
title: "D",
|
|
children: [
|
|
{
|
|
guid: "bookmarkEEEE",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "E",
|
|
url: "http://example.com/e",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
guid: "folderFFFFFF",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 2,
|
|
title: "F",
|
|
children: [
|
|
{
|
|
guid: "bookmarkGGGG",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "G",
|
|
url: "http://example.com/g",
|
|
},
|
|
{
|
|
guid: "bookmarkHHHH",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 1,
|
|
title: "H",
|
|
url: "http://example.com/h",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"Should change local GUIDs for mixed older and newer items"
|
|
);
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|
|
|
|
add_task(async function test_applying_two_empty_folders_doesnt_smush() {
|
|
let buf = await openMirror("applying_two_empty_folders_doesnt_smush");
|
|
|
|
info("Set up empty mirror");
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
info("Make remote changes");
|
|
await storeRecords(
|
|
buf,
|
|
shuffle([
|
|
{
|
|
id: "mobile",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["emptyempty01", "emptyempty02"],
|
|
},
|
|
{
|
|
id: "emptyempty01",
|
|
parentid: "mobile",
|
|
type: "folder",
|
|
title: "Empty",
|
|
},
|
|
{
|
|
id: "emptyempty02",
|
|
parentid: "mobile",
|
|
type: "folder",
|
|
title: "Empty",
|
|
},
|
|
])
|
|
);
|
|
|
|
info("Apply remote");
|
|
let changesToUpload = await buf.apply();
|
|
deepEqual(await buf.fetchUnmergedGuids(), [], "Should merge all items");
|
|
|
|
let idsToUpload = inspectChangeRecords(changesToUpload);
|
|
deepEqual(
|
|
idsToUpload,
|
|
{
|
|
updated: [],
|
|
deleted: [],
|
|
},
|
|
"Should not upload records for remote-only value changes"
|
|
);
|
|
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.mobileGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 4,
|
|
title: "mobile",
|
|
children: [
|
|
{
|
|
guid: "emptyempty01",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: "Empty",
|
|
},
|
|
{
|
|
guid: "emptyempty02",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 1,
|
|
title: "Empty",
|
|
},
|
|
],
|
|
},
|
|
"Should not smush 1 and 2"
|
|
);
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|
|
|
|
add_task(async function test_applying_two_empty_folders_matches_only_one() {
|
|
let buf = await openMirror("applying_two_empty_folders_doesnt_smush");
|
|
|
|
info("Set up empty mirror");
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
info("Make local changes");
|
|
await PlacesUtils.bookmarks.insertTree({
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
children: [
|
|
{
|
|
guid: "emptyempty02",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "Empty",
|
|
},
|
|
{
|
|
guid: "emptyemptyL0",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
title: "Empty",
|
|
},
|
|
],
|
|
});
|
|
|
|
info("Make remote changes");
|
|
await storeRecords(
|
|
buf,
|
|
shuffle([
|
|
{
|
|
id: "mobile",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["emptyempty01", "emptyempty02", "emptyempty03"],
|
|
},
|
|
{
|
|
id: "emptyempty01",
|
|
parentid: "mobile",
|
|
type: "folder",
|
|
title: "Empty",
|
|
},
|
|
{
|
|
id: "emptyempty02",
|
|
parentid: "mobile",
|
|
type: "folder",
|
|
title: "Empty",
|
|
},
|
|
{
|
|
id: "emptyempty03",
|
|
parentid: "mobile",
|
|
type: "folder",
|
|
title: "Empty",
|
|
},
|
|
])
|
|
);
|
|
|
|
info("Apply remote");
|
|
let changesToUpload = await buf.apply();
|
|
deepEqual(
|
|
await buf.fetchUnmergedGuids(),
|
|
[PlacesUtils.bookmarks.mobileGuid],
|
|
"Should leave mobile with new remote structure unmerged"
|
|
);
|
|
|
|
let idsToUpload = inspectChangeRecords(changesToUpload);
|
|
deepEqual(
|
|
idsToUpload,
|
|
{
|
|
updated: ["mobile"],
|
|
deleted: [],
|
|
},
|
|
"Should not upload records after applying empty folders"
|
|
);
|
|
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.mobileGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 4,
|
|
title: "mobile",
|
|
children: [
|
|
{
|
|
guid: "emptyempty01",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: "Empty",
|
|
},
|
|
{
|
|
guid: "emptyempty02",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 1,
|
|
title: "Empty",
|
|
},
|
|
{
|
|
guid: "emptyempty03",
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 2,
|
|
title: "Empty",
|
|
},
|
|
],
|
|
},
|
|
"Should apply 1 and dedupe L0 to 3"
|
|
);
|
|
|
|
await storeChangesInMirror(buf, changesToUpload);
|
|
deepEqual(await buf.fetchUnmergedGuids(), [], "Should merge all items");
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|
|
|
|
// Bug 747699.
|
|
add_task(async function test_duping_mobile_bookmarks() {
|
|
let buf = await openMirror("duping_mobile_bookmarks");
|
|
|
|
info("Set up empty mirror with localized mobile root title");
|
|
let mobileInfo = await PlacesUtils.bookmarks.fetch(
|
|
PlacesUtils.bookmarks.mobileGuid
|
|
);
|
|
await PlacesUtils.bookmarks.update({
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
title: "Favoritos do celular",
|
|
});
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
info("Make local changes");
|
|
await PlacesUtils.bookmarks.insert({
|
|
guid: "bookmarkAAA1",
|
|
parentGuid: PlacesUtils.bookmarks.mobileGuid,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
});
|
|
|
|
info("Make remote changes");
|
|
await storeRecords(
|
|
buf,
|
|
shuffle([
|
|
{
|
|
id: "mobile",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["bookmarkAAAA"],
|
|
},
|
|
{
|
|
id: "bookmarkAAAA",
|
|
parentid: "mobile",
|
|
type: "bookmark",
|
|
title: "A",
|
|
bmkUri: "http://example.com/a",
|
|
},
|
|
])
|
|
);
|
|
|
|
info("Apply remote");
|
|
let changesToUpload = await buf.apply();
|
|
deepEqual(
|
|
await buf.fetchUnmergedGuids(),
|
|
[PlacesUtils.bookmarks.mobileGuid],
|
|
"Should leave mobile with new remote structure unmerged"
|
|
);
|
|
|
|
let idsToUpload = inspectChangeRecords(changesToUpload);
|
|
deepEqual(
|
|
idsToUpload,
|
|
{
|
|
updated: ["mobile"],
|
|
deleted: [],
|
|
},
|
|
"Should not upload records after applying deduped mobile bookmark"
|
|
);
|
|
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.mobileGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 4,
|
|
title: "Favoritos do celular",
|
|
children: [
|
|
{
|
|
guid: "bookmarkAAAA",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
],
|
|
},
|
|
"Should dedupe A1 to A with different parent title"
|
|
);
|
|
|
|
await storeChangesInMirror(buf, changesToUpload);
|
|
deepEqual(await buf.fetchUnmergedGuids(), [], "Should merge all items");
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
// Restore the original mobile root title.
|
|
await PlacesUtils.bookmarks.update({
|
|
guid: PlacesUtils.bookmarks.mobileGuid,
|
|
title: mobileInfo.title,
|
|
});
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|
|
|
|
add_task(async function test_duping_invalid() {
|
|
// To check if invalid items are prevented from deduping
|
|
|
|
info("Set up empty mirror");
|
|
await PlacesTestUtils.markBookmarksAsSynced();
|
|
|
|
await PlacesUtils.bookmarks.insertTree({
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
children: [
|
|
{
|
|
guid: "bookmarkAAA1",
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
],
|
|
});
|
|
|
|
let buf = await openMirror("duping_invalid");
|
|
await storeRecords(buf, [
|
|
{
|
|
id: "menu",
|
|
parentid: "places",
|
|
type: "folder",
|
|
children: ["bookmarkAAA2"],
|
|
},
|
|
{
|
|
id: "bookmarkAAA2",
|
|
parentid: "menu",
|
|
type: "bookmark",
|
|
title: "A",
|
|
bmkUri: "http://example.com/a",
|
|
},
|
|
]);
|
|
|
|
// Invalidate bookmarkAAA2 so that it does not dedupe to bookmarkAAA1
|
|
await buf.db.execute(
|
|
`UPDATE items SET
|
|
validity = :validity
|
|
WHERE guid = :guid`,
|
|
{
|
|
validity: Ci.mozISyncedBookmarksMerger.VALIDITY_REPLACE,
|
|
guid: "bookmarkAAA2",
|
|
}
|
|
);
|
|
|
|
let changesToUpload = await buf.apply();
|
|
deepEqual(
|
|
changesToUpload.menu.cleartext.children,
|
|
["bookmarkAAA1"],
|
|
"Should upload A1 in menu"
|
|
);
|
|
ok(
|
|
!changesToUpload.bookmarkAAA1.tombstone,
|
|
"Should not upload tombstone for A1"
|
|
);
|
|
ok(changesToUpload.bookmarkAAA2.tombstone, "Should upload tombstone for A2");
|
|
await assertLocalTree(
|
|
PlacesUtils.bookmarks.menuGuid,
|
|
{
|
|
guid: PlacesUtils.bookmarks.menuGuid,
|
|
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
|
index: 0,
|
|
title: BookmarksMenuTitle,
|
|
children: [
|
|
{
|
|
guid: "bookmarkAAA1",
|
|
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
|
index: 0,
|
|
title: "A",
|
|
url: "http://example.com/a",
|
|
},
|
|
],
|
|
},
|
|
"No deduping of invalid items"
|
|
);
|
|
|
|
await buf.finalize();
|
|
await PlacesUtils.bookmarks.eraseEverything();
|
|
await PlacesSyncUtils.bookmarks.reset();
|
|
});
|