browser/experiments/test/xpcshell/test_api.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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: [
  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: [
  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: [
  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       },
  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: [
  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: [
  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;
  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: [
  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: [
  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 });

mercurial