|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
5 |
|
6 Cu.import("resource://gre/modules/Services.jsm"); |
|
7 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
8 Cu.import("resource://gre/modules/Promise.jsm"); |
|
9 Cu.import("resource://gre/modules/Task.jsm"); |
|
10 Cu.import("resource://gre/modules/osfile.jsm"); |
|
11 Cu.import("resource://services-sync/healthreport.jsm", this); |
|
12 Cu.import("resource://testing-common/services/healthreport/utils.jsm", this); |
|
13 Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); |
|
14 Cu.import("resource://testing-common/AddonManagerTesting.jsm"); |
|
15 |
|
16 const PREF_EXPERIMENTS_ENABLED = "experiments.enabled"; |
|
17 const PREF_LOGGING_LEVEL = "experiments.logging.level"; |
|
18 const PREF_LOGGING_DUMP = "experiments.logging.dump"; |
|
19 const PREF_MANIFEST_URI = "experiments.manifest.uri"; |
|
20 const PREF_FETCHINTERVAL = "experiments.manifest.fetchIntervalSeconds"; |
|
21 const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled"; |
|
22 const PREF_HEALTHREPORT_ENABLED = "datareporting.healthreport.service.enabled"; |
|
23 |
|
24 function getExperimentPath(base) { |
|
25 let p = do_get_cwd(); |
|
26 p.append(base); |
|
27 return p.path; |
|
28 } |
|
29 |
|
30 function sha1File(path) { |
|
31 let f = Cc["@mozilla.org/file/local;1"] |
|
32 .createInstance(Ci.nsILocalFile); |
|
33 f.initWithPath(path); |
|
34 let hasher = Cc["@mozilla.org/security/hash;1"] |
|
35 .createInstance(Ci.nsICryptoHash); |
|
36 hasher.init(hasher.SHA1); |
|
37 |
|
38 let is = Cc["@mozilla.org/network/file-input-stream;1"] |
|
39 .createInstance(Ci.nsIFileInputStream); |
|
40 is.init(f, -1, 0, 0); |
|
41 hasher.updateFromStream(is, Math.pow(2, 32) - 1); |
|
42 is.close(); |
|
43 let bytes = hasher.finish(false); |
|
44 |
|
45 return [("0" + bytes.charCodeAt(byte).toString(16)).slice(-2) |
|
46 for (byte in bytes)] |
|
47 .join(""); |
|
48 } |
|
49 |
|
50 const EXPERIMENT1_ID = "test-experiment-1@tests.mozilla.org"; |
|
51 const EXPERIMENT1_XPI_NAME = "experiment-1.xpi"; |
|
52 const EXPERIMENT1_NAME = "Test experiment 1"; |
|
53 const EXPERIMENT1_PATH = getExperimentPath(EXPERIMENT1_XPI_NAME); |
|
54 const EXPERIMENT1_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT1_PATH); |
|
55 |
|
56 |
|
57 const EXPERIMENT1A_XPI_NAME = "experiment-1a.xpi"; |
|
58 const EXPERIMENT1A_NAME = "Test experiment 1.1"; |
|
59 const EXPERIMENT1A_PATH = getExperimentPath(EXPERIMENT1A_XPI_NAME); |
|
60 const EXPERIMENT1A_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT1A_PATH); |
|
61 |
|
62 const EXPERIMENT2_ID = "test-experiment-2@tests.mozilla.org" |
|
63 const EXPERIMENT2_XPI_NAME = "experiment-2.xpi"; |
|
64 const EXPERIMENT2_PATH = getExperimentPath(EXPERIMENT2_XPI_NAME); |
|
65 const EXPERIMENT2_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT2_PATH); |
|
66 |
|
67 const EXPERIMENT3_ID = "test-experiment-3@tests.mozilla.org"; |
|
68 const EXPERIMENT4_ID = "test-experiment-4@tests.mozilla.org"; |
|
69 |
|
70 const DEFAULT_BUILDID = "2014060601"; |
|
71 |
|
72 const FAKE_EXPERIMENTS_1 = [ |
|
73 { |
|
74 id: "id1", |
|
75 name: "experiment1", |
|
76 description: "experiment 1", |
|
77 active: true, |
|
78 detailUrl: "https://dummy/experiment1", |
|
79 branch: "foo", |
|
80 }, |
|
81 ]; |
|
82 |
|
83 const FAKE_EXPERIMENTS_2 = [ |
|
84 { |
|
85 id: "id2", |
|
86 name: "experiment2", |
|
87 description: "experiment 2", |
|
88 active: false, |
|
89 endDate: new Date(2014, 2, 11, 2, 4, 35, 42).getTime(), |
|
90 detailUrl: "https://dummy/experiment2", |
|
91 branch: null, |
|
92 }, |
|
93 { |
|
94 id: "id1", |
|
95 name: "experiment1", |
|
96 description: "experiment 1", |
|
97 active: false, |
|
98 endDate: new Date(2014, 2, 10, 0, 0, 0, 0).getTime(), |
|
99 detailURL: "https://dummy/experiment1", |
|
100 branch: null, |
|
101 }, |
|
102 ]; |
|
103 |
|
104 let gAppInfo = null; |
|
105 |
|
106 function getReporter(name, uri, inspected) { |
|
107 return Task.spawn(function init() { |
|
108 let reporter = getHealthReporter(name, uri, inspected); |
|
109 yield reporter.init(); |
|
110 |
|
111 yield reporter._providerManager.registerProviderFromType( |
|
112 HealthReportProvider); |
|
113 |
|
114 throw new Task.Result(reporter); |
|
115 }); |
|
116 } |
|
117 |
|
118 function removeCacheFile() { |
|
119 let path = OS.Path.join(OS.Constants.Path.profileDir, "experiments.json"); |
|
120 return OS.File.remove(path); |
|
121 } |
|
122 |
|
123 function disableCertificateChecks() { |
|
124 let pref = "experiments.manifest.cert.checkAttributes"; |
|
125 Services.prefs.setBoolPref(pref, false); |
|
126 do_register_cleanup(() => Services.prefs.clearUserPref(pref)); |
|
127 } |
|
128 |
|
129 function patchPolicy(policy, data) { |
|
130 for (let key of Object.keys(data)) { |
|
131 Object.defineProperty(policy, key, { |
|
132 value: data[key], |
|
133 writable: true, |
|
134 }); |
|
135 } |
|
136 } |
|
137 |
|
138 function defineNow(policy, time) { |
|
139 patchPolicy(policy, { now: () => new Date(time) }); |
|
140 } |
|
141 |
|
142 function futureDate(date, offset) { |
|
143 return new Date(date.getTime() + offset); |
|
144 } |
|
145 |
|
146 function dateToSeconds(date) { |
|
147 return date.getTime() / 1000; |
|
148 } |
|
149 |
|
150 let gGlobalScope = this; |
|
151 function loadAddonManager() { |
|
152 let ns = {}; |
|
153 Cu.import("resource://gre/modules/Services.jsm", ns); |
|
154 let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js"; |
|
155 let file = do_get_file(head); |
|
156 let uri = ns.Services.io.newFileURI(file); |
|
157 ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope); |
|
158 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); |
|
159 startupManager(); |
|
160 } |
|
161 |
|
162 function getExperimentAddons(previous=false) { |
|
163 let deferred = Promise.defer(); |
|
164 |
|
165 AddonManager.getAddonsByTypes(["experiment"], (addons) => { |
|
166 if (previous) { |
|
167 deferred.resolve(addons); |
|
168 } else { |
|
169 deferred.resolve([a for (a of addons) if (!a.appDisabled)]); |
|
170 } |
|
171 }); |
|
172 |
|
173 return deferred.promise; |
|
174 } |
|
175 |
|
176 function createAppInfo(optionsIn) { |
|
177 const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; |
|
178 const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); |
|
179 |
|
180 let options = optionsIn || {}; |
|
181 let id = options.id || "xpcshell@tests.mozilla.org"; |
|
182 let name = options.name || "XPCShell"; |
|
183 let version = options.version || "1.0"; |
|
184 let platformVersion = options.platformVersion || "1.0"; |
|
185 let date = options.date || new Date(); |
|
186 |
|
187 let buildID = options.buildID || DEFAULT_BUILDID; |
|
188 |
|
189 gAppInfo = { |
|
190 // nsIXULAppInfo |
|
191 vendor: "Mozilla", |
|
192 name: name, |
|
193 ID: id, |
|
194 version: version, |
|
195 appBuildID: buildID, |
|
196 platformVersion: platformVersion ? platformVersion : "1.0", |
|
197 platformBuildID: buildID, |
|
198 |
|
199 // nsIXULRuntime |
|
200 inSafeMode: false, |
|
201 logConsoleErrors: true, |
|
202 OS: "XPCShell", |
|
203 XPCOMABI: "noarch-spidermonkey", |
|
204 invalidateCachesOnRestart: function invalidateCachesOnRestart() { |
|
205 // Do nothing |
|
206 }, |
|
207 |
|
208 // nsICrashReporter |
|
209 annotations: {}, |
|
210 |
|
211 annotateCrashReport: function(key, data) { |
|
212 this.annotations[key] = data; |
|
213 }, |
|
214 |
|
215 QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, |
|
216 Ci.nsIXULRuntime, |
|
217 Ci.nsICrashReporter, |
|
218 Ci.nsISupports]) |
|
219 }; |
|
220 |
|
221 let XULAppInfoFactory = { |
|
222 createInstance: function (outer, iid) { |
|
223 if (outer != null) { |
|
224 throw Cr.NS_ERROR_NO_AGGREGATION; |
|
225 } |
|
226 return gAppInfo.QueryInterface(iid); |
|
227 } |
|
228 }; |
|
229 |
|
230 let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); |
|
231 registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", |
|
232 XULAPPINFO_CONTRACTID, XULAppInfoFactory); |
|
233 } |
|
234 |
|
235 /** |
|
236 * Replace the experiments on an Experiments with a new list. |
|
237 * |
|
238 * This monkeypatches getExperiments(). It doesn't monkeypatch the internal |
|
239 * experiments list. So its utility is not as great as it could be. |
|
240 */ |
|
241 function replaceExperiments(experiment, list) { |
|
242 Object.defineProperty(experiment, "getExperiments", { |
|
243 writable: true, |
|
244 value: () => { |
|
245 return Promise.resolve(list); |
|
246 }, |
|
247 }); |
|
248 } |