Bug 1274394 - Run the bookmark validator after each phase of TPS tests that touched bookmarks. r=markh
MozReview-Commit-ID: 9WH965qdhAE
--- a/services/sync/tests/tps/test_bug563989.js
+++ b/services/sync/tests/tps/test_bug563989.js
@@ -83,17 +83,18 @@ Phase('phase1', [
]);
// Sync to profile2 and verify that the bookmarks are present. Delete
// some bookmarks, and verify that they're not present, but don't sync again.
Phase('phase2', [
[Sync],
[Bookmarks.verify, bookmarks_initial],
[Bookmarks.delete, bookmarks_to_delete],
- [Bookmarks.verifyNot, bookmarks_to_delete]
+ [Bookmarks.verifyNot, bookmarks_to_delete],
+ [Bookmarks.skipValidation]
]);
// Using profile1, sync again with wipe-server set to true. Verify our
// initial bookmarks are still all present.
Phase('phase3', [
[Sync, SYNC_WIPE_REMOTE],
[Bookmarks.verify, bookmarks_initial]
]);
--- a/services/sync/tps/extensions/tps/resource/tps.jsm
+++ b/services/sync/tps/extensions/tps/resource/tps.jsm
@@ -13,21 +13,22 @@ const {classes: Cc, interfaces: Ci, util
var module = this;
// Global modules
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://services-common/async.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/main.js");
Cu.import("resource://services-sync/util.js");
-
+Cu.import("resource://services-sync/bookmark_validator.js");
// TPS modules
Cu.import("resource://tps/logger.jsm");
// Module wrappers for tests
Cu.import("resource://tps/modules/addons.jsm");
Cu.import("resource://tps/modules/bookmarks.jsm");
Cu.import("resource://tps/modules/forms.jsm");
Cu.import("resource://tps/modules/history.jsm");
@@ -106,16 +107,17 @@ var TPS = {
_syncErrors: 0,
_syncWipeAction: null,
_tabsAdded: 0,
_tabsFinished: 0,
_test: null,
_triggeredSync: false,
_usSinceEpoch: 0,
_requestedQuit: false,
+ shouldValidateBookmarks: false,
_init: function TPS__init() {
// Check if Firefox Accounts is enabled
let service = Cc["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
this.fxaccounts_enabled = service.fxAccountsEnabled;
@@ -487,16 +489,17 @@ var TPS = {
throw new Error("Unknown action for add-on: " + action);
}
}
Logger.logPass("executing action " + action.toUpperCase() +
" on addons");
},
HandleBookmarks: function (bookmarks, action) {
+ this.shouldValidateBookmarks = true;
try {
let items = [];
for (let folder in bookmarks) {
let last_item_pos = -1;
for (let bookmark of bookmarks[folder]) {
Logger.clearPotentialError();
let placesItem;
bookmark['location'] = folder;
@@ -578,20 +581,91 @@ var TPS = {
}, 2000, this, "postmozmilltest");
}
},
MozmillSetTestListener: function TPS__MozmillSetTestListener(obj) {
Logger.logInfo("mozmill setTest: " + obj.name);
},
+
+
+ /**
+ * Use Sync's bookmark validation code to see if we've corrupted the tree.
+ */
+ ValidateBookmarks() {
+
+ let getServerBookmarkState = () => {
+ let bookmarkEngine = Weave.Service.engineManager.get('bookmarks');
+ let collection = bookmarkEngine._itemSource();
+ let collectionKey = bookmarkEngine.service.collectionKeys.keyForCollection(bookmarkEngine.name);
+ collection.full = true;
+ let items = [];
+ collection.recordHandler = function(item) {
+ item.decrypt(collectionKey);
+ items.push(item.cleartext);
+ };
+ collection.get();
+ return items;
+ };
+ let serverRecordDumpStr;
+ try {
+ Logger.logInfo("About to perform bookmark validation");
+ let clientTree = Async.promiseSpinningly(PlacesUtils.promiseBookmarksTree("", {
+ includeItemIds: true
+ }));
+ let serverRecords = getServerBookmarkState();
+ // We can't wait until catch to stringify this, since at that point it will have cycles.
+ serverRecordDumpStr = JSON.stringify(serverRecords);
+
+ let validator = new BookmarkValidator();
+ let {problemData} = validator.compareServerWithClient(serverRecords, clientTree);
+
+ for (let {name, count} of problemData.getSummary()) {
+ // Exclude mobile showing up on the server hackily so that we don't
+ // report it every time, see bug 1273234 and 1274394 for more information.
+ if (name === "serverUnexpected" && problemData.serverUnexpected.indexOf("mobile") >= 0) {
+ --count;
+ } else if (name === "differences") {
+ // Also exclude errors in parentName/wrongParentName (bug 1276969) for
+ // the same reason.
+ let newCount = problemData.differences.filter(diffInfo =>
+ !diffInfo.differences.every(diff =>
+ diff === "parentName")).length
+ count = newCount;
+ } else if (name === "wrongParentName") {
+ continue;
+ }
+ if (count) {
+ // Log this out before we assert. This is useful in the context of TPS logs, since we
+ // can see the IDs in the test files.
+ Logger.logInfo(`Validation problem: "${name}": ${JSON.stringify(problemData[name])}`);
+ }
+ Logger.AssertEqual(count, 0, `Bookmark validation error of type ${name}`);
+ }
+ } catch (e) {
+ // Dump the client records (should always be doable)
+ DumpBookmarks();
+ // Dump the server records if gotten them already.
+ if (serverRecordDumpStr) {
+ Logger.logInfo("Server bookmark records:\n" + serverRecordDumpStr + "\n");
+ }
+ this.DumpError("Bookmark validation failed", e);
+ }
+ Logger.logInfo("Bookmark validation finished");
+ },
+
RunNextTestAction: function() {
try {
if (this._currentAction >=
this._phaselist["phase" + this._currentPhase].length) {
+ if (this.shouldValidateBookmarks) {
+ // Run bookmark validation and then finish up
+ this.ValidateBookmarks();
+ }
// we're all done
Logger.logInfo("test phase " + this._currentPhase + ": " +
(this._errors ? "FAIL" : "PASS"));
this._phaseFinished = true;
this.quit();
return;
}
@@ -671,16 +745,19 @@ var TPS = {
return;
}
// Wait for Sync service to become ready.
if (!Weave.Status.ready) {
this.waitForEvent("weave:service:ready");
}
+ // We only want to do this if we modified the bookmarks this phase.
+ this.shouldValidateBookmarks = false;
+
// Always give Sync an extra tick to initialize. If we waited for the
// service:ready event, this is required to ensure all handlers have
// executed.
Utils.nextTick(this._executeTestPhase.bind(this, file, phase, settings));
} catch(e) {
this.DumpError("RunTestPhase failed", e);
return;
}
@@ -969,16 +1046,19 @@ var Bookmarks = {
delete: function Bookmarks__delete(bookmarks) {
TPS.HandleBookmarks(bookmarks, ACTION_DELETE);
},
verify: function Bookmarks__verify(bookmarks) {
TPS.HandleBookmarks(bookmarks, ACTION_VERIFY);
},
verifyNot: function Bookmarks__verifyNot(bookmarks) {
TPS.HandleBookmarks(bookmarks, ACTION_VERIFY_NOT);
+ },
+ skipValidation() {
+ TPS.shouldValidateBookmarks = false;
}
};
var Formdata = {
add: function Formdata__add(formdata) {
this.HandleForms(formdata, ACTION_ADD);
},
delete: function Formdata__delete(formdata) {