diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index ddf278ff2878..cc281b5050cc 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -55,6 +55,9 @@ user_pref("font.size.inflation.minTwips", 0); user_pref("experiments.enabled", true); user_pref("experiments.supported", true); user_pref("experiments.logging.level", "Trace"); +// Point the manifest at something local so we don't risk it hitting production +// data and installing experiments that may vary over time. +user_pref("experiments.manifest.uri", "http://%(server)s/experiments-dummy/manifest"); // Only load extensions from the application and user profile // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION diff --git a/toolkit/mozapps/extensions/test/browser/browser_experiments.js b/toolkit/mozapps/extensions/test/browser/browser_experiments.js index 79f63ee07df9..d592a3f2b05a 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_experiments.js +++ b/toolkit/mozapps/extensions/test/browser/browser_experiments.js @@ -2,27 +2,44 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ +let {AddonTestUtils} = Components.utils.import("resource://testing-common/AddonManagerTesting.jsm", {}); +let {HttpServer} = Components.utils.import("resource://testing-common/httpd.js", {}); + let gManagerWindow; let gCategoryUtilities; -let gInstalledAddons = []; -let gContext = this; +let gExperiments; +let gHttpServer; + +function getExperimentAddons() { + let deferred = Promise.defer(); + AddonManager.getAddonsByTypes(["experiment"], (addons) => { + deferred.resolve(addons); + }); + return deferred.promise; +} add_task(function* initializeState() { gManagerWindow = yield open_manager(); gCategoryUtilities = new CategoryUtilities(gManagerWindow); + registerCleanupFunction(() => { + if (gHttpServer) { + gHttpServer.stop(() => {}); + } + }); + // The Experiments Manager will interfere with us by preventing installs // of experiments it doesn't know about. We remove it from the equation // because here we are only concerned with core Addon Manager operation, // not the superset Experiments Manager has imposed. if ("@mozilla.org/browser/experiments-service;1" in Components.classes) { - Components.utils.import("resource:///modules/experiments/Experiments.jsm", gContext); - + let tmp = {}; + Components.utils.import("resource:///modules/experiments/Experiments.jsm", tmp); // There is a race condition between XPCOM service initialization and // this test running. We have to initialize the instance first, then // uninitialize it to prevent this. - let instance = gContext.Experiments.instance(); - yield instance.uninit(); + gExperiments = tmp.Experiments.instance(); + yield gExperiments.uninit(); } }); @@ -30,7 +47,6 @@ add_task(function* initializeState() { // should be hidden. add_task(function* testInitialState() { Assert.ok(gCategoryUtilities.get("experiment", false), "Experiment tab is defined."); - Assert.ok(!gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab hidden by default."); }); @@ -44,7 +60,6 @@ add_task(function* testExperimentInfoNotVisible() { // and that tab should have some messages. add_task(function* testActiveExperiment() { let addon = yield install_addon("addons/browser_experiment1.xpi"); - gInstalledAddons.push(addon); Assert.ok(addon.userDisabled, "Add-on is disabled upon initial install."); Assert.equal(addon.isActive, false, "Add-on is not active."); @@ -143,14 +158,127 @@ add_task(function* testButtonPresence() { is_element_hidden(el, "Enable button not visible."); }); +// Remove the add-on we've been testing with. add_task(function* testCleanup() { - for (let addon of gInstalledAddons) { - addon.uninstall(); + yield AddonTestUtils.uninstallAddonByID("test-experiment1@experiments.mozilla.org"); + // Verify some conditions, just in case. + let addons = yield getExperimentAddons(); + Assert.equal(addons.length, 0, "No experiment add-ons are installed."); +}); + +// We need to initialize the experiments service for the following tests. +add_task(function* initializeExperiments() { + if (!gExperiments) { + return; } + // We need to remove the cache file to help ensure consistent state. + yield OS.File.remove(gExperiments._cacheFilePath); + + info("Initializing experiments service."); + yield gExperiments.init(); + info("Experiments service finished first run."); + + // Check conditions, just to be sure. + let experiments = yield gExperiments.getExperiments(); + Assert.equal(experiments.length, 0, "No experiments known to the service."); +}); + +// The following tests should ideally live in browser/experiments/. However, +// they rely on some of the helper functions from head.js, which can't easily +// be consumed from other directories. So, they live here. + +add_task(function* testActivateExperiment() { + if (!gExperiments) { + info("Skipping experiments test because that feature isn't available."); + return; + } + + gHttpServer = new HttpServer(); + gHttpServer.start(-1); + let root = "http://localhost:" + gHttpServer.identity.primaryPort + "/"; + gHttpServer.registerPathHandler("/manifest", (request, response) => { + response.setStatusLine(null, 200, "OK"); + response.write(JSON.stringify({ + "version": 1, + "experiments": [ + { + id: "experiment-1", + xpiURL: TESTROOT + "addons/browser_experiment1.xpi", + xpiHash: "IRRELEVANT", + startTime: Date.now() / 1000 - 3600, + endTime: Date.now() / 1000 + 3600, + maxActiveSeconds: 600, + appName: [Services.appinfo.name], + channel: [gExperiments._policy.updatechannel()], + }, + ], + })); + response.processAsync(); + response.finish(); + }); + + Services.prefs.setBoolPref("experiments.manifest.cert.checkAttributes", false); + Services.prefs.setCharPref("experiments.manifest.uri", root + "manifest"); + registerCleanupFunction(() => { + Services.prefs.clearUserPref("experiments.manifest.cert.checkAttributes"); + Services.prefs.clearUserPref("experiments.manifest.uri"); + }); + + // This makes testing easier. + gExperiments._policy.ignoreHashes = true; + registerCleanupFunction(() => { gExperiments._policy.ignoreHashes = false; }); + + info("Manually updating experiments manifest."); + yield gExperiments.updateManifest(); + info("Experiments update complete."); + + let deferred = Promise.defer(); + gHttpServer.stop(() => { + gHttpServer = null; + + info("getting experiment by ID"); + AddonManager.getAddonByID("test-experiment1@experiments.mozilla.org", (addon) => { + Assert.ok(addon, "Add-on installed via Experiments manager."); + + deferred.resolve(); + }); + }); + + yield deferred.promise; + + Assert.ok(gCategoryUtilities.isTypeVisible, "experiment", "Experiment tab visible."); + yield gCategoryUtilities.openType("experiment"); + let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0]; + is_element_visible(el, "Experiment info is visible on experiment tab."); +}); + +add_task(function testDeactivateExperiment() { + if (!gExperiments) { + return; + } + + yield gExperiments._updateExperiments({ + "version": 1, + "experiments": [], + }); + + yield gExperiments.disableExperiment("testing"); +}); + +add_task(function* testCleanup() { + if (gExperiments) { + // We perform the uninit/init cycle to purge any leftover state. + yield OS.File.remove(gExperiments._cacheFilePath); + yield gExperiments.uninit(); + yield gExperiments.init(); + } + + // Check post-conditions. + let addons = yield getExperimentAddons(); + Assert.equal(addons.length, 0, "No experiment add-ons are installed."); + yield close_manager(gManagerWindow); - if ("@mozilla.org/browser/experiments-service;1" in Components.classes) { - yield gContext.Experiments.instance().init(); - } }); +