michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: "use strict"; michael@0: michael@0: Cu.import("resource://gre/modules/AddonManager.jsm"); michael@0: Cu.import("resource://gre/modules/Preferences.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://services-common/async.js"); michael@0: Cu.import("resource://services-sync/addonsreconciler.js"); michael@0: Cu.import("resource://services-sync/engines/addons.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: let prefs = new Preferences(); michael@0: prefs.set("extensions.getAddons.get.url", michael@0: "http://localhost:8888/search/guid:%IDS%"); michael@0: michael@0: loadAddonTestFunctions(); michael@0: startupManager(); michael@0: michael@0: let engineManager = Service.engineManager; michael@0: michael@0: engineManager.register(AddonsEngine); michael@0: let engine = engineManager.get("addons"); michael@0: let reconciler = engine._reconciler; michael@0: let tracker = engine._tracker; michael@0: michael@0: function advance_test() { michael@0: reconciler._addons = {}; michael@0: reconciler._changes = []; michael@0: michael@0: let cb = Async.makeSpinningCallback(); michael@0: reconciler.saveState(null, cb); michael@0: cb.wait(); michael@0: michael@0: Svc.Prefs.reset("addons.ignoreRepositoryChecking"); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: // This is a basic sanity test for the unit test itself. If this breaks, the michael@0: // add-ons API likely changed upstream. michael@0: add_test(function test_addon_install() { michael@0: _("Ensure basic add-on APIs work as expected."); michael@0: michael@0: let install = getAddonInstall("test_bootstrap1_1"); michael@0: do_check_neq(install, null); michael@0: do_check_eq(install.type, "extension"); michael@0: do_check_eq(install.name, "Test Bootstrap 1"); michael@0: michael@0: advance_test(); michael@0: }); michael@0: michael@0: add_test(function test_find_dupe() { michael@0: _("Ensure the _findDupe() implementation is sane."); michael@0: michael@0: // This gets invoked at the top of sync, which is bypassed by this michael@0: // test, so we do it manually. michael@0: engine._refreshReconcilerState(); michael@0: michael@0: let addon = installAddon("test_bootstrap1_1"); michael@0: michael@0: let record = { michael@0: id: Utils.makeGUID(), michael@0: addonID: addon.id, michael@0: enabled: true, michael@0: applicationID: Services.appinfo.ID, michael@0: source: "amo" michael@0: }; michael@0: michael@0: let dupe = engine._findDupe(record); michael@0: do_check_eq(addon.syncGUID, dupe); michael@0: michael@0: record.id = addon.syncGUID; michael@0: dupe = engine._findDupe(record); michael@0: do_check_eq(null, dupe); michael@0: michael@0: uninstallAddon(addon); michael@0: advance_test(); michael@0: }); michael@0: michael@0: add_test(function test_get_changed_ids() { michael@0: _("Ensure getChangedIDs() has the appropriate behavior."); michael@0: michael@0: _("Ensure getChangedIDs() returns an empty object by default."); michael@0: let changes = engine.getChangedIDs(); michael@0: do_check_eq("object", typeof(changes)); michael@0: do_check_eq(0, Object.keys(changes).length); michael@0: michael@0: _("Ensure tracker changes are populated."); michael@0: let now = new Date(); michael@0: let changeTime = now.getTime() / 1000; michael@0: let guid1 = Utils.makeGUID(); michael@0: tracker.addChangedID(guid1, changeTime); michael@0: michael@0: changes = engine.getChangedIDs(); michael@0: do_check_eq("object", typeof(changes)); michael@0: do_check_eq(1, Object.keys(changes).length); michael@0: do_check_true(guid1 in changes); michael@0: do_check_eq(changeTime, changes[guid1]); michael@0: michael@0: tracker.clearChangedIDs(); michael@0: michael@0: _("Ensure reconciler changes are populated."); michael@0: Svc.Prefs.set("addons.ignoreRepositoryChecking", true); michael@0: let addon = installAddon("test_bootstrap1_1"); michael@0: tracker.clearChangedIDs(); // Just in case. michael@0: changes = engine.getChangedIDs(); michael@0: do_check_eq("object", typeof(changes)); michael@0: do_check_eq(1, Object.keys(changes).length); michael@0: do_check_true(addon.syncGUID in changes); michael@0: _("Change time: " + changeTime + ", addon change: " + changes[addon.syncGUID]); michael@0: do_check_true(changes[addon.syncGUID] >= changeTime); michael@0: michael@0: let oldTime = changes[addon.syncGUID]; michael@0: let guid2 = addon.syncGUID; michael@0: uninstallAddon(addon); michael@0: changes = engine.getChangedIDs(); michael@0: do_check_eq(1, Object.keys(changes).length); michael@0: do_check_true(guid2 in changes); michael@0: do_check_true(changes[guid2] > oldTime); michael@0: michael@0: _("Ensure non-syncable add-ons aren't picked up by reconciler changes."); michael@0: reconciler._addons = {}; michael@0: reconciler._changes = []; michael@0: let record = { michael@0: id: "DUMMY", michael@0: guid: Utils.makeGUID(), michael@0: enabled: true, michael@0: installed: true, michael@0: modified: new Date(), michael@0: type: "UNSUPPORTED", michael@0: scope: 0, michael@0: foreignInstall: false michael@0: }; michael@0: reconciler.addons["DUMMY"] = record; michael@0: reconciler._addChange(record.modified, CHANGE_INSTALLED, record); michael@0: michael@0: changes = engine.getChangedIDs(); michael@0: _(JSON.stringify(changes)); michael@0: do_check_eq(0, Object.keys(changes).length); michael@0: michael@0: advance_test(); michael@0: }); michael@0: michael@0: add_test(function test_disabled_install_semantics() { michael@0: _("Ensure that syncing a disabled add-on preserves proper state."); michael@0: michael@0: // This is essentially a test for bug 712542, which snuck into the original michael@0: // add-on sync drop. It ensures that when an add-on is installed that the michael@0: // disabled state and incoming syncGUID is preserved, even on the next sync. michael@0: michael@0: Svc.Prefs.set("addons.ignoreRepositoryChecking", true); michael@0: michael@0: const USER = "foo"; michael@0: const PASSWORD = "password"; michael@0: const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea"; michael@0: const ADDON_ID = "addon1@tests.mozilla.org"; michael@0: michael@0: let server = new SyncServer(); michael@0: server.start(); michael@0: new SyncTestingInfrastructure(server.server, USER, PASSWORD, PASSPHRASE); michael@0: michael@0: generateNewKeys(Service.collectionKeys); michael@0: michael@0: let contents = { michael@0: meta: {global: {engines: {addons: {version: engine.version, michael@0: syncID: engine.syncID}}}}, michael@0: crypto: {}, michael@0: addons: {} michael@0: }; michael@0: michael@0: server.registerUser(USER, "password"); michael@0: server.createContents(USER, contents); michael@0: michael@0: let amoServer = new HttpServer(); michael@0: amoServer.registerFile("/search/guid:addon1%40tests.mozilla.org", michael@0: do_get_file("addon1-search.xml")); michael@0: michael@0: let installXPI = ExtensionsTestPath("/addons/test_install1.xpi"); michael@0: amoServer.registerFile("/addon1.xpi", do_get_file(installXPI)); michael@0: amoServer.start(8888); michael@0: michael@0: // Insert an existing record into the server. michael@0: let id = Utils.makeGUID(); michael@0: let now = Date.now() / 1000; michael@0: michael@0: let record = encryptPayload({ michael@0: id: id, michael@0: applicationID: Services.appinfo.ID, michael@0: addonID: ADDON_ID, michael@0: enabled: false, michael@0: deleted: false, michael@0: source: "amo", michael@0: }); michael@0: let wbo = new ServerWBO(id, record, now - 2); michael@0: server.insertWBO(USER, "addons", wbo); michael@0: michael@0: _("Performing sync of add-ons engine."); michael@0: engine._sync(); michael@0: michael@0: // At this point the non-restartless extension should be staged for install. michael@0: michael@0: // Don't need this server any more. michael@0: let cb = Async.makeSpinningCallback(); michael@0: amoServer.stop(cb); michael@0: cb.wait(); michael@0: michael@0: // We ensure the reconciler has recorded the proper ID and enabled state. michael@0: let addon = reconciler.getAddonStateFromSyncGUID(id); michael@0: do_check_neq(null, addon); michael@0: do_check_eq(false, addon.enabled); michael@0: michael@0: // We fake an app restart and perform another sync, just to make sure things michael@0: // are sane. michael@0: restartManager(); michael@0: michael@0: engine._sync(); michael@0: michael@0: // The client should not upload a new record. The old record should be michael@0: // retained and unmodified. michael@0: let collection = server.getCollection(USER, "addons"); michael@0: do_check_eq(1, collection.count()); michael@0: michael@0: let payload = collection.payloads()[0]; michael@0: do_check_neq(null, collection.wbo(id)); michael@0: do_check_eq(ADDON_ID, payload.addonID); michael@0: do_check_false(payload.enabled); michael@0: michael@0: server.stop(advance_test); michael@0: }); michael@0: michael@0: add_test(function cleanup() { michael@0: // There's an xpcom-shutdown hook for this, but let's give this a shot. michael@0: reconciler.stopListening(); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: function run_test() { michael@0: initTestLogging("Trace"); michael@0: Log.repository.getLogger("Sync.Engine.Addons").level = michael@0: Log.Level.Trace; michael@0: Log.repository.getLogger("Sync.Store.Addons").level = Log.Level.Trace; michael@0: Log.repository.getLogger("Sync.Tracker.Addons").level = michael@0: Log.Level.Trace; michael@0: Log.repository.getLogger("Sync.AddonsRepository").level = michael@0: Log.Level.Trace; michael@0: michael@0: reconciler.startListening(); michael@0: michael@0: // Don't flush to disk in the middle of an event listener! michael@0: // This causes test hangs on WinXP. michael@0: reconciler._shouldPersist = false; michael@0: michael@0: advance_test(); michael@0: }