/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://testing-common/AddonManagerTesting.jsm"); const PREF_EXPERIMENTS_ENABLED = "experiments.enabled"; const PREF_LOGGING_LEVEL = "experiments.logging.level"; const PREF_LOGGING_DUMP = "experiments.logging.dump"; const PREF_MANIFEST_URI = "experiments.manifest.uri"; const PREF_FETCHINTERVAL = "experiments.manifest.fetchIntervalSeconds"; const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled"; function getExperimentPath(base) { let p = do_get_cwd(); p.append(base); return p.path; } function sha1File(path) { let f = Cc["@mozilla.org/file/local;1"] .createInstance(Ci.nsILocalFile); f.initWithPath(path); let hasher = Cc["@mozilla.org/security/hash;1"] .createInstance(Ci.nsICryptoHash); hasher.init(hasher.SHA1); let is = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); is.init(f, -1, 0, 0); hasher.updateFromStream(is, Math.pow(2, 32) - 1); is.close(); let bytes = hasher.finish(false); return [("0" + bytes.charCodeAt(byte).toString(16)).slice(-2) for (byte in bytes)] .join(""); } const EXPERIMENT1_ID = "test-experiment-1@tests.mozilla.org"; const EXPERIMENT1_XPI_NAME = "experiment-1.xpi"; const EXPERIMENT1_NAME = "Test experiment 1"; const EXPERIMENT1_PATH = getExperimentPath(EXPERIMENT1_XPI_NAME); const EXPERIMENT1_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT1_PATH); const EXPERIMENT1A_XPI_NAME = "experiment-1a.xpi"; const EXPERIMENT1A_NAME = "Test experiment 1.1"; const EXPERIMENT1A_PATH = getExperimentPath(EXPERIMENT1A_XPI_NAME); const EXPERIMENT1A_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT1A_PATH); const EXPERIMENT2_ID = "test-experiment-2@tests.mozilla.org" const EXPERIMENT2_XPI_NAME = "experiment-2.xpi"; const EXPERIMENT2_PATH = getExperimentPath(EXPERIMENT2_XPI_NAME); const EXPERIMENT2_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT2_PATH); const EXPERIMENT3_ID = "test-experiment-3@tests.mozilla.org"; const EXPERIMENT4_ID = "test-experiment-4@tests.mozilla.org"; const DEFAULT_BUILDID = "2014060601"; const FAKE_EXPERIMENTS_1 = [ { id: "id1", name: "experiment1", description: "experiment 1", active: true, detailUrl: "https://dummy/experiment1", branch: "foo", }, ]; const FAKE_EXPERIMENTS_2 = [ { id: "id2", name: "experiment2", description: "experiment 2", active: false, endDate: new Date(2014, 2, 11, 2, 4, 35, 42).getTime(), detailUrl: "https://dummy/experiment2", branch: null, }, { id: "id1", name: "experiment1", description: "experiment 1", active: false, endDate: new Date(2014, 2, 10, 0, 0, 0, 0).getTime(), detailURL: "https://dummy/experiment1", branch: null, }, ]; let gAppInfo = null; function removeCacheFile() { let path = OS.Path.join(OS.Constants.Path.profileDir, "experiments.json"); return OS.File.remove(path); } function patchPolicy(policy, data) { for (let key of Object.keys(data)) { Object.defineProperty(policy, key, { value: data[key], writable: true, }); } } function defineNow(policy, time) { patchPolicy(policy, { now: () => new Date(time) }); } function futureDate(date, offset) { return new Date(date.getTime() + offset); } function dateToSeconds(date) { return date.getTime() / 1000; } let gGlobalScope = this; function loadAddonManager() { let ns = {}; Cu.import("resource://gre/modules/Services.jsm", ns); let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js"; let file = do_get_file(head); let uri = ns.Services.io.newFileURI(file); ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope); createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); startupManager(); } // Starts the addon manager without creating app info. We can't directly use // |loadAddonManager| defined above in test_conditions.js as it would make the test fail. function startAddonManagerOnly() { let addonManager = Cc["@mozilla.org/addons/integration;1"] .getService(Ci.nsIObserver) .QueryInterface(Ci.nsITimerCallback); addonManager.observe(null, "addons-startup", null); } function getExperimentAddons(previous=false) { let deferred = Promise.defer(); AddonManager.getAddonsByTypes(["experiment"], (addons) => { if (previous) { deferred.resolve(addons); } else { deferred.resolve([a for (a of addons) if (!a.appDisabled)]); } }); return deferred.promise; } function createAppInfo(optionsIn) { const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); let options = optionsIn || {}; let id = options.id || "xpcshell@tests.mozilla.org"; let name = options.name || "XPCShell"; let version = options.version || "1.0"; let platformVersion = options.platformVersion || "1.0"; let date = options.date || new Date(); let buildID = options.buildID || DEFAULT_BUILDID; gAppInfo = { // nsIXULAppInfo vendor: "Mozilla", name: name, ID: id, version: version, appBuildID: buildID, platformVersion: platformVersion ? platformVersion : "1.0", platformBuildID: buildID, // nsIXULRuntime inSafeMode: false, logConsoleErrors: true, OS: "XPCShell", XPCOMABI: "noarch-spidermonkey", invalidateCachesOnRestart: function invalidateCachesOnRestart() { // Do nothing }, // nsICrashReporter annotations: {}, annotateCrashReport: function(key, data) { this.annotations[key] = data; }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime, Ci.nsICrashReporter, Ci.nsISupports]) }; let XULAppInfoFactory = { createInstance: function (outer, iid) { if (outer != null) { throw Cr.NS_ERROR_NO_AGGREGATION; } return gAppInfo.QueryInterface(iid); } }; let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", XULAPPINFO_CONTRACTID, XULAppInfoFactory); } /** * Replace the experiments on an Experiments with a new list. * * This monkeypatches getExperiments(). It doesn't monkeypatch the internal * experiments list. So its utility is not as great as it could be. */ function replaceExperiments(experiment, list) { Object.defineProperty(experiment, "getExperiments", { writable: true, value: () => { return Promise.resolve(list); }, }); }