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