browser/experiments/test/xpcshell/test_api.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:598195cdcb54
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 Cu.import("resource://testing-common/AddonManagerTesting.jsm");
8
9 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
10 "resource:///modules/experiments/Experiments.jsm");
11
12 const FILE_MANIFEST = "experiments.manifest";
13 const MANIFEST_HANDLER = "manifests/handler";
14
15 const SEC_IN_ONE_DAY = 24 * 60 * 60;
16 const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
17
18 let gProfileDir = null;
19 let gHttpServer = null;
20 let gHttpRoot = null;
21 let gDataRoot = null;
22 let gReporter = null;
23 let gPolicy = null;
24 let gManifestObject = null;
25 let gManifestHandlerURI = null;
26 let gTimerScheduleOffset = -1;
27
28 function uninstallExperimentAddons() {
29 return Task.spawn(function* () {
30 let addons = yield getExperimentAddons();
31 for (let a of addons) {
32 yield AddonTestUtils.uninstallAddonByID(a.id);
33 }
34 });
35 }
36
37 function testCleanup(experimentsInstance) {
38 return Task.spawn(function* () {
39 yield experimentsInstance.uninit();
40 yield removeCacheFile();
41 yield uninstallExperimentAddons();
42 restartManager();
43 });
44 }
45
46 function run_test() {
47 run_next_test();
48 }
49
50 add_task(function* test_setup() {
51 loadAddonManager();
52 gProfileDir = do_get_profile();
53
54 gHttpServer = new HttpServer();
55 gHttpServer.start(-1);
56 let port = gHttpServer.identity.primaryPort;
57 gHttpRoot = "http://localhost:" + port + "/";
58 gDataRoot = gHttpRoot + "data/";
59 gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
60 gHttpServer.registerDirectory("/data/", do_get_cwd());
61 gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
62 response.setStatusLine(null, 200, "OK");
63 response.write(JSON.stringify(gManifestObject));
64 response.processAsync();
65 response.finish();
66 });
67 do_register_cleanup(() => gHttpServer.stop(() => {}));
68
69 disableCertificateChecks();
70
71 Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
72 Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
73 Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
74 Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
75 Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
76
77 gReporter = yield getReporter("json_payload_simple");
78 yield gReporter.collectMeasurements();
79 let payload = yield gReporter.getJSONPayload(true);
80 do_register_cleanup(() => gReporter._shutdown());
81
82 gPolicy = new Experiments.Policy();
83 patchPolicy(gPolicy, {
84 updatechannel: () => "nightly",
85 healthReportPayload: () => Promise.resolve(payload),
86 oneshotTimer: (callback, timeout, thisObj, name) => gTimerScheduleOffset = timeout,
87 });
88 });
89
90 add_task(function* test_contract() {
91 Cc["@mozilla.org/browser/experiments-service;1"].getService();
92 });
93
94 // Test basic starting and stopping of experiments.
95
96 add_task(function* test_getExperiments() {
97 const OBSERVER_TOPIC = "experiments-changed";
98 let observerFireCount = 0;
99 let expectedObserverFireCount = 0;
100 let observer = () => ++observerFireCount;
101 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
102
103 // Dates the following tests are based on.
104
105 let baseDate = new Date(2014, 5, 1, 12);
106 let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
107 let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
108 let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
109 let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
110
111 // The manifest data we test with.
112
113 gManifestObject = {
114 "version": 1,
115 experiments: [
116 {
117 id: EXPERIMENT2_ID,
118 xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
119 xpiHash: EXPERIMENT2_XPI_SHA1,
120 startTime: dateToSeconds(startDate2),
121 endTime: dateToSeconds(endDate2),
122 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
123 appName: ["XPCShell"],
124 channel: ["nightly"],
125 },
126 {
127 id: EXPERIMENT1_ID,
128 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
129 xpiHash: EXPERIMENT1_XPI_SHA1,
130 startTime: dateToSeconds(startDate1),
131 endTime: dateToSeconds(endDate1),
132 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
133 appName: ["XPCShell"],
134 channel: ["nightly"],
135 },
136 ],
137 };
138
139 // Data to compare the result of Experiments.getExperiments() against.
140
141 let experimentListData = [
142 {
143 id: EXPERIMENT2_ID,
144 name: "Test experiment 2",
145 description: "And yet another experiment that experiments experimentally.",
146 },
147 {
148 id: EXPERIMENT1_ID,
149 name: EXPERIMENT1_NAME,
150 description: "Yet another experiment that experiments experimentally.",
151 },
152 ];
153
154 let experiments = new Experiments.Experiments(gPolicy);
155
156 // Trigger update, clock set to before any activation.
157 // Use updateManifest() to provide for coverage of that path.
158
159 let now = baseDate;
160 gTimerScheduleOffset = -1;
161 defineNow(gPolicy, now);
162
163 yield experiments.updateManifest();
164 Assert.equal(observerFireCount, ++expectedObserverFireCount,
165 "Experiments observer should have been called.");
166 Assert.equal(experiments.getActiveExperimentID(), null,
167 "getActiveExperimentID should return null");
168
169 let list = yield experiments.getExperiments();
170 Assert.equal(list.length, 0, "Experiment list should be empty.");
171 let addons = yield getExperimentAddons();
172 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are installed.");
173
174 try {
175 let b = yield experiments.getExperimentBranch();
176 Assert.ok(false, "getExperimentBranch should fail with no experiment");
177 }
178 catch (e) {
179 Assert.ok(true, "getExperimentBranch correctly threw");
180 }
181
182 // Trigger update, clock set for experiment 1 to start.
183
184 now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
185 gTimerScheduleOffset = -1;
186 defineNow(gPolicy, now);
187
188 yield experiments.updateManifest();
189 Assert.equal(observerFireCount, ++expectedObserverFireCount,
190 "Experiments observer should have been called.");
191
192 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
193 "getActiveExperimentID should return the active experiment1");
194
195 list = yield experiments.getExperiments();
196 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
197 addons = yield getExperimentAddons();
198 Assert.equal(addons.length, 1, "An experiment add-on was installed.");
199
200 experimentListData[1].active = true;
201 experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
202 for (let k of Object.keys(experimentListData[1])) {
203 Assert.equal(experimentListData[1][k], list[0][k],
204 "Property " + k + " should match reference data.");
205 }
206
207 let b = yield experiments.getExperimentBranch();
208 Assert.strictEqual(b, null, "getExperimentBranch should return null by default");
209
210 b = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
211 Assert.strictEqual(b, null, "getExperimentsBranch should return null (with id)");
212
213 yield experiments.setExperimentBranch(EXPERIMENT1_ID, "foo");
214 b = yield experiments.getExperimentBranch();
215 Assert.strictEqual(b, "foo", "getExperimentsBranch should return the set value");
216
217 Assert.equal(observerFireCount, ++expectedObserverFireCount,
218 "Experiments observer should have been called.");
219
220 Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
221 "Experiment re-evaluation should have been scheduled correctly.");
222
223 // Trigger update, clock set for experiment 1 to stop.
224
225 now = futureDate(endDate1, 1000);
226 gTimerScheduleOffset = -1;
227 defineNow(gPolicy, now);
228
229 yield experiments.updateManifest();
230 Assert.equal(observerFireCount, ++expectedObserverFireCount,
231 "Experiments observer should have been called.");
232
233 Assert.equal(experiments.getActiveExperimentID(), null,
234 "getActiveExperimentID should return null again");
235
236 list = yield experiments.getExperiments();
237 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
238 addons = yield getExperimentAddons();
239 Assert.equal(addons.length, 0, "The experiment add-on should be uninstalled.");
240
241 experimentListData[1].active = false;
242 experimentListData[1].endDate = now.getTime();
243 for (let k of Object.keys(experimentListData[1])) {
244 Assert.equal(experimentListData[1][k], list[0][k],
245 "Property " + k + " should match reference data.");
246 }
247
248 Assert.equal(gTimerScheduleOffset, startDate2 - now,
249 "Experiment re-evaluation should have been scheduled correctly.");
250
251 // Trigger update, clock set for experiment 2 to start.
252 // Use notify() to provide for coverage of that path.
253
254 now = startDate2;
255 gTimerScheduleOffset = -1;
256 defineNow(gPolicy, now);
257
258 yield experiments.notify();
259 Assert.equal(observerFireCount, ++expectedObserverFireCount,
260 "Experiments observer should have been called.");
261
262 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT2_ID,
263 "getActiveExperimentID should return the active experiment2");
264
265 list = yield experiments.getExperiments();
266 Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
267 addons = yield getExperimentAddons();
268 Assert.equal(addons.length, 1, "An experiment add-on is installed.");
269
270 experimentListData[0].active = true;
271 experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
272 for (let i=0; i<experimentListData.length; ++i) {
273 let entry = experimentListData[i];
274 for (let k of Object.keys(entry)) {
275 Assert.equal(entry[k], list[i][k],
276 "Entry " + i + " - Property '" + k + "' should match reference data.");
277 }
278 }
279
280 Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
281 "Experiment re-evaluation should have been scheduled correctly.");
282
283 // Trigger update, clock set for experiment 2 to stop.
284
285 now = futureDate(startDate2, 10 * MS_IN_ONE_DAY + 1000);
286 gTimerScheduleOffset = -1;
287 defineNow(gPolicy, now);
288 yield experiments.notify();
289 Assert.equal(observerFireCount, ++expectedObserverFireCount,
290 "Experiments observer should have been called.");
291
292 Assert.equal(experiments.getActiveExperimentID(), null,
293 "getActiveExperimentID should return null again2");
294
295 list = yield experiments.getExperiments();
296 Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
297 addons = yield getExperimentAddons();
298 Assert.equal(addons.length, 0, "No experiments add-ons are installed.");
299
300 experimentListData[0].active = false;
301 experimentListData[0].endDate = now.getTime();
302 for (let i=0; i<experimentListData.length; ++i) {
303 let entry = experimentListData[i];
304 for (let k of Object.keys(entry)) {
305 Assert.equal(entry[k], list[i][k],
306 "Entry " + i + " - Property '" + k + "' should match reference data.");
307 }
308 }
309
310 // Cleanup.
311
312 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
313 yield testCleanup(experiments);
314 });
315
316 add_task(function* test_getActiveExperimentID() {
317 // Check that getActiveExperimentID returns the correct result even
318 // after .uninit()
319
320 // Dates the following tests are based on.
321
322 let baseDate = new Date(2014, 5, 1, 12);
323 let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
324 let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
325
326 gManifestObject = {
327 "version": 1,
328 experiments: [
329 {
330 id: EXPERIMENT1_ID,
331 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
332 xpiHash: EXPERIMENT1_XPI_SHA1,
333 startTime: dateToSeconds(startDate1),
334 endTime: dateToSeconds(endDate1),
335 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
336 appName: ["XPCShell"],
337 channel: ["nightly"],
338 },
339 ],
340 };
341
342 let now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
343 gTimerScheduleOffset = -1;
344 defineNow(gPolicy, now);
345
346 let experiments = new Experiments.Experiments(gPolicy);
347 yield experiments.updateManifest();
348
349 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
350 "getActiveExperimentID should return the active experiment1");
351
352 yield experiments.uninit();
353 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
354 "getActiveExperimentID should return the active experiment1 after uninit()");
355
356 yield testCleanup(experiments);
357 });
358
359 // Test that we handle the experiments addon already being
360 // installed properly.
361 // We should just pave over them.
362
363 add_task(function* test_addonAlreadyInstalled() {
364 const OBSERVER_TOPIC = "experiments-changed";
365 let observerFireCount = 0;
366 let expectedObserverFireCount = 0;
367 let observer = () => ++observerFireCount;
368 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
369
370 // Dates the following tests are based on.
371
372 let baseDate = new Date(2014, 5, 1, 12);
373 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
374 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
375
376 // The manifest data we test with.
377
378 gManifestObject = {
379 "version": 1,
380 experiments: [
381 {
382 id: EXPERIMENT1_ID,
383 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
384 xpiHash: EXPERIMENT1_XPI_SHA1,
385 startTime: dateToSeconds(startDate),
386 endTime: dateToSeconds(endDate),
387 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
388 appName: ["XPCShell"],
389 channel: ["nightly"],
390 },
391 ],
392 };
393
394 let experiments = new Experiments.Experiments(gPolicy);
395
396 // Trigger update, clock set to before any activation.
397
398 let now = baseDate;
399 defineNow(gPolicy, now);
400 yield experiments.updateManifest();
401 Assert.equal(observerFireCount, ++expectedObserverFireCount,
402 "Experiments observer should have been called.");
403 let list = yield experiments.getExperiments();
404 Assert.equal(list.length, 0, "Experiment list should be empty.");
405
406 // Trigger update, clock set for the experiment to start.
407
408 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
409 defineNow(gPolicy, now);
410 yield experiments.updateManifest();
411 Assert.equal(observerFireCount, ++expectedObserverFireCount,
412 "Experiments observer should have been called.");
413
414 list = yield experiments.getExperiments();
415 list = yield experiments.getExperiments();
416 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
417 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
418 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
419
420 let addons = yield getExperimentAddons();
421 Assert.equal(addons.length, 1, "1 add-on is installed.");
422
423 // Install conflicting addon.
424
425 yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
426 addons = yield getExperimentAddons();
427 Assert.equal(addons.length, 1, "1 add-on is installed.");
428 list = yield experiments.getExperiments();
429 Assert.equal(list.length, 1, "Experiment list should still have 1 entry.");
430 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
431 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
432
433 // Cleanup.
434
435 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
436 yield testCleanup(experiments);
437 });
438
439 add_task(function* test_lastActiveToday() {
440 let experiments = new Experiments.Experiments(gPolicy);
441
442 replaceExperiments(experiments, FAKE_EXPERIMENTS_1);
443
444 let e = yield experiments.getExperiments();
445 Assert.equal(e.length, 1, "Monkeypatch successful.");
446 Assert.equal(e[0].id, "id1", "ID looks sane");
447 Assert.ok(e[0].active, "Experiment is active.");
448
449 let lastActive = yield experiments.lastActiveToday();
450 Assert.equal(e[0], lastActive, "Last active object is expected.");
451
452 replaceExperiments(experiments, FAKE_EXPERIMENTS_2);
453 e = yield experiments.getExperiments();
454 Assert.equal(e.length, 2, "Monkeypatch successful.");
455
456 defineNow(gPolicy, e[0].endDate);
457
458 lastActive = yield experiments.lastActiveToday();
459 Assert.ok(lastActive, "Have a last active experiment");
460 Assert.equal(lastActive, e[0], "Last active object is expected.");
461
462 yield testCleanup(experiments);
463 });
464
465 // Test explicitly disabling experiments.
466
467 add_task(function* test_disableExperiment() {
468 // Dates this test is based on.
469
470 let startDate = new Date(2004, 10, 9, 12);
471 let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
472
473 // The manifest data we test with.
474
475 gManifestObject = {
476 "version": 1,
477 experiments: [
478 {
479 id: EXPERIMENT1_ID,
480 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
481 xpiHash: EXPERIMENT1_XPI_SHA1,
482 startTime: dateToSeconds(startDate),
483 endTime: dateToSeconds(endDate),
484 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
485 appName: ["XPCShell"],
486 channel: ["nightly"],
487 },
488 ],
489 };
490
491 // Data to compare the result of Experiments.getExperiments() against.
492
493 let experimentInfo = {
494 id: EXPERIMENT1_ID,
495 name: EXPERIMENT1_NAME,
496 description: "Yet another experiment that experiments experimentally.",
497 };
498
499 let experiments = new Experiments.Experiments(gPolicy);
500
501 // Trigger update, clock set for the experiment to start.
502
503 let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
504 defineNow(gPolicy, now);
505 yield experiments.updateManifest();
506
507 let list = yield experiments.getExperiments();
508 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
509
510 experimentInfo.active = true;
511 experimentInfo.endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
512 for (let k of Object.keys(experimentInfo)) {
513 Assert.equal(experimentInfo[k], list[0][k],
514 "Property " + k + " should match reference data.");
515 }
516
517 // Test disabling the experiment.
518
519 now = futureDate(now, 1 * MS_IN_ONE_DAY);
520 defineNow(gPolicy, now);
521 yield experiments.disableExperiment("foo");
522
523 list = yield experiments.getExperiments();
524 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
525
526 experimentInfo.active = false;
527 experimentInfo.endDate = now.getTime();
528 for (let k of Object.keys(experimentInfo)) {
529 Assert.equal(experimentInfo[k], list[0][k],
530 "Property " + k + " should match reference data.");
531 }
532
533 // Test that updating the list doesn't re-enable it.
534
535 now = futureDate(now, 1 * MS_IN_ONE_DAY);
536 defineNow(gPolicy, now);
537 yield experiments.updateManifest();
538
539 list = yield experiments.getExperiments();
540 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
541
542 for (let k of Object.keys(experimentInfo)) {
543 Assert.equal(experimentInfo[k], list[0][k],
544 "Property " + k + " should match reference data.");
545 }
546
547 yield testCleanup(experiments);
548 });
549
550 add_task(function* test_disableExperimentsFeature() {
551 // Dates this test is based on.
552
553 let startDate = new Date(2004, 10, 9, 12);
554 let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
555
556 // The manifest data we test with.
557
558 gManifestObject = {
559 "version": 1,
560 experiments: [
561 {
562 id: EXPERIMENT1_ID,
563 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
564 xpiHash: EXPERIMENT1_XPI_SHA1,
565 startTime: dateToSeconds(startDate),
566 endTime: dateToSeconds(endDate),
567 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
568 appName: ["XPCShell"],
569 channel: ["nightly"],
570 },
571 ],
572 };
573
574 // Data to compare the result of Experiments.getExperiments() against.
575
576 let experimentInfo = {
577 id: EXPERIMENT1_ID,
578 name: EXPERIMENT1_NAME,
579 description: "Yet another experiment that experiments experimentally.",
580 };
581
582 let experiments = new Experiments.Experiments(gPolicy);
583 Assert.equal(experiments.enabled, true, "Experiments feature should be enabled.");
584
585 // Trigger update, clock set for the experiment to start.
586
587 let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
588 defineNow(gPolicy, now);
589 yield experiments.updateManifest();
590
591 let list = yield experiments.getExperiments();
592 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
593
594 experimentInfo.active = true;
595 experimentInfo.endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
596 for (let k of Object.keys(experimentInfo)) {
597 Assert.equal(experimentInfo[k], list[0][k],
598 "Property " + k + " should match reference data.");
599 }
600
601 // Test disabling experiments.
602
603 experiments._toggleExperimentsEnabled(false);
604 yield experiments.notify();
605 Assert.equal(experiments.enabled, false, "Experiments feature should be disabled now.");
606
607 list = yield experiments.getExperiments();
608 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
609
610 experimentInfo.active = false;
611 experimentInfo.endDate = now.getTime();
612 for (let k of Object.keys(experimentInfo)) {
613 Assert.equal(experimentInfo[k], list[0][k],
614 "Property " + k + " should match reference data.");
615 }
616
617 // Test that updating the list doesn't re-enable it.
618
619 now = futureDate(now, 1 * MS_IN_ONE_DAY);
620 defineNow(gPolicy, now);
621 try {
622 yield experiments.updateManifest();
623 } catch (e) {
624 // Exception expected, the feature is disabled.
625 }
626
627 list = yield experiments.getExperiments();
628 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
629
630 for (let k of Object.keys(experimentInfo)) {
631 Assert.equal(experimentInfo[k], list[0][k],
632 "Property " + k + " should match reference data.");
633 }
634
635 yield testCleanup(experiments);
636 });
637
638 // Test that after a failed experiment install:
639 // * the next applicable experiment gets installed
640 // * changing the experiments data later triggers re-evaluation
641
642 add_task(function* test_installFailure() {
643 const OBSERVER_TOPIC = "experiments-changed";
644 let observerFireCount = 0;
645 let expectedObserverFireCount = 0;
646 let observer = () => ++observerFireCount;
647 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
648
649 // Dates the following tests are based on.
650
651 let baseDate = new Date(2014, 5, 1, 12);
652 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
653 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
654
655 // The manifest data we test with.
656
657 gManifestObject = {
658 "version": 1,
659 experiments: [
660 {
661 id: EXPERIMENT1_ID,
662 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
663 xpiHash: EXPERIMENT1_XPI_SHA1,
664 startTime: dateToSeconds(startDate),
665 endTime: dateToSeconds(endDate),
666 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
667 appName: ["XPCShell"],
668 channel: ["nightly"],
669 },
670 {
671 id: EXPERIMENT2_ID,
672 xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
673 xpiHash: EXPERIMENT2_XPI_SHA1,
674 startTime: dateToSeconds(startDate),
675 endTime: dateToSeconds(endDate),
676 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
677 appName: ["XPCShell"],
678 channel: ["nightly"],
679 },
680 ],
681 };
682
683 // Data to compare the result of Experiments.getExperiments() against.
684
685 let experimentListData = [
686 {
687 id: EXPERIMENT1_ID,
688 name: EXPERIMENT1_NAME,
689 description: "Yet another experiment that experiments experimentally.",
690 },
691 {
692 id: EXPERIMENT2_ID,
693 name: "Test experiment 2",
694 description: "And yet another experiment that experiments experimentally.",
695 },
696 ];
697
698 let experiments = new Experiments.Experiments(gPolicy);
699
700 // Trigger update, clock set to before any activation.
701
702 let now = baseDate;
703 defineNow(gPolicy, now);
704 yield experiments.updateManifest();
705 Assert.equal(observerFireCount, ++expectedObserverFireCount,
706 "Experiments observer should have been called.");
707 let list = yield experiments.getExperiments();
708 Assert.equal(list.length, 0, "Experiment list should be empty.");
709
710 // Trigger update, clock set for experiment 1 & 2 to start,
711 // invalid hash for experiment 1.
712 // Order in the manifest matters, so we should start experiment 1,
713 // fail to install it & start experiment 2 instead.
714
715 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
716 defineNow(gPolicy, now);
717 gManifestObject.experiments[0].xpiHash = "sha1:0000000000000000000000000000000000000000";
718 yield experiments.updateManifest();
719 Assert.equal(observerFireCount, ++expectedObserverFireCount,
720 "Experiments observer should have been called.");
721
722 list = yield experiments.getExperiments();
723 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
724 Assert.equal(list[0].id, EXPERIMENT2_ID, "Experiment 2 should be the sole entry.");
725 Assert.equal(list[0].active, true, "Experiment 2 should be active.");
726
727 // Trigger update, clock set for experiment 2 to stop.
728
729 now = futureDate(now, 20 * MS_IN_ONE_DAY);
730 defineNow(gPolicy, now);
731 yield experiments.updateManifest();
732 Assert.equal(observerFireCount, ++expectedObserverFireCount,
733 "Experiments observer should have been called.");
734
735 experimentListData[0].active = false;
736 experimentListData[0].endDate = now;
737
738 list = yield experiments.getExperiments();
739 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
740 Assert.equal(list[0].id, EXPERIMENT2_ID, "Experiment 2 should be the sole entry.");
741 Assert.equal(list[0].active, false, "Experiment should not be active.");
742
743 // Trigger update with a fixed entry for experiment 1,
744 // which should get re-evaluated & started now.
745
746 now = futureDate(now, 20 * MS_IN_ONE_DAY);
747 defineNow(gPolicy, now);
748 gManifestObject.experiments[0].xpiHash = EXPERIMENT1_XPI_SHA1;
749 yield experiments.updateManifest();
750 Assert.equal(observerFireCount, ++expectedObserverFireCount,
751 "Experiments observer should have been called.");
752
753 experimentListData[0].active = true;
754 experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
755
756 list = yield experiments.getExperiments();
757 Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
758
759 for (let i=0; i<experimentListData.length; ++i) {
760 let entry = experimentListData[i];
761 for (let k of Object.keys(entry)) {
762 Assert.equal(entry[k], list[i][k],
763 "Entry " + i + " - Property '" + k + "' should match reference data.");
764 }
765 }
766
767 yield testCleanup(experiments);
768 });
769
770 // Test that after an experiment was disabled by user action,
771 // the experiment is not activated again if manifest data changes.
772
773 add_task(function* test_userDisabledAndUpdated() {
774 const OBSERVER_TOPIC = "experiments-changed";
775 let observerFireCount = 0;
776 let expectedObserverFireCount = 0;
777 let observer = () => ++observerFireCount;
778 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
779
780 // Dates the following tests are based on.
781
782 let baseDate = new Date(2014, 5, 1, 12);
783 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
784 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
785
786 // The manifest data we test with.
787
788 gManifestObject = {
789 "version": 1,
790 experiments: [
791 {
792 id: EXPERIMENT1_ID,
793 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
794 xpiHash: EXPERIMENT1_XPI_SHA1,
795 startTime: dateToSeconds(startDate),
796 endTime: dateToSeconds(endDate),
797 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
798 appName: ["XPCShell"],
799 channel: ["nightly"],
800 },
801 ],
802 };
803
804 let experiments = new Experiments.Experiments(gPolicy);
805
806 // Trigger update, clock set to before any activation.
807
808 let now = baseDate;
809 defineNow(gPolicy, now);
810 yield experiments.updateManifest();
811 Assert.equal(observerFireCount, ++expectedObserverFireCount,
812 "Experiments observer should have been called.");
813 let list = yield experiments.getExperiments();
814 Assert.equal(list.length, 0, "Experiment list should be empty.");
815
816 // Trigger update, clock set for experiment 1 to start.
817
818 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
819 defineNow(gPolicy, now);
820 yield experiments.updateManifest();
821 Assert.equal(observerFireCount, ++expectedObserverFireCount,
822 "Experiments observer should have been called.");
823
824 list = yield experiments.getExperiments();
825 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
826 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
827 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
828 let todayActive = yield experiments.lastActiveToday();
829 Assert.ok(todayActive, "Last active for today reports a value.");
830 Assert.equal(todayActive.id, list[0].id, "The entry is what we expect.");
831
832 // Explicitly disable an experiment.
833
834 now = futureDate(now, 20 * MS_IN_ONE_DAY);
835 defineNow(gPolicy, now);
836 yield experiments.disableExperiment("foo");
837 Assert.equal(observerFireCount, ++expectedObserverFireCount,
838 "Experiments observer should have been called.");
839
840 list = yield experiments.getExperiments();
841 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
842 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
843 Assert.equal(list[0].active, false, "Experiment should not be active anymore.");
844 todayActive = yield experiments.lastActiveToday();
845 Assert.ok(todayActive, "Last active for today still returns a value.");
846 Assert.equal(todayActive.id, list[0].id, "The ID is still the same.");
847
848 // Trigger an update with a faked change for experiment 1.
849
850 now = futureDate(now, 20 * MS_IN_ONE_DAY);
851 defineNow(gPolicy, now);
852 experiments._experiments.get(EXPERIMENT1_ID)._manifestData.xpiHash =
853 "sha1:0000000000000000000000000000000000000000";
854 yield experiments.updateManifest();
855 Assert.equal(observerFireCount, expectedObserverFireCount,
856 "Experiments observer should not have been called.");
857
858 list = yield experiments.getExperiments();
859 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
860 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
861 Assert.equal(list[0].active, false, "Experiment should still be inactive.");
862
863 // Cleanup.
864
865 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
866 yield testCleanup(experiments);
867 });
868
869 // Test that changing the hash for an active experiments triggers an
870 // update for it.
871
872 add_task(function* test_updateActiveExperiment() {
873 const OBSERVER_TOPIC = "experiments-changed";
874 let observerFireCount = 0;
875 let expectedObserverFireCount = 0;
876 let observer = () => ++observerFireCount;
877 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
878
879 // Dates the following tests are based on.
880
881 let baseDate = new Date(2014, 5, 1, 12);
882 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
883 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
884
885 // The manifest data we test with.
886
887 gManifestObject = {
888 "version": 1,
889 experiments: [
890 {
891 id: EXPERIMENT1_ID,
892 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
893 xpiHash: EXPERIMENT1_XPI_SHA1,
894 startTime: dateToSeconds(startDate),
895 endTime: dateToSeconds(endDate),
896 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
897 appName: ["XPCShell"],
898 channel: ["nightly"],
899 },
900 ],
901 };
902
903 let experiments = new Experiments.Experiments(gPolicy);
904
905 // Trigger update, clock set to before any activation.
906
907 let now = baseDate;
908 defineNow(gPolicy, now);
909 yield experiments.updateManifest();
910 Assert.equal(observerFireCount, ++expectedObserverFireCount,
911 "Experiments observer should have been called.");
912 let list = yield experiments.getExperiments();
913 Assert.equal(list.length, 0, "Experiment list should be empty.");
914
915 let todayActive = yield experiments.lastActiveToday();
916 Assert.equal(todayActive, null, "No experiment active today.");
917
918 // Trigger update, clock set for the experiment to start.
919
920 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
921 defineNow(gPolicy, now);
922 yield experiments.updateManifest();
923 Assert.equal(observerFireCount, ++expectedObserverFireCount,
924 "Experiments observer should have been called.");
925
926 list = yield experiments.getExperiments();
927 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
928 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
929 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
930 Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
931 todayActive = yield experiments.lastActiveToday();
932 Assert.ok(todayActive, "todayActive() returns a value.");
933 Assert.equal(todayActive.id, list[0].id, "It returns the active experiment.");
934
935 // Trigger an update for the active experiment by changing it's hash (and xpi)
936 // in the manifest.
937
938 now = futureDate(now, 1 * MS_IN_ONE_DAY);
939 defineNow(gPolicy, now);
940 gManifestObject.experiments[0].xpiHash = EXPERIMENT1A_XPI_SHA1;
941 gManifestObject.experiments[0].xpiURL = gDataRoot + EXPERIMENT1A_XPI_NAME;
942 yield experiments.updateManifest();
943 Assert.equal(observerFireCount, ++expectedObserverFireCount,
944 "Experiments observer should have been called.");
945
946 list = yield experiments.getExperiments();
947 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
948 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
949 Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
950 Assert.equal(list[0].name, EXPERIMENT1A_NAME, "Experiments name should have been updated.");
951 todayActive = yield experiments.lastActiveToday();
952 Assert.equal(todayActive.id, list[0].id, "last active today is still sane.");
953
954 // Cleanup.
955
956 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
957 yield testCleanup(experiments);
958 });
959
960 // Tests that setting the disable flag for an active experiment
961 // stops it.
962
963 add_task(function* test_disableActiveExperiment() {
964 const OBSERVER_TOPIC = "experiments-changed";
965 let observerFireCount = 0;
966 let expectedObserverFireCount = 0;
967 let observer = () => ++observerFireCount;
968 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
969
970 // Dates the following tests are based on.
971
972 let baseDate = new Date(2014, 5, 1, 12);
973 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
974 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
975
976 // The manifest data we test with.
977
978 gManifestObject = {
979 "version": 1,
980 experiments: [
981 {
982 id: EXPERIMENT1_ID,
983 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
984 xpiHash: EXPERIMENT1_XPI_SHA1,
985 startTime: dateToSeconds(startDate),
986 endTime: dateToSeconds(endDate),
987 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
988 appName: ["XPCShell"],
989 channel: ["nightly"],
990 },
991 ],
992 };
993
994 let experiments = new Experiments.Experiments(gPolicy);
995
996 // Trigger update, clock set to before any activation.
997
998 let now = baseDate;
999 defineNow(gPolicy, now);
1000 yield experiments.updateManifest();
1001 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1002 "Experiments observer should have been called.");
1003 let list = yield experiments.getExperiments();
1004 Assert.equal(list.length, 0, "Experiment list should be empty.");
1005
1006 // Trigger update, clock set for the experiment to start.
1007
1008 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1009 defineNow(gPolicy, now);
1010 yield experiments.updateManifest();
1011 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1012 "Experiments observer should have been called.");
1013
1014 list = yield experiments.getExperiments();
1015 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1016 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1017 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
1018
1019 // Trigger an update with the experiment being disabled.
1020
1021 now = futureDate(now, 1 * MS_IN_ONE_DAY);
1022 defineNow(gPolicy, now);
1023 gManifestObject.experiments[0].disabled = true;
1024 yield experiments.updateManifest();
1025 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1026 "Experiments observer should have been called.");
1027
1028 list = yield experiments.getExperiments();
1029 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1030 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1031 Assert.equal(list[0].active, false, "Experiment 1 should be disabled.");
1032
1033 // Check that the experiment stays disabled.
1034
1035 now = futureDate(now, 1 * MS_IN_ONE_DAY);
1036 defineNow(gPolicy, now);
1037 delete gManifestObject.experiments[0].disabled;
1038 yield experiments.updateManifest();
1039
1040 list = yield experiments.getExperiments();
1041 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1042 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1043 Assert.equal(list[0].active, false, "Experiment 1 should still be disabled.");
1044
1045 // Cleanup.
1046
1047 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1048 yield testCleanup(experiments);
1049 });
1050
1051 // Test that:
1052 // * setting the frozen flag for a not-yet-started experiment keeps
1053 // it from starting
1054 // * after a removing the frozen flag, the experiment can still start
1055
1056 add_task(function* test_freezePendingExperiment() {
1057 const OBSERVER_TOPIC = "experiments-changed";
1058 let observerFireCount = 0;
1059 let expectedObserverFireCount = 0;
1060 let observer = () => ++observerFireCount;
1061 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
1062
1063 // Dates the following tests are based on.
1064
1065 let baseDate = new Date(2014, 5, 1, 12);
1066 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
1067 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
1068
1069 // The manifest data we test with.
1070
1071 gManifestObject = {
1072 "version": 1,
1073 experiments: [
1074 {
1075 id: EXPERIMENT1_ID,
1076 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1077 xpiHash: EXPERIMENT1_XPI_SHA1,
1078 startTime: dateToSeconds(startDate),
1079 endTime: dateToSeconds(endDate),
1080 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1081 appName: ["XPCShell"],
1082 channel: ["nightly"],
1083 },
1084 ],
1085 };
1086
1087 let experiments = new Experiments.Experiments(gPolicy);
1088
1089 // Trigger update, clock set to before any activation.
1090
1091 let now = baseDate;
1092 defineNow(gPolicy, now);
1093 yield experiments.updateManifest();
1094 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1095 "Experiments observer should have been called.");
1096 let list = yield experiments.getExperiments();
1097 Assert.equal(list.length, 0, "Experiment list should be empty.");
1098
1099 // Trigger update, clock set for the experiment to start but frozen.
1100
1101 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1102 defineNow(gPolicy, now);
1103 gManifestObject.experiments[0].frozen = true;
1104 yield experiments.updateManifest();
1105 Assert.equal(observerFireCount, expectedObserverFireCount,
1106 "Experiments observer should have not been called.");
1107
1108 list = yield experiments.getExperiments();
1109 Assert.equal(list.length, 0, "Experiment list should have no entries yet.");
1110
1111 // Trigger an update with the experiment not being frozen anymore.
1112
1113 now = futureDate(now, 1 * MS_IN_ONE_DAY);
1114 defineNow(gPolicy, now);
1115 delete gManifestObject.experiments[0].frozen;
1116 yield experiments.updateManifest();
1117 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1118 "Experiments observer should have been called.");
1119
1120 list = yield experiments.getExperiments();
1121 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1122 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1123 Assert.equal(list[0].active, true, "Experiment 1 should be active now.");
1124
1125 // Cleanup.
1126
1127 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1128 yield testCleanup(experiments);
1129 });
1130
1131 // Test that setting the frozen flag for an active experiment doesn't
1132 // stop it.
1133
1134 add_task(function* test_freezeActiveExperiment() {
1135 const OBSERVER_TOPIC = "experiments-changed";
1136 let observerFireCount = 0;
1137 let expectedObserverFireCount = 0;
1138 let observer = () => ++observerFireCount;
1139 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
1140
1141 // Dates the following tests are based on.
1142
1143 let baseDate = new Date(2014, 5, 1, 12);
1144 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
1145 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
1146
1147 // The manifest data we test with.
1148
1149 gManifestObject = {
1150 "version": 1,
1151 experiments: [
1152 {
1153 id: EXPERIMENT1_ID,
1154 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1155 xpiHash: EXPERIMENT1_XPI_SHA1,
1156 startTime: dateToSeconds(startDate),
1157 endTime: dateToSeconds(endDate),
1158 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1159 appName: ["XPCShell"],
1160 channel: ["nightly"],
1161 },
1162 ],
1163 };
1164
1165 let experiments = new Experiments.Experiments(gPolicy);
1166
1167 // Trigger update, clock set to before any activation.
1168
1169 let now = baseDate;
1170 defineNow(gPolicy, now);
1171 yield experiments.updateManifest();
1172 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1173 "Experiments observer should have been called.");
1174 let list = yield experiments.getExperiments();
1175 Assert.equal(list.length, 0, "Experiment list should be empty.");
1176
1177 // Trigger update, clock set for the experiment to start.
1178
1179 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1180 defineNow(gPolicy, now);
1181 yield experiments.updateManifest();
1182 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1183 "Experiments observer should have been called.");
1184
1185 list = yield experiments.getExperiments();
1186 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1187 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1188 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
1189 Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
1190
1191 // Trigger an update with the experiment being disabled.
1192
1193 now = futureDate(now, 1 * MS_IN_ONE_DAY);
1194 defineNow(gPolicy, now);
1195 gManifestObject.experiments[0].frozen = true;
1196 yield experiments.updateManifest();
1197 Assert.equal(observerFireCount, expectedObserverFireCount,
1198 "Experiments observer should have been called.");
1199
1200 list = yield experiments.getExperiments();
1201 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1202 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1203 Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
1204
1205 // Cleanup.
1206
1207 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1208 yield testCleanup(experiments);
1209 });
1210
1211 // Test that removing an active experiment from the manifest doesn't
1212 // stop it.
1213
1214 add_task(function* test_removeActiveExperiment() {
1215 const OBSERVER_TOPIC = "experiments-changed";
1216 let observerFireCount = 0;
1217 let expectedObserverFireCount = 0;
1218 let observer = () => ++observerFireCount;
1219 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
1220
1221 // Dates the following tests are based on.
1222
1223 let baseDate = new Date(2014, 5, 1, 12);
1224 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
1225 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
1226 let startDate2 = futureDate(baseDate, 20000 * MS_IN_ONE_DAY);
1227 let endDate2 = futureDate(baseDate, 30000 * MS_IN_ONE_DAY);
1228
1229 // The manifest data we test with.
1230
1231 gManifestObject = {
1232 "version": 1,
1233 experiments: [
1234 {
1235 id: EXPERIMENT1_ID,
1236 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1237 xpiHash: EXPERIMENT1_XPI_SHA1,
1238 startTime: dateToSeconds(startDate),
1239 endTime: dateToSeconds(endDate),
1240 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1241 appName: ["XPCShell"],
1242 channel: ["nightly"],
1243 },
1244 {
1245 id: EXPERIMENT2_ID,
1246 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1247 xpiHash: EXPERIMENT2_XPI_SHA1,
1248 startTime: dateToSeconds(startDate2),
1249 endTime: dateToSeconds(endDate2),
1250 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1251 appName: ["XPCShell"],
1252 channel: ["nightly"],
1253 },
1254 ],
1255 };
1256
1257 let experiments = new Experiments.Experiments(gPolicy);
1258
1259 // Trigger update, clock set to before any activation.
1260
1261 let now = baseDate;
1262 defineNow(gPolicy, now);
1263 yield experiments.updateManifest();
1264 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1265 "Experiments observer should have been called.");
1266 let list = yield experiments.getExperiments();
1267 Assert.equal(list.length, 0, "Experiment list should be empty.");
1268
1269 // Trigger update, clock set for the experiment to start.
1270
1271 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1272 defineNow(gPolicy, now);
1273 yield experiments.updateManifest();
1274 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1275 "Experiments observer should have been called.");
1276
1277 list = yield experiments.getExperiments();
1278 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1279 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1280 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
1281 Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
1282
1283 // Trigger an update with experiment 1 missing from the manifest
1284
1285 now = futureDate(now, 1 * MS_IN_ONE_DAY);
1286 defineNow(gPolicy, now);
1287 gManifestObject.experiments[0].frozen = true;
1288 yield experiments.updateManifest();
1289 Assert.equal(observerFireCount, expectedObserverFireCount,
1290 "Experiments observer should have been called.");
1291
1292 list = yield experiments.getExperiments();
1293 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1294 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1295 Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
1296
1297 // Cleanup.
1298
1299 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1300 yield testCleanup(experiments);
1301 });
1302
1303 // Test that we correctly handle experiment start & install failures.
1304
1305 add_task(function* test_invalidUrl() {
1306 const OBSERVER_TOPIC = "experiments-changed";
1307 let observerFireCount = 0;
1308 let expectedObserverFireCount = 0;
1309 let observer = () => ++observerFireCount;
1310 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
1311
1312 // Dates the following tests are based on.
1313
1314 let baseDate = new Date(2014, 5, 1, 12);
1315 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
1316 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
1317
1318 // The manifest data we test with.
1319
1320 gManifestObject = {
1321 "version": 1,
1322 experiments: [
1323 {
1324 id: EXPERIMENT1_ID,
1325 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME + ".invalid",
1326 xpiHash: EXPERIMENT1_XPI_SHA1,
1327 startTime: 0,
1328 endTime: dateToSeconds(endDate),
1329 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1330 appName: ["XPCShell"],
1331 channel: ["nightly"],
1332 },
1333 ],
1334 };
1335
1336 let experiments = new Experiments.Experiments(gPolicy);
1337
1338 // Trigger update, clock set for the experiment to start.
1339
1340 let now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1341 defineNow(gPolicy, now);
1342 gTimerScheduleOffset = null;
1343
1344 yield experiments.updateManifest();
1345 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1346 "Experiments observer should have been called.");
1347 Assert.equal(gTimerScheduleOffset, null, "No new timer should have been scheduled.");
1348
1349 let list = yield experiments.getExperiments();
1350 Assert.equal(list.length, 0, "Experiment list should be empty.");
1351
1352 // Cleanup.
1353
1354 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1355 yield testCleanup(experiments);
1356 });
1357
1358 // Test that we handle it properly when active experiment addons are being
1359 // uninstalled.
1360
1361 add_task(function* test_unexpectedUninstall() {
1362 const OBSERVER_TOPIC = "experiments-changed";
1363 let observerFireCount = 0;
1364 let expectedObserverFireCount = 0;
1365 let observer = () => ++observerFireCount;
1366 Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
1367
1368 // Dates the following tests are based on.
1369
1370 let baseDate = new Date(2014, 5, 1, 12);
1371 let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
1372 let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
1373
1374 // The manifest data we test with.
1375
1376 gManifestObject = {
1377 "version": 1,
1378 experiments: [
1379 {
1380 id: EXPERIMENT1_ID,
1381 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1382 xpiHash: EXPERIMENT1_XPI_SHA1,
1383 startTime: dateToSeconds(startDate),
1384 endTime: dateToSeconds(endDate),
1385 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1386 appName: ["XPCShell"],
1387 channel: ["nightly"],
1388 },
1389 ],
1390 };
1391
1392 let experiments = new Experiments.Experiments(gPolicy);
1393
1394 // Trigger update, clock set to before any activation.
1395
1396 let now = baseDate;
1397 defineNow(gPolicy, now);
1398 yield experiments.updateManifest();
1399 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1400 "Experiments observer should have been called.");
1401 let list = yield experiments.getExperiments();
1402 Assert.equal(list.length, 0, "Experiment list should be empty.");
1403
1404 // Trigger update, clock set for the experiment to start.
1405
1406 now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1407 defineNow(gPolicy, now);
1408 yield experiments.updateManifest();
1409 Assert.equal(observerFireCount, ++expectedObserverFireCount,
1410 "Experiments observer should have been called.");
1411
1412 list = yield experiments.getExperiments();
1413 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1414 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1415 Assert.equal(list[0].active, true, "Experiment 1 should be active.");
1416
1417 // Uninstall the addon through the addon manager instead of stopping it through
1418 // the experiments API.
1419
1420 yield AddonTestUtils.uninstallAddonByID(EXPERIMENT1_ID);
1421 yield experiments._mainTask;
1422
1423 yield experiments.notify();
1424
1425 list = yield experiments.getExperiments();
1426 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
1427 Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1428 Assert.equal(list[0].active, false, "Experiment 1 should not be active anymore.");
1429
1430 // Cleanup.
1431
1432 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1433 yield testCleanup(experiments);
1434 });
1435
1436 // If the Addon Manager knows of an experiment that we don't, it should get
1437 // uninstalled.
1438 add_task(function* testUnknownExperimentsUninstalled() {
1439 let experiments = new Experiments.Experiments(gPolicy);
1440
1441 let addons = yield getExperimentAddons();
1442 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are present.");
1443
1444 // Simulate us not listening.
1445 experiments._unregisterWithAddonManager();
1446 yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
1447 experiments._registerWithAddonManager();
1448
1449 addons = yield getExperimentAddons();
1450 Assert.equal(addons.length, 1, "Experiment 1 installed via AddonManager");
1451
1452 // Simulate no known experiments.
1453 gManifestObject = {
1454 "version": 1,
1455 experiments: [],
1456 };
1457
1458 yield experiments.updateManifest();
1459 let fromManifest = yield experiments.getExperiments();
1460 Assert.equal(fromManifest.length, 0, "No experiments known in manifest.");
1461
1462 // And the unknown add-on should be gone.
1463 addons = yield getExperimentAddons();
1464 Assert.equal(addons.length, 0, "Experiment 1 was uninstalled.");
1465
1466 yield testCleanup(experiments);
1467 });
1468
1469 // If someone else installs an experiment add-on, we detect and stop that.
1470 add_task(function* testForeignExperimentInstall() {
1471 let experiments = new Experiments.Experiments(gPolicy);
1472
1473 gManifestObject = {
1474 "version": 1,
1475 experiments: [],
1476 };
1477
1478 yield experiments.init();
1479
1480 let addons = yield getExperimentAddons();
1481 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons present.");
1482
1483 let failed = false;
1484 try {
1485 yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
1486 } catch (ex) {
1487 failed = true;
1488 }
1489 Assert.ok(failed, "Add-on install should not have completed successfully");
1490 addons = yield getExperimentAddons();
1491 Assert.equal(addons.length, 0, "Add-on install should have been cancelled.");
1492
1493 yield testCleanup(experiments);
1494 });
1495
1496 // Experiment add-ons will be disabled after Addon Manager restarts. Ensure
1497 // we enable them automatically.
1498 add_task(function* testEnabledAfterRestart() {
1499 let experiments = new Experiments.Experiments(gPolicy);
1500
1501 gManifestObject = {
1502 "version": 1,
1503 experiments: [
1504 {
1505 id: EXPERIMENT1_ID,
1506 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1507 xpiHash: EXPERIMENT1_XPI_SHA1,
1508 startTime: gPolicy.now().getTime() / 1000 - 60,
1509 endTime: gPolicy.now().getTime() / 1000 + 60,
1510 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1511 appName: ["XPCShell"],
1512 channel: ["nightly"],
1513 },
1514 ],
1515 };
1516
1517 let addons = yield getExperimentAddons();
1518 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
1519
1520 yield experiments.updateManifest();
1521 let fromManifest = yield experiments.getExperiments();
1522 Assert.equal(fromManifest.length, 1, "A single experiment is known.");
1523
1524 addons = yield getExperimentAddons();
1525 Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
1526 Assert.ok(addons[0].isActive, "That experiment is active.");
1527
1528 dump("Restarting Addon Manager\n");
1529 experiments._unregisterWithAddonManager();
1530 restartManager();
1531 experiments._registerWithAddonManager();
1532
1533 addons = yield getExperimentAddons();
1534 Assert.equal(addons.length, 1, "The experiment is still there after restart.");
1535 Assert.ok(addons[0].userDisabled, "But it is disabled.");
1536 Assert.equal(addons[0].isActive, false, "And not active.");
1537
1538 yield experiments.updateManifest();
1539 Assert.ok(addons[0].isActive, "It activates when the manifest is evaluated.");
1540
1541 yield testCleanup(experiments);
1542 });
1543
1544 // Test coverage for an add-on uninstall disabling the experiment and that it stays
1545 // disabled over restarts.
1546 add_task(function* test_foreignUninstallAndRestart() {
1547 let experiments = new Experiments.Experiments(gPolicy);
1548
1549 gManifestObject = {
1550 "version": 1,
1551 experiments: [
1552 {
1553 id: EXPERIMENT1_ID,
1554 xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
1555 xpiHash: EXPERIMENT1_XPI_SHA1,
1556 startTime: gPolicy.now().getTime() / 1000 - 60,
1557 endTime: gPolicy.now().getTime() / 1000 + 60,
1558 maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
1559 appName: ["XPCShell"],
1560 channel: ["nightly"],
1561 },
1562 ],
1563 };
1564
1565 let addons = yield getExperimentAddons();
1566 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
1567
1568 yield experiments.updateManifest();
1569 let experimentList = yield experiments.getExperiments();
1570 Assert.equal(experimentList.length, 1, "A single experiment is known.");
1571
1572 addons = yield getExperimentAddons();
1573 Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
1574 Assert.ok(addons[0].isActive, "That experiment is active.");
1575
1576 yield AddonTestUtils.uninstallAddonByID(EXPERIMENT1_ID);
1577 yield experiments._mainTask;
1578
1579 let addons = yield getExperimentAddons();
1580 Assert.equal(addons.length, 0, "Experiment add-on should have been removed.");
1581
1582 experimentList = yield experiments.getExperiments();
1583 Assert.equal(experimentList.length, 1, "A single experiment is known.");
1584 Assert.equal(experimentList[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1585 Assert.ok(!experimentList[0].active, "Experiment 1 should not be active anymore.");
1586
1587 // Fake restart behaviour.
1588 experiments.uninit();
1589 restartManager();
1590 experiments = new Experiments.Experiments(gPolicy);
1591 yield experiments.updateManifest();
1592
1593 let addons = yield getExperimentAddons();
1594 Assert.equal(addons.length, 0, "No experiment add-ons installed.");
1595
1596 experimentList = yield experiments.getExperiments();
1597 Assert.equal(experimentList.length, 1, "A single experiment is known.");
1598 Assert.equal(experimentList[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
1599 Assert.ok(!experimentList[0].active, "Experiment 1 should not be active.");
1600
1601 yield testCleanup(experiments);
1602 });

mercurial