|
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://testing-common/httpd.js"); |
|
7 XPCOMUtils.defineLazyModuleGetter(this, "Experiments", |
|
8 "resource:///modules/experiments/Experiments.jsm"); |
|
9 |
|
10 const FILE_MANIFEST = "experiments.manifest"; |
|
11 const MANIFEST_HANDLER = "manifests/handler"; |
|
12 |
|
13 const SEC_IN_ONE_DAY = 24 * 60 * 60; |
|
14 const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000; |
|
15 |
|
16 let gProfileDir = null; |
|
17 let gHttpServer = null; |
|
18 let gHttpRoot = null; |
|
19 let gDataRoot = null; |
|
20 let gReporter = null; |
|
21 let gPolicy = null; |
|
22 let gManifestObject = null; |
|
23 let gManifestHandlerURI = null; |
|
24 let gTimerScheduleOffset = -1; |
|
25 |
|
26 function run_test() { |
|
27 run_next_test(); |
|
28 } |
|
29 |
|
30 add_task(function* test_setup() { |
|
31 loadAddonManager(); |
|
32 gProfileDir = do_get_profile(); |
|
33 yield removeCacheFile(); |
|
34 |
|
35 gHttpServer = new HttpServer(); |
|
36 gHttpServer.start(-1); |
|
37 let port = gHttpServer.identity.primaryPort; |
|
38 gHttpRoot = "http://localhost:" + port + "/"; |
|
39 gDataRoot = gHttpRoot + "data/"; |
|
40 gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER; |
|
41 gHttpServer.registerDirectory("/data/", do_get_cwd()); |
|
42 gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => { |
|
43 response.setStatusLine(null, 200, "OK"); |
|
44 response.write(JSON.stringify(gManifestObject)); |
|
45 response.processAsync(); |
|
46 response.finish(); |
|
47 }); |
|
48 do_register_cleanup(() => gHttpServer.stop(() => {})); |
|
49 |
|
50 disableCertificateChecks(); |
|
51 |
|
52 Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true); |
|
53 Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0); |
|
54 Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true); |
|
55 Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI); |
|
56 Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0); |
|
57 |
|
58 gReporter = yield getReporter("json_payload_simple"); |
|
59 yield gReporter.collectMeasurements(); |
|
60 let payload = yield gReporter.getJSONPayload(true); |
|
61 do_register_cleanup(() => gReporter._shutdown()); |
|
62 |
|
63 gPolicy = new Experiments.Policy(); |
|
64 patchPolicy(gPolicy, { |
|
65 updatechannel: () => "nightly", |
|
66 healthReportPayload: () => {}, |
|
67 oneshotTimer: (callback, timeout, thisObj, name) => gTimerScheduleOffset = timeout, |
|
68 }); |
|
69 }); |
|
70 |
|
71 function checkExperimentListsEqual(list, list2) { |
|
72 Assert.equal(list.length, list2.length, "Lists should have the same length.") |
|
73 |
|
74 for (let i=0; i<list.length; ++i) { |
|
75 for (let k of Object.keys(list[i])) { |
|
76 Assert.equal(list[i][k], list2[i][k], |
|
77 "Field '" + k + "' should match for list entry " + i + "."); |
|
78 } |
|
79 } |
|
80 } |
|
81 |
|
82 function checkExperimentSerializations(experimentEntryIterator) { |
|
83 for (let experiment of experimentEntryIterator) { |
|
84 let experiment2 = new Experiments.ExperimentEntry(gPolicy); |
|
85 let jsonStr = JSON.stringify(experiment.toJSON()); |
|
86 Assert.ok(experiment2.initFromCacheData(JSON.parse(jsonStr)), |
|
87 "Should have initialized successfully from JSON serialization."); |
|
88 Assert.equal(JSON.stringify(experiment), JSON.stringify(experiment2), |
|
89 "Object stringifications should match."); |
|
90 } |
|
91 } |
|
92 |
|
93 // Set up an experiments instance and check if it is properly restored from cache. |
|
94 |
|
95 add_task(function* test_cache() { |
|
96 // The manifest data we test with. |
|
97 |
|
98 gManifestObject = { |
|
99 "version": 1, |
|
100 experiments: [ |
|
101 { |
|
102 id: EXPERIMENT1_ID, |
|
103 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME, |
|
104 xpiHash: EXPERIMENT1_XPI_SHA1, |
|
105 maxActiveSeconds: 10 * SEC_IN_ONE_DAY, |
|
106 appName: ["XPCShell"], |
|
107 channel: ["nightly"], |
|
108 }, |
|
109 { |
|
110 id: EXPERIMENT2_ID, |
|
111 xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME, |
|
112 xpiHash: EXPERIMENT2_XPI_SHA1, |
|
113 maxActiveSeconds: 10 * SEC_IN_ONE_DAY, |
|
114 appName: ["XPCShell"], |
|
115 channel: ["nightly"], |
|
116 }, |
|
117 { |
|
118 id: EXPERIMENT3_ID, |
|
119 xpiURL: "https://inval.id/foo.xpi", |
|
120 xpiHash: "sha1:0000000000000000000000000000000000000000", |
|
121 maxActiveSeconds: 10 * SEC_IN_ONE_DAY, |
|
122 appName: ["XPCShell"], |
|
123 channel: ["nightly"], |
|
124 }, |
|
125 ], |
|
126 }; |
|
127 |
|
128 // Setup dates for the experiments. |
|
129 |
|
130 let baseDate = new Date(2014, 5, 1, 12); |
|
131 let startDates = []; |
|
132 let endDates = []; |
|
133 |
|
134 for (let i=0; i<gManifestObject.experiments.length; ++i) { |
|
135 let experiment = gManifestObject.experiments[i]; |
|
136 startDates.push(futureDate(baseDate, (50 + (150 * i)) * MS_IN_ONE_DAY)); |
|
137 endDates .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY)); |
|
138 experiment.startTime = dateToSeconds(startDates[i]); |
|
139 experiment.endTime = dateToSeconds(endDates[i]); |
|
140 } |
|
141 |
|
142 // Data to compare the result of Experiments.getExperiments() against. |
|
143 |
|
144 let experimentListData = [ |
|
145 { |
|
146 id: EXPERIMENT2_ID, |
|
147 name: "Test experiment 2", |
|
148 description: "And yet another experiment that experiments experimentally.", |
|
149 }, |
|
150 { |
|
151 id: EXPERIMENT1_ID, |
|
152 name: EXPERIMENT1_NAME, |
|
153 description: "Yet another experiment that experiments experimentally.", |
|
154 }, |
|
155 ]; |
|
156 |
|
157 // Trigger update & re-init, clock set to before any activation. |
|
158 |
|
159 let now = baseDate; |
|
160 defineNow(gPolicy, now); |
|
161 |
|
162 let experiments = new Experiments.Experiments(gPolicy); |
|
163 yield experiments.updateManifest(); |
|
164 let list = yield experiments.getExperiments(); |
|
165 Assert.equal(list.length, 0, "Experiment list should be empty."); |
|
166 checkExperimentSerializations(experiments._experiments.values()); |
|
167 |
|
168 yield experiments.uninit(); |
|
169 experiments = new Experiments.Experiments(gPolicy); |
|
170 |
|
171 yield experiments._run(); |
|
172 list = yield experiments.getExperiments(); |
|
173 Assert.equal(list.length, 0, "Experiment list should be empty."); |
|
174 checkExperimentSerializations(experiments._experiments.values()); |
|
175 |
|
176 // Re-init, clock set for experiment 1 to start. |
|
177 |
|
178 now = futureDate(startDates[0], 5 * MS_IN_ONE_DAY); |
|
179 defineNow(gPolicy, now); |
|
180 |
|
181 yield experiments.uninit(); |
|
182 experiments = new Experiments.Experiments(gPolicy); |
|
183 yield experiments._run(); |
|
184 |
|
185 list = yield experiments.getExperiments(); |
|
186 Assert.equal(list.length, 1, "Experiment list should have 1 entry now."); |
|
187 |
|
188 experimentListData[1].active = true; |
|
189 experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY; |
|
190 checkExperimentListsEqual(experimentListData.slice(1), list); |
|
191 checkExperimentSerializations(experiments._experiments.values()); |
|
192 |
|
193 let branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID); |
|
194 Assert.strictEqual(branch, null); |
|
195 |
|
196 yield experiments.setExperimentBranch(EXPERIMENT1_ID, "testbranch"); |
|
197 branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID); |
|
198 Assert.strictEqual(branch, "testbranch"); |
|
199 |
|
200 // Re-init, clock set for experiment 1 to stop. |
|
201 |
|
202 now = futureDate(now, 20 * MS_IN_ONE_DAY); |
|
203 defineNow(gPolicy, now); |
|
204 |
|
205 yield experiments.uninit(); |
|
206 experiments = new Experiments.Experiments(gPolicy); |
|
207 yield experiments._run(); |
|
208 |
|
209 list = yield experiments.getExperiments(); |
|
210 Assert.equal(list.length, 1, "Experiment list should have 1 entry."); |
|
211 |
|
212 experimentListData[1].active = false; |
|
213 experimentListData[1].endDate = now.getTime(); |
|
214 checkExperimentListsEqual(experimentListData.slice(1), list); |
|
215 checkExperimentSerializations(experiments._experiments.values()); |
|
216 |
|
217 branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID); |
|
218 Assert.strictEqual(branch, "testbranch"); |
|
219 |
|
220 // Re-init, clock set for experiment 2 to start. |
|
221 |
|
222 now = futureDate(startDates[1], 20 * MS_IN_ONE_DAY); |
|
223 defineNow(gPolicy, now); |
|
224 |
|
225 yield experiments.uninit(); |
|
226 experiments = new Experiments.Experiments(gPolicy); |
|
227 yield experiments._run(); |
|
228 |
|
229 list = yield experiments.getExperiments(); |
|
230 Assert.equal(list.length, 2, "Experiment list should have 2 entries."); |
|
231 |
|
232 experimentListData[0].active = true; |
|
233 experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY; |
|
234 checkExperimentListsEqual(experimentListData, list); |
|
235 checkExperimentSerializations(experiments._experiments.values()); |
|
236 |
|
237 // Re-init, clock set for experiment 2 to stop. |
|
238 |
|
239 now = futureDate(now, 20 * MS_IN_ONE_DAY); |
|
240 defineNow(gPolicy, now); |
|
241 |
|
242 yield experiments.uninit(); |
|
243 experiments = new Experiments.Experiments(gPolicy); |
|
244 yield experiments._run(); |
|
245 |
|
246 list = yield experiments.getExperiments(); |
|
247 Assert.equal(list.length, 2, "Experiment list should have 2 entries."); |
|
248 |
|
249 experimentListData[0].active = false; |
|
250 experimentListData[0].endDate = now.getTime(); |
|
251 checkExperimentListsEqual(experimentListData, list); |
|
252 checkExperimentSerializations(experiments._experiments.values()); |
|
253 |
|
254 // Cleanup. |
|
255 |
|
256 yield experiments._toggleExperimentsEnabled(false); |
|
257 yield experiments.uninit(); |
|
258 yield removeCacheFile(); |
|
259 }); |