Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | "use strict"; |
michael@0 | 5 | |
michael@0 | 6 | Cu.import("resource://gre/modules/AddonManager.jsm"); |
michael@0 | 7 | Cu.import("resource://gre/modules/Preferences.jsm"); |
michael@0 | 8 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 9 | Cu.import("resource://services-common/async.js"); |
michael@0 | 10 | Cu.import("resource://services-sync/addonsreconciler.js"); |
michael@0 | 11 | Cu.import("resource://services-sync/engines/addons.js"); |
michael@0 | 12 | Cu.import("resource://services-sync/service.js"); |
michael@0 | 13 | Cu.import("resource://services-sync/util.js"); |
michael@0 | 14 | Cu.import("resource://testing-common/services/sync/utils.js"); |
michael@0 | 15 | |
michael@0 | 16 | let prefs = new Preferences(); |
michael@0 | 17 | prefs.set("extensions.getAddons.get.url", |
michael@0 | 18 | "http://localhost:8888/search/guid:%IDS%"); |
michael@0 | 19 | |
michael@0 | 20 | loadAddonTestFunctions(); |
michael@0 | 21 | startupManager(); |
michael@0 | 22 | |
michael@0 | 23 | let engineManager = Service.engineManager; |
michael@0 | 24 | |
michael@0 | 25 | engineManager.register(AddonsEngine); |
michael@0 | 26 | let engine = engineManager.get("addons"); |
michael@0 | 27 | let reconciler = engine._reconciler; |
michael@0 | 28 | let tracker = engine._tracker; |
michael@0 | 29 | |
michael@0 | 30 | function advance_test() { |
michael@0 | 31 | reconciler._addons = {}; |
michael@0 | 32 | reconciler._changes = []; |
michael@0 | 33 | |
michael@0 | 34 | let cb = Async.makeSpinningCallback(); |
michael@0 | 35 | reconciler.saveState(null, cb); |
michael@0 | 36 | cb.wait(); |
michael@0 | 37 | |
michael@0 | 38 | Svc.Prefs.reset("addons.ignoreRepositoryChecking"); |
michael@0 | 39 | |
michael@0 | 40 | run_next_test(); |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | // This is a basic sanity test for the unit test itself. If this breaks, the |
michael@0 | 44 | // add-ons API likely changed upstream. |
michael@0 | 45 | add_test(function test_addon_install() { |
michael@0 | 46 | _("Ensure basic add-on APIs work as expected."); |
michael@0 | 47 | |
michael@0 | 48 | let install = getAddonInstall("test_bootstrap1_1"); |
michael@0 | 49 | do_check_neq(install, null); |
michael@0 | 50 | do_check_eq(install.type, "extension"); |
michael@0 | 51 | do_check_eq(install.name, "Test Bootstrap 1"); |
michael@0 | 52 | |
michael@0 | 53 | advance_test(); |
michael@0 | 54 | }); |
michael@0 | 55 | |
michael@0 | 56 | add_test(function test_find_dupe() { |
michael@0 | 57 | _("Ensure the _findDupe() implementation is sane."); |
michael@0 | 58 | |
michael@0 | 59 | // This gets invoked at the top of sync, which is bypassed by this |
michael@0 | 60 | // test, so we do it manually. |
michael@0 | 61 | engine._refreshReconcilerState(); |
michael@0 | 62 | |
michael@0 | 63 | let addon = installAddon("test_bootstrap1_1"); |
michael@0 | 64 | |
michael@0 | 65 | let record = { |
michael@0 | 66 | id: Utils.makeGUID(), |
michael@0 | 67 | addonID: addon.id, |
michael@0 | 68 | enabled: true, |
michael@0 | 69 | applicationID: Services.appinfo.ID, |
michael@0 | 70 | source: "amo" |
michael@0 | 71 | }; |
michael@0 | 72 | |
michael@0 | 73 | let dupe = engine._findDupe(record); |
michael@0 | 74 | do_check_eq(addon.syncGUID, dupe); |
michael@0 | 75 | |
michael@0 | 76 | record.id = addon.syncGUID; |
michael@0 | 77 | dupe = engine._findDupe(record); |
michael@0 | 78 | do_check_eq(null, dupe); |
michael@0 | 79 | |
michael@0 | 80 | uninstallAddon(addon); |
michael@0 | 81 | advance_test(); |
michael@0 | 82 | }); |
michael@0 | 83 | |
michael@0 | 84 | add_test(function test_get_changed_ids() { |
michael@0 | 85 | _("Ensure getChangedIDs() has the appropriate behavior."); |
michael@0 | 86 | |
michael@0 | 87 | _("Ensure getChangedIDs() returns an empty object by default."); |
michael@0 | 88 | let changes = engine.getChangedIDs(); |
michael@0 | 89 | do_check_eq("object", typeof(changes)); |
michael@0 | 90 | do_check_eq(0, Object.keys(changes).length); |
michael@0 | 91 | |
michael@0 | 92 | _("Ensure tracker changes are populated."); |
michael@0 | 93 | let now = new Date(); |
michael@0 | 94 | let changeTime = now.getTime() / 1000; |
michael@0 | 95 | let guid1 = Utils.makeGUID(); |
michael@0 | 96 | tracker.addChangedID(guid1, changeTime); |
michael@0 | 97 | |
michael@0 | 98 | changes = engine.getChangedIDs(); |
michael@0 | 99 | do_check_eq("object", typeof(changes)); |
michael@0 | 100 | do_check_eq(1, Object.keys(changes).length); |
michael@0 | 101 | do_check_true(guid1 in changes); |
michael@0 | 102 | do_check_eq(changeTime, changes[guid1]); |
michael@0 | 103 | |
michael@0 | 104 | tracker.clearChangedIDs(); |
michael@0 | 105 | |
michael@0 | 106 | _("Ensure reconciler changes are populated."); |
michael@0 | 107 | Svc.Prefs.set("addons.ignoreRepositoryChecking", true); |
michael@0 | 108 | let addon = installAddon("test_bootstrap1_1"); |
michael@0 | 109 | tracker.clearChangedIDs(); // Just in case. |
michael@0 | 110 | changes = engine.getChangedIDs(); |
michael@0 | 111 | do_check_eq("object", typeof(changes)); |
michael@0 | 112 | do_check_eq(1, Object.keys(changes).length); |
michael@0 | 113 | do_check_true(addon.syncGUID in changes); |
michael@0 | 114 | _("Change time: " + changeTime + ", addon change: " + changes[addon.syncGUID]); |
michael@0 | 115 | do_check_true(changes[addon.syncGUID] >= changeTime); |
michael@0 | 116 | |
michael@0 | 117 | let oldTime = changes[addon.syncGUID]; |
michael@0 | 118 | let guid2 = addon.syncGUID; |
michael@0 | 119 | uninstallAddon(addon); |
michael@0 | 120 | changes = engine.getChangedIDs(); |
michael@0 | 121 | do_check_eq(1, Object.keys(changes).length); |
michael@0 | 122 | do_check_true(guid2 in changes); |
michael@0 | 123 | do_check_true(changes[guid2] > oldTime); |
michael@0 | 124 | |
michael@0 | 125 | _("Ensure non-syncable add-ons aren't picked up by reconciler changes."); |
michael@0 | 126 | reconciler._addons = {}; |
michael@0 | 127 | reconciler._changes = []; |
michael@0 | 128 | let record = { |
michael@0 | 129 | id: "DUMMY", |
michael@0 | 130 | guid: Utils.makeGUID(), |
michael@0 | 131 | enabled: true, |
michael@0 | 132 | installed: true, |
michael@0 | 133 | modified: new Date(), |
michael@0 | 134 | type: "UNSUPPORTED", |
michael@0 | 135 | scope: 0, |
michael@0 | 136 | foreignInstall: false |
michael@0 | 137 | }; |
michael@0 | 138 | reconciler.addons["DUMMY"] = record; |
michael@0 | 139 | reconciler._addChange(record.modified, CHANGE_INSTALLED, record); |
michael@0 | 140 | |
michael@0 | 141 | changes = engine.getChangedIDs(); |
michael@0 | 142 | _(JSON.stringify(changes)); |
michael@0 | 143 | do_check_eq(0, Object.keys(changes).length); |
michael@0 | 144 | |
michael@0 | 145 | advance_test(); |
michael@0 | 146 | }); |
michael@0 | 147 | |
michael@0 | 148 | add_test(function test_disabled_install_semantics() { |
michael@0 | 149 | _("Ensure that syncing a disabled add-on preserves proper state."); |
michael@0 | 150 | |
michael@0 | 151 | // This is essentially a test for bug 712542, which snuck into the original |
michael@0 | 152 | // add-on sync drop. It ensures that when an add-on is installed that the |
michael@0 | 153 | // disabled state and incoming syncGUID is preserved, even on the next sync. |
michael@0 | 154 | |
michael@0 | 155 | Svc.Prefs.set("addons.ignoreRepositoryChecking", true); |
michael@0 | 156 | |
michael@0 | 157 | const USER = "foo"; |
michael@0 | 158 | const PASSWORD = "password"; |
michael@0 | 159 | const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea"; |
michael@0 | 160 | const ADDON_ID = "addon1@tests.mozilla.org"; |
michael@0 | 161 | |
michael@0 | 162 | let server = new SyncServer(); |
michael@0 | 163 | server.start(); |
michael@0 | 164 | new SyncTestingInfrastructure(server.server, USER, PASSWORD, PASSPHRASE); |
michael@0 | 165 | |
michael@0 | 166 | generateNewKeys(Service.collectionKeys); |
michael@0 | 167 | |
michael@0 | 168 | let contents = { |
michael@0 | 169 | meta: {global: {engines: {addons: {version: engine.version, |
michael@0 | 170 | syncID: engine.syncID}}}}, |
michael@0 | 171 | crypto: {}, |
michael@0 | 172 | addons: {} |
michael@0 | 173 | }; |
michael@0 | 174 | |
michael@0 | 175 | server.registerUser(USER, "password"); |
michael@0 | 176 | server.createContents(USER, contents); |
michael@0 | 177 | |
michael@0 | 178 | let amoServer = new HttpServer(); |
michael@0 | 179 | amoServer.registerFile("/search/guid:addon1%40tests.mozilla.org", |
michael@0 | 180 | do_get_file("addon1-search.xml")); |
michael@0 | 181 | |
michael@0 | 182 | let installXPI = ExtensionsTestPath("/addons/test_install1.xpi"); |
michael@0 | 183 | amoServer.registerFile("/addon1.xpi", do_get_file(installXPI)); |
michael@0 | 184 | amoServer.start(8888); |
michael@0 | 185 | |
michael@0 | 186 | // Insert an existing record into the server. |
michael@0 | 187 | let id = Utils.makeGUID(); |
michael@0 | 188 | let now = Date.now() / 1000; |
michael@0 | 189 | |
michael@0 | 190 | let record = encryptPayload({ |
michael@0 | 191 | id: id, |
michael@0 | 192 | applicationID: Services.appinfo.ID, |
michael@0 | 193 | addonID: ADDON_ID, |
michael@0 | 194 | enabled: false, |
michael@0 | 195 | deleted: false, |
michael@0 | 196 | source: "amo", |
michael@0 | 197 | }); |
michael@0 | 198 | let wbo = new ServerWBO(id, record, now - 2); |
michael@0 | 199 | server.insertWBO(USER, "addons", wbo); |
michael@0 | 200 | |
michael@0 | 201 | _("Performing sync of add-ons engine."); |
michael@0 | 202 | engine._sync(); |
michael@0 | 203 | |
michael@0 | 204 | // At this point the non-restartless extension should be staged for install. |
michael@0 | 205 | |
michael@0 | 206 | // Don't need this server any more. |
michael@0 | 207 | let cb = Async.makeSpinningCallback(); |
michael@0 | 208 | amoServer.stop(cb); |
michael@0 | 209 | cb.wait(); |
michael@0 | 210 | |
michael@0 | 211 | // We ensure the reconciler has recorded the proper ID and enabled state. |
michael@0 | 212 | let addon = reconciler.getAddonStateFromSyncGUID(id); |
michael@0 | 213 | do_check_neq(null, addon); |
michael@0 | 214 | do_check_eq(false, addon.enabled); |
michael@0 | 215 | |
michael@0 | 216 | // We fake an app restart and perform another sync, just to make sure things |
michael@0 | 217 | // are sane. |
michael@0 | 218 | restartManager(); |
michael@0 | 219 | |
michael@0 | 220 | engine._sync(); |
michael@0 | 221 | |
michael@0 | 222 | // The client should not upload a new record. The old record should be |
michael@0 | 223 | // retained and unmodified. |
michael@0 | 224 | let collection = server.getCollection(USER, "addons"); |
michael@0 | 225 | do_check_eq(1, collection.count()); |
michael@0 | 226 | |
michael@0 | 227 | let payload = collection.payloads()[0]; |
michael@0 | 228 | do_check_neq(null, collection.wbo(id)); |
michael@0 | 229 | do_check_eq(ADDON_ID, payload.addonID); |
michael@0 | 230 | do_check_false(payload.enabled); |
michael@0 | 231 | |
michael@0 | 232 | server.stop(advance_test); |
michael@0 | 233 | }); |
michael@0 | 234 | |
michael@0 | 235 | add_test(function cleanup() { |
michael@0 | 236 | // There's an xpcom-shutdown hook for this, but let's give this a shot. |
michael@0 | 237 | reconciler.stopListening(); |
michael@0 | 238 | run_next_test(); |
michael@0 | 239 | }); |
michael@0 | 240 | |
michael@0 | 241 | function run_test() { |
michael@0 | 242 | initTestLogging("Trace"); |
michael@0 | 243 | Log.repository.getLogger("Sync.Engine.Addons").level = |
michael@0 | 244 | Log.Level.Trace; |
michael@0 | 245 | Log.repository.getLogger("Sync.Store.Addons").level = Log.Level.Trace; |
michael@0 | 246 | Log.repository.getLogger("Sync.Tracker.Addons").level = |
michael@0 | 247 | Log.Level.Trace; |
michael@0 | 248 | Log.repository.getLogger("Sync.AddonsRepository").level = |
michael@0 | 249 | Log.Level.Trace; |
michael@0 | 250 | |
michael@0 | 251 | reconciler.startListening(); |
michael@0 | 252 | |
michael@0 | 253 | // Don't flush to disk in the middle of an event listener! |
michael@0 | 254 | // This causes test hangs on WinXP. |
michael@0 | 255 | reconciler._shouldPersist = false; |
michael@0 | 256 | |
michael@0 | 257 | advance_test(); |
michael@0 | 258 | } |