Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/ */
4 "use strict";
6 Cu.import("resource://testing-common/httpd.js");
7 Cu.import("resource://testing-common/AddonManagerTesting.jsm");
9 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
10 "resource:///modules/experiments/Experiments.jsm");
12 const FILE_MANIFEST = "experiments.manifest";
13 const MANIFEST_HANDLER = "manifests/handler";
15 const SEC_IN_ONE_DAY = 24 * 60 * 60;
16 const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
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;
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 }
37 function testCleanup(experimentsInstance) {
38 return Task.spawn(function* () {
39 yield experimentsInstance.uninit();
40 yield removeCacheFile();
41 yield uninstallExperimentAddons();
42 restartManager();
43 });
44 }
46 function run_test() {
47 run_next_test();
48 }
50 add_task(function* test_setup() {
51 loadAddonManager();
52 gProfileDir = do_get_profile();
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(() => {}));
69 disableCertificateChecks();
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);
77 gReporter = yield getReporter("json_payload_simple");
78 yield gReporter.collectMeasurements();
79 let payload = yield gReporter.getJSONPayload(true);
80 do_register_cleanup(() => gReporter._shutdown());
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 });
90 add_task(function* test_contract() {
91 Cc["@mozilla.org/browser/experiments-service;1"].getService();
92 });
94 // Test basic starting and stopping of experiments.
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);
103 // Dates the following tests are based on.
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);
111 // The manifest data we test with.
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 };
139 // Data to compare the result of Experiments.getExperiments() against.
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 ];
154 let experiments = new Experiments.Experiments(gPolicy);
156 // Trigger update, clock set to before any activation.
157 // Use updateManifest() to provide for coverage of that path.
159 let now = baseDate;
160 gTimerScheduleOffset = -1;
161 defineNow(gPolicy, now);
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");
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.");
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 }
182 // Trigger update, clock set for experiment 1 to start.
184 now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
185 gTimerScheduleOffset = -1;
186 defineNow(gPolicy, now);
188 yield experiments.updateManifest();
189 Assert.equal(observerFireCount, ++expectedObserverFireCount,
190 "Experiments observer should have been called.");
192 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
193 "getActiveExperimentID should return the active experiment1");
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.");
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 }
207 let b = yield experiments.getExperimentBranch();
208 Assert.strictEqual(b, null, "getExperimentBranch should return null by default");
210 b = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
211 Assert.strictEqual(b, null, "getExperimentsBranch should return null (with id)");
213 yield experiments.setExperimentBranch(EXPERIMENT1_ID, "foo");
214 b = yield experiments.getExperimentBranch();
215 Assert.strictEqual(b, "foo", "getExperimentsBranch should return the set value");
217 Assert.equal(observerFireCount, ++expectedObserverFireCount,
218 "Experiments observer should have been called.");
220 Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
221 "Experiment re-evaluation should have been scheduled correctly.");
223 // Trigger update, clock set for experiment 1 to stop.
225 now = futureDate(endDate1, 1000);
226 gTimerScheduleOffset = -1;
227 defineNow(gPolicy, now);
229 yield experiments.updateManifest();
230 Assert.equal(observerFireCount, ++expectedObserverFireCount,
231 "Experiments observer should have been called.");
233 Assert.equal(experiments.getActiveExperimentID(), null,
234 "getActiveExperimentID should return null again");
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.");
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 }
248 Assert.equal(gTimerScheduleOffset, startDate2 - now,
249 "Experiment re-evaluation should have been scheduled correctly.");
251 // Trigger update, clock set for experiment 2 to start.
252 // Use notify() to provide for coverage of that path.
254 now = startDate2;
255 gTimerScheduleOffset = -1;
256 defineNow(gPolicy, now);
258 yield experiments.notify();
259 Assert.equal(observerFireCount, ++expectedObserverFireCount,
260 "Experiments observer should have been called.");
262 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT2_ID,
263 "getActiveExperimentID should return the active experiment2");
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.");
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 }
280 Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
281 "Experiment re-evaluation should have been scheduled correctly.");
283 // Trigger update, clock set for experiment 2 to stop.
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.");
292 Assert.equal(experiments.getActiveExperimentID(), null,
293 "getActiveExperimentID should return null again2");
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.");
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 }
310 // Cleanup.
312 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
313 yield testCleanup(experiments);
314 });
316 add_task(function* test_getActiveExperimentID() {
317 // Check that getActiveExperimentID returns the correct result even
318 // after .uninit()
320 // Dates the following tests are based on.
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);
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 };
342 let now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
343 gTimerScheduleOffset = -1;
344 defineNow(gPolicy, now);
346 let experiments = new Experiments.Experiments(gPolicy);
347 yield experiments.updateManifest();
349 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
350 "getActiveExperimentID should return the active experiment1");
352 yield experiments.uninit();
353 Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
354 "getActiveExperimentID should return the active experiment1 after uninit()");
356 yield testCleanup(experiments);
357 });
359 // Test that we handle the experiments addon already being
360 // installed properly.
361 // We should just pave over them.
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);
370 // Dates the following tests are based on.
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);
376 // The manifest data we test with.
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 };
394 let experiments = new Experiments.Experiments(gPolicy);
396 // Trigger update, clock set to before any activation.
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.");
406 // Trigger update, clock set for the experiment to start.
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.");
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.");
420 let addons = yield getExperimentAddons();
421 Assert.equal(addons.length, 1, "1 add-on is installed.");
423 // Install conflicting addon.
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.");
433 // Cleanup.
435 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
436 yield testCleanup(experiments);
437 });
439 add_task(function* test_lastActiveToday() {
440 let experiments = new Experiments.Experiments(gPolicy);
442 replaceExperiments(experiments, FAKE_EXPERIMENTS_1);
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.");
449 let lastActive = yield experiments.lastActiveToday();
450 Assert.equal(e[0], lastActive, "Last active object is expected.");
452 replaceExperiments(experiments, FAKE_EXPERIMENTS_2);
453 e = yield experiments.getExperiments();
454 Assert.equal(e.length, 2, "Monkeypatch successful.");
456 defineNow(gPolicy, e[0].endDate);
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.");
462 yield testCleanup(experiments);
463 });
465 // Test explicitly disabling experiments.
467 add_task(function* test_disableExperiment() {
468 // Dates this test is based on.
470 let startDate = new Date(2004, 10, 9, 12);
471 let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
473 // The manifest data we test with.
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 };
491 // Data to compare the result of Experiments.getExperiments() against.
493 let experimentInfo = {
494 id: EXPERIMENT1_ID,
495 name: EXPERIMENT1_NAME,
496 description: "Yet another experiment that experiments experimentally.",
497 };
499 let experiments = new Experiments.Experiments(gPolicy);
501 // Trigger update, clock set for the experiment to start.
503 let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
504 defineNow(gPolicy, now);
505 yield experiments.updateManifest();
507 let list = yield experiments.getExperiments();
508 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
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 }
517 // Test disabling the experiment.
519 now = futureDate(now, 1 * MS_IN_ONE_DAY);
520 defineNow(gPolicy, now);
521 yield experiments.disableExperiment("foo");
523 list = yield experiments.getExperiments();
524 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
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 }
533 // Test that updating the list doesn't re-enable it.
535 now = futureDate(now, 1 * MS_IN_ONE_DAY);
536 defineNow(gPolicy, now);
537 yield experiments.updateManifest();
539 list = yield experiments.getExperiments();
540 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
542 for (let k of Object.keys(experimentInfo)) {
543 Assert.equal(experimentInfo[k], list[0][k],
544 "Property " + k + " should match reference data.");
545 }
547 yield testCleanup(experiments);
548 });
550 add_task(function* test_disableExperimentsFeature() {
551 // Dates this test is based on.
553 let startDate = new Date(2004, 10, 9, 12);
554 let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
556 // The manifest data we test with.
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 };
574 // Data to compare the result of Experiments.getExperiments() against.
576 let experimentInfo = {
577 id: EXPERIMENT1_ID,
578 name: EXPERIMENT1_NAME,
579 description: "Yet another experiment that experiments experimentally.",
580 };
582 let experiments = new Experiments.Experiments(gPolicy);
583 Assert.equal(experiments.enabled, true, "Experiments feature should be enabled.");
585 // Trigger update, clock set for the experiment to start.
587 let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
588 defineNow(gPolicy, now);
589 yield experiments.updateManifest();
591 let list = yield experiments.getExperiments();
592 Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
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 }
601 // Test disabling experiments.
603 experiments._toggleExperimentsEnabled(false);
604 yield experiments.notify();
605 Assert.equal(experiments.enabled, false, "Experiments feature should be disabled now.");
607 list = yield experiments.getExperiments();
608 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
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 }
617 // Test that updating the list doesn't re-enable it.
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 }
627 list = yield experiments.getExperiments();
628 Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
630 for (let k of Object.keys(experimentInfo)) {
631 Assert.equal(experimentInfo[k], list[0][k],
632 "Property " + k + " should match reference data.");
633 }
635 yield testCleanup(experiments);
636 });
638 // Test that after a failed experiment install:
639 // * the next applicable experiment gets installed
640 // * changing the experiments data later triggers re-evaluation
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);
649 // Dates the following tests are based on.
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);
655 // The manifest data we test with.
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 };
683 // Data to compare the result of Experiments.getExperiments() against.
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 ];
698 let experiments = new Experiments.Experiments(gPolicy);
700 // Trigger update, clock set to before any activation.
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.");
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.
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.");
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.");
727 // Trigger update, clock set for experiment 2 to stop.
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.");
735 experimentListData[0].active = false;
736 experimentListData[0].endDate = now;
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.");
743 // Trigger update with a fixed entry for experiment 1,
744 // which should get re-evaluated & started now.
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.");
753 experimentListData[0].active = true;
754 experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
756 list = yield experiments.getExperiments();
757 Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
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 }
767 yield testCleanup(experiments);
768 });
770 // Test that after an experiment was disabled by user action,
771 // the experiment is not activated again if manifest data changes.
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);
780 // Dates the following tests are based on.
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);
786 // The manifest data we test with.
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 };
804 let experiments = new Experiments.Experiments(gPolicy);
806 // Trigger update, clock set to before any activation.
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.");
816 // Trigger update, clock set for experiment 1 to start.
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.");
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.");
832 // Explicitly disable an experiment.
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.");
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.");
848 // Trigger an update with a faked change for experiment 1.
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.");
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.");
863 // Cleanup.
865 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
866 yield testCleanup(experiments);
867 });
869 // Test that changing the hash for an active experiments triggers an
870 // update for it.
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);
879 // Dates the following tests are based on.
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);
885 // The manifest data we test with.
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 };
903 let experiments = new Experiments.Experiments(gPolicy);
905 // Trigger update, clock set to before any activation.
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.");
915 let todayActive = yield experiments.lastActiveToday();
916 Assert.equal(todayActive, null, "No experiment active today.");
918 // Trigger update, clock set for the experiment to start.
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.");
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.");
935 // Trigger an update for the active experiment by changing it's hash (and xpi)
936 // in the manifest.
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.");
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.");
954 // Cleanup.
956 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
957 yield testCleanup(experiments);
958 });
960 // Tests that setting the disable flag for an active experiment
961 // stops it.
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);
970 // Dates the following tests are based on.
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);
976 // The manifest data we test with.
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 };
994 let experiments = new Experiments.Experiments(gPolicy);
996 // Trigger update, clock set to before any activation.
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.");
1006 // Trigger update, clock set for the experiment to start.
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.");
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.");
1019 // Trigger an update with the experiment being disabled.
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.");
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.");
1033 // Check that the experiment stays disabled.
1035 now = futureDate(now, 1 * MS_IN_ONE_DAY);
1036 defineNow(gPolicy, now);
1037 delete gManifestObject.experiments[0].disabled;
1038 yield experiments.updateManifest();
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.");
1045 // Cleanup.
1047 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1048 yield testCleanup(experiments);
1049 });
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
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);
1063 // Dates the following tests are based on.
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);
1069 // The manifest data we test with.
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 };
1087 let experiments = new Experiments.Experiments(gPolicy);
1089 // Trigger update, clock set to before any activation.
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.");
1099 // Trigger update, clock set for the experiment to start but frozen.
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.");
1108 list = yield experiments.getExperiments();
1109 Assert.equal(list.length, 0, "Experiment list should have no entries yet.");
1111 // Trigger an update with the experiment not being frozen anymore.
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.");
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.");
1125 // Cleanup.
1127 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1128 yield testCleanup(experiments);
1129 });
1131 // Test that setting the frozen flag for an active experiment doesn't
1132 // stop it.
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);
1141 // Dates the following tests are based on.
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);
1147 // The manifest data we test with.
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 };
1165 let experiments = new Experiments.Experiments(gPolicy);
1167 // Trigger update, clock set to before any activation.
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.");
1177 // Trigger update, clock set for the experiment to start.
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.");
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.");
1191 // Trigger an update with the experiment being disabled.
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.");
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.");
1205 // Cleanup.
1207 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1208 yield testCleanup(experiments);
1209 });
1211 // Test that removing an active experiment from the manifest doesn't
1212 // stop it.
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);
1221 // Dates the following tests are based on.
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);
1229 // The manifest data we test with.
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 };
1257 let experiments = new Experiments.Experiments(gPolicy);
1259 // Trigger update, clock set to before any activation.
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.");
1269 // Trigger update, clock set for the experiment to start.
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.");
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.");
1283 // Trigger an update with experiment 1 missing from the manifest
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.");
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.");
1297 // Cleanup.
1299 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1300 yield testCleanup(experiments);
1301 });
1303 // Test that we correctly handle experiment start & install failures.
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);
1312 // Dates the following tests are based on.
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);
1318 // The manifest data we test with.
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 };
1336 let experiments = new Experiments.Experiments(gPolicy);
1338 // Trigger update, clock set for the experiment to start.
1340 let now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
1341 defineNow(gPolicy, now);
1342 gTimerScheduleOffset = null;
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.");
1349 let list = yield experiments.getExperiments();
1350 Assert.equal(list.length, 0, "Experiment list should be empty.");
1352 // Cleanup.
1354 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1355 yield testCleanup(experiments);
1356 });
1358 // Test that we handle it properly when active experiment addons are being
1359 // uninstalled.
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);
1368 // Dates the following tests are based on.
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);
1374 // The manifest data we test with.
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 };
1392 let experiments = new Experiments.Experiments(gPolicy);
1394 // Trigger update, clock set to before any activation.
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.");
1404 // Trigger update, clock set for the experiment to start.
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.");
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.");
1417 // Uninstall the addon through the addon manager instead of stopping it through
1418 // the experiments API.
1420 yield AddonTestUtils.uninstallAddonByID(EXPERIMENT1_ID);
1421 yield experiments._mainTask;
1423 yield experiments.notify();
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.");
1430 // Cleanup.
1432 Services.obs.removeObserver(observer, OBSERVER_TOPIC);
1433 yield testCleanup(experiments);
1434 });
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);
1441 let addons = yield getExperimentAddons();
1442 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are present.");
1444 // Simulate us not listening.
1445 experiments._unregisterWithAddonManager();
1446 yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
1447 experiments._registerWithAddonManager();
1449 addons = yield getExperimentAddons();
1450 Assert.equal(addons.length, 1, "Experiment 1 installed via AddonManager");
1452 // Simulate no known experiments.
1453 gManifestObject = {
1454 "version": 1,
1455 experiments: [],
1456 };
1458 yield experiments.updateManifest();
1459 let fromManifest = yield experiments.getExperiments();
1460 Assert.equal(fromManifest.length, 0, "No experiments known in manifest.");
1462 // And the unknown add-on should be gone.
1463 addons = yield getExperimentAddons();
1464 Assert.equal(addons.length, 0, "Experiment 1 was uninstalled.");
1466 yield testCleanup(experiments);
1467 });
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);
1473 gManifestObject = {
1474 "version": 1,
1475 experiments: [],
1476 };
1478 yield experiments.init();
1480 let addons = yield getExperimentAddons();
1481 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons present.");
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.");
1493 yield testCleanup(experiments);
1494 });
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);
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 };
1517 let addons = yield getExperimentAddons();
1518 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
1520 yield experiments.updateManifest();
1521 let fromManifest = yield experiments.getExperiments();
1522 Assert.equal(fromManifest.length, 1, "A single experiment is known.");
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.");
1528 dump("Restarting Addon Manager\n");
1529 experiments._unregisterWithAddonManager();
1530 restartManager();
1531 experiments._registerWithAddonManager();
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.");
1538 yield experiments.updateManifest();
1539 Assert.ok(addons[0].isActive, "It activates when the manifest is evaluated.");
1541 yield testCleanup(experiments);
1542 });
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);
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 };
1565 let addons = yield getExperimentAddons();
1566 Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
1568 yield experiments.updateManifest();
1569 let experimentList = yield experiments.getExperiments();
1570 Assert.equal(experimentList.length, 1, "A single experiment is known.");
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.");
1576 yield AddonTestUtils.uninstallAddonByID(EXPERIMENT1_ID);
1577 yield experiments._mainTask;
1579 let addons = yield getExperimentAddons();
1580 Assert.equal(addons.length, 0, "Experiment add-on should have been removed.");
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.");
1587 // Fake restart behaviour.
1588 experiments.uninit();
1589 restartManager();
1590 experiments = new Experiments.Experiments(gPolicy);
1591 yield experiments.updateManifest();
1593 let addons = yield getExperimentAddons();
1594 Assert.equal(addons.length, 0, "No experiment add-ons installed.");
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.");
1601 yield testCleanup(experiments);
1602 });