import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,15 @@
"use strict";
module.exports = {
"extends": [
"../../../../testing/xpcshell/xpcshell.eslintrc.js"
],
"rules": {
"no-unused-vars": ["error", {
"vars": "all",
"varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
"args": "none"
}]
}
};

View file

@ -0,0 +1,19 @@
{
"version": 1,
"experiments": [
{
"id": "test-experiment-1@tests.mozilla.org",
"xpiURL": "https://experiments.mozilla.org/foo.xpi",
"xpiHash": "sha1:cb1eb32b89d86d78b7326f416cf404548c5e0099",
"startTime": 1393000000,
"endTime": 1394000000,
"appName": ["Firefox", "Fennec"],
"minVersion": "28",
"maxVersion": "30",
"maxActiveSeconds": 60,
"os": ["windows", "linux", "osx"],
"channel": ["daily", "weekly", "nightly"],
"jsfilter": "function filter(context) { return true; }"
}
]
}

View file

@ -0,0 +1,199 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* exported PREF_EXPERIMENTS_ENABLED, PREF_LOGGING_LEVEL, PREF_LOGGING_DUMP
PREF_MANIFEST_URI, PREF_FETCHINTERVAL, EXPERIMENT1_ID,
EXPERIMENT1_NAME, EXPERIMENT1_XPI_SHA1, EXPERIMENT1A_NAME,
EXPERIMENT1A_XPI_SHA1, EXPERIMENT2_ID, EXPERIMENT2_XPI_SHA1,
EXPERIMENT3_ID, EXPERIMENT4_ID, FAKE_EXPERIMENTS_1,
FAKE_EXPERIMENTS_2, gAppInfo, removeCacheFile, defineNow,
futureDate, dateToSeconds, loadAddonManager, promiseRestartManager,
startAddonManagerOnly, getExperimentAddons, replaceExperiments */
var {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");
Cu.import("resource://testing-common/AddonTestUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.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);
let rv = "";
for (let i = 0; i < bytes.length; i++) {
rv += ("0" + bytes.charCodeAt(i).toString(16)).substr(-2);
}
return rv;
}
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 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,
},
];
var 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;
}
var gGlobalScope = this;
function loadAddonManager() {
AddonTestUtils.init(gGlobalScope);
AddonTestUtils.overrideCertDB();
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
return AddonTestUtils.promiseStartupManager();
}
const {
promiseRestartManager,
} = AddonTestUtils;
// 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(addons.filter(a => !a.appDisabled));
}
});
return deferred.promise;
}
function createAppInfo(ID="xpcshell@tests.mozilla.org", name="XPCShell",
version="1.0", platformVersion="1.0") {
AddonTestUtils.createAppInfo(ID, name, version, platformVersion);
gAppInfo = AddonTestUtils.appInfo;
}
/**
* 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);
},
});
}
// Experiments require Telemetry to be enabled, and that's not true for debug
// builds. Let's just enable it here instead of going through each test.
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);

View file

@ -0,0 +1,151 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource:///modules/experiments/Experiments.jsm");
const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
var gHttpServer = null;
var gHttpRoot = null;
var gPolicy = null;
function ManifestEntry(data) {
this.id = data.id || EXPERIMENT1_ID;
this.xpiURL = data.xpiURL || gHttpRoot + EXPERIMENT1_XPI_NAME;
this.xpiHash = data.xpiHash || EXPERIMENT1_XPI_SHA1;
this.appName = data.appName || ["XPCShell"];
this.channel = data.appName || ["nightly"];
this.startTime = data.startTime || new Date(2010, 0, 1, 12).getTime() / 1000;
this.endTime = data.endTime || new Date(9001, 0, 1, 12).getTime() / 1000;
this.maxActiveSeconds = data.maxActiveSeconds || 5 * SEC_IN_ONE_DAY;
}
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
loadAddonManager();
gPolicy = new Experiments.Policy();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let port = gHttpServer.identity.primaryPort;
gHttpRoot = "http://localhost:" + port + "/";
gHttpServer.registerDirectory("/", do_get_cwd());
do_register_cleanup(() => gHttpServer.stop(() => {}));
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
});
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
});
function isApplicable(experiment) {
let deferred = Promise.defer();
experiment.isApplicable().then(
result => deferred.resolve({ applicable: true, reason: null }),
reason => deferred.resolve({ applicable: false, reason: reason })
);
return deferred.promise;
}
add_task(function* test_startStop() {
let baseDate = new Date(2014, 5, 1, 12);
let startDate = futureDate(baseDate, 30 * MS_IN_ONE_DAY);
let endDate = futureDate(baseDate, 60 * MS_IN_ONE_DAY);
let manifestData = new ManifestEntry({
startTime: dateToSeconds(startDate),
endTime: dateToSeconds(endDate),
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
});
let experiment = new Experiments.ExperimentEntry(gPolicy);
experiment.initFromManifestData(manifestData);
// We need to associate it with the singleton so the onInstallStarted
// Addon Manager listener will know about it.
Experiments.instance()._experiments = new Map();
Experiments.instance()._experiments.set(experiment.id, experiment);
let result;
defineNow(gPolicy, baseDate);
result = yield isApplicable(experiment);
Assert.equal(result.applicable, false, "Experiment should not be applicable.");
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
let addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
defineNow(gPolicy, futureDate(startDate, 5 * MS_IN_ONE_DAY));
result = yield isApplicable(experiment);
Assert.equal(result.applicable, true, "Experiment should now be applicable.");
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
let changes = yield experiment.start();
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
addons = yield getExperimentAddons();
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
Assert.equal(addons[0].id, experiment._addonId, "The add-on is the one we expect.");
Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
Assert.ok(addons[0].isActive, "The add-on is active.");
changes = yield experiment.stop();
Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on was uninstalled.");
addons = yield getExperimentAddons();
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
Assert.equal(addons.length, 0, "Experiment should be uninstalled from the Addon Manager.");
changes = yield experiment.start();
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
addons = yield getExperimentAddons();
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
Assert.equal(addons[0].id, experiment._addonId, "The add-on is the one we expect.");
Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
Assert.ok(addons[0].isActive, "The add-on is active.");
result = yield experiment.shouldStop();
Assert.equal(result.shouldStop, false, "shouldStop should be false.");
Assert.equal(experiment.enabled, true, "Experiment should be enabled.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 1, "Experiment still in add-ons manager.");
Assert.ok(addons[0].isActive, "The add-on is still active.");
defineNow(gPolicy, futureDate(endDate, MS_IN_ONE_DAY));
result = yield experiment.shouldStop();
Assert.equal(result.shouldStop, true, "shouldStop should now be true.");
changes = yield experiment.stop();
Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on should be uninstalled.");
Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "Experiment add-on is uninstalled.");
// Ensure hash validation works.
// We set an incorrect hash and expect the install to fail.
experiment._manifestData.xpiHash = "sha1:41014dcc66b4dcedcd973491a1530a32f0517d8a";
let errored = false;
try {
yield experiment.start();
} catch (ex) {
errored = true;
}
Assert.ok(experiment._failedStart, "Experiment failed to start.");
Assert.ok(errored, "start() threw an exception.");
// Make sure "ignore hashes" mode works.
gPolicy.ignoreHashes = true;
changes = yield experiment.start();
Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL);
yield experiment.stop();
gPolicy.ignoreHashes = false;
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,399 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
"resource:///modules/experiments/Experiments.jsm");
const MANIFEST_HANDLER = "manifests/handler";
const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
var gHttpServer = null;
var gHttpRoot = null;
var gDataRoot = null;
var gPolicy = null;
var gManifestObject = null;
var gManifestHandlerURI = null;
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
loadAddonManager();
yield removeCacheFile();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let port = gHttpServer.identity.primaryPort;
gHttpRoot = "http://localhost:" + port + "/";
gDataRoot = gHttpRoot + "data/";
gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
gHttpServer.registerDirectory("/data/", do_get_cwd());
gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
response.setStatusLine(null, 200, "OK");
response.write(JSON.stringify(gManifestObject));
response.processAsync();
response.finish();
});
do_register_cleanup(() => gHttpServer.stop(() => {}));
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
gPolicy = new Experiments.Policy();
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
oneshotTimer: (callback, timeout, thisObj, name) => {},
});
});
function checkExperimentListsEqual(list, list2) {
Assert.equal(list.length, list2.length, "Lists should have the same length.")
for (let i=0; i<list.length; ++i) {
for (let k of Object.keys(list[i])) {
Assert.equal(list[i][k], list2[i][k],
"Field '" + k + "' should match for list entry " + i + ".");
}
}
}
function checkExperimentSerializations(experimentEntryIterator) {
for (let experiment of experimentEntryIterator) {
let experiment2 = new Experiments.ExperimentEntry(gPolicy);
let jsonStr = JSON.stringify(experiment.toJSON());
Assert.ok(experiment2.initFromCacheData(JSON.parse(jsonStr)),
"Should have initialized successfully from JSON serialization.");
Assert.equal(JSON.stringify(experiment), JSON.stringify(experiment2),
"Object stringifications should match.");
}
}
function validateCache(cachedExperiments, experimentIds) {
let cachedExperimentIds = new Set(cachedExperiments);
Assert.equal(cachedExperimentIds.size, experimentIds.length,
"The number of cached experiments does not match with the provided list");
for (let id of experimentIds) {
Assert.ok(cachedExperimentIds.has(id), "The cache must contain the experiment with id " + id);
}
}
// Set up an experiments instance and check if it is properly restored from cache.
add_task(function* test_cache() {
// The manifest data we test with.
gManifestObject = {
"version": 1,
experiments: [
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
{
id: EXPERIMENT2_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
{
id: EXPERIMENT3_ID,
xpiURL: "https://inval.id/foo.xpi",
xpiHash: "sha1:0000000000000000000000000000000000000000",
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
],
};
// Setup dates for the experiments.
let baseDate = new Date(2014, 5, 1, 12);
let startDates = [];
let endDates = [];
for (let i=0; i<gManifestObject.experiments.length; ++i) {
let experiment = gManifestObject.experiments[i];
startDates.push(futureDate(baseDate, (50 + (150 * i)) * MS_IN_ONE_DAY));
endDates .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY));
experiment.startTime = dateToSeconds(startDates[i]);
experiment.endTime = dateToSeconds(endDates[i]);
}
// Data to compare the result of Experiments.getExperiments() against.
let experimentListData = [
{
id: EXPERIMENT2_ID,
name: "Test experiment 2",
description: "And yet another experiment that experiments experimentally.",
},
{
id: EXPERIMENT1_ID,
name: EXPERIMENT1_NAME,
description: "Yet another experiment that experiments experimentally.",
},
];
// Trigger update & re-init, clock set to before any activation.
let now = baseDate;
defineNow(gPolicy, now);
let experiments = new Experiments.Experiments(gPolicy);
yield experiments.updateManifest();
let list = yield experiments.getExperiments();
Assert.equal(list.length, 0, "Experiment list should be empty.");
checkExperimentSerializations(experiments._experiments.values());
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
list = yield experiments.getExperiments();
Assert.equal(list.length, 0, "Experiment list should be empty.");
checkExperimentSerializations(experiments._experiments.values());
// Re-init, clock set for experiment 1 to start.
now = futureDate(startDates[0], 5 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
experimentListData[1].active = true;
experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
checkExperimentListsEqual(experimentListData.slice(1), list);
checkExperimentSerializations(experiments._experiments.values());
let branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
Assert.strictEqual(branch, null);
yield experiments.setExperimentBranch(EXPERIMENT1_ID, "testbranch");
branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
Assert.strictEqual(branch, "testbranch");
// Re-init, clock set for experiment 1 to stop.
now = futureDate(now, 20 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
experimentListData[1].active = false;
experimentListData[1].endDate = now.getTime();
checkExperimentListsEqual(experimentListData.slice(1), list);
checkExperimentSerializations(experiments._experiments.values());
branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
Assert.strictEqual(branch, "testbranch");
// Re-init, clock set for experiment 2 to start.
now = futureDate(startDates[1], 20 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
experimentListData[0].active = true;
experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
checkExperimentListsEqual(experimentListData, list);
checkExperimentSerializations(experiments._experiments.values());
// Re-init, clock set for experiment 2 to stop.
now = futureDate(now, 20 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
experimentListData[0].active = false;
experimentListData[0].endDate = now.getTime();
checkExperimentListsEqual(experimentListData, list);
checkExperimentSerializations(experiments._experiments.values());
// Cleanup.
yield experiments._toggleExperimentsEnabled(false);
yield promiseRestartManager();
yield removeCacheFile();
});
add_task(function* test_expiration() {
// The manifest data we test with.
gManifestObject = {
"version": 1,
experiments: [
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
{
id: EXPERIMENT2_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
maxActiveSeconds: 50 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
// The 3rd experiment will never run, so it's ok to use experiment's 2 data.
{
id: EXPERIMENT3_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
}
],
};
// Data to compare the result of Experiments.getExperiments() against.
let experimentListData = [
{
id: EXPERIMENT2_ID,
name: "Test experiment 2",
description: "And yet another experiment that experiments experimentally.",
},
{
id: EXPERIMENT1_ID,
name: EXPERIMENT1_NAME,
description: "Yet another experiment that experiments experimentally.",
},
];
// Setup dates for the experiments.
let baseDate = new Date(2014, 5, 1, 12);
let startDates = [];
let endDates = [];
for (let i=0; i<gManifestObject.experiments.length; ++i) {
let experiment = gManifestObject.experiments[i];
// Spread out experiments in time so that one experiment can end and expire while
// the next is still running.
startDates.push(futureDate(baseDate, (50 + (200 * i)) * MS_IN_ONE_DAY));
endDates .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY));
experiment.startTime = dateToSeconds(startDates[i]);
experiment.endTime = dateToSeconds(endDates[i]);
}
let now = null;
let experiments = null;
let setDateAndRestartExperiments = new Task.async(function* (newDate) {
now = newDate;
defineNow(gPolicy, now);
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
});
// Trigger update & re-init, clock set to before any activation.
now = baseDate;
defineNow(gPolicy, now);
experiments = new Experiments.Experiments(gPolicy);
yield experiments.updateManifest();
let list = yield experiments.getExperiments();
Assert.equal(list.length, 0, "Experiment list should be empty.");
// Re-init, clock set for experiment 1 to start...
yield setDateAndRestartExperiments(startDates[0]);
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "The first experiment should have started.");
// ... init again, and set the clock so that the first experiment ends.
yield setDateAndRestartExperiments(endDates[0]);
// The experiment just ended, it should still be in the cache, but marked
// as finished.
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
experimentListData[1].active = false;
experimentListData[1].endDate = now.getTime();
checkExperimentListsEqual(experimentListData.slice(1), list);
validateCache([...experiments._experiments.keys()], [EXPERIMENT1_ID, EXPERIMENT2_ID, EXPERIMENT3_ID]);
// Start the second experiment.
yield setDateAndRestartExperiments(startDates[1]);
// The experiments cache should contain the finished experiment and the
// one that's still running.
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
experimentListData[0].active = true;
experimentListData[0].endDate = now.getTime() + 50 * MS_IN_ONE_DAY;
checkExperimentListsEqual(experimentListData, list);
// Move the clock in the future, just 31 days after the start date of the second experiment,
// so that the cache for the first experiment expires and the second experiment is still running.
yield setDateAndRestartExperiments(futureDate(startDates[1], 31 * MS_IN_ONE_DAY));
validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID, EXPERIMENT3_ID]);
// Make sure that the expired experiment is not reported anymore.
let history = yield experiments.getExperiments();
Assert.equal(history.length, 1, "Experiments older than 180 days must be removed from the cache.");
// Test that we don't write expired experiments in the cache.
yield setDateAndRestartExperiments(now);
validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID, EXPERIMENT3_ID]);
// The first experiment should be expired and not in the cache, it ended more than
// 180 days ago. We should see the one still running in the cache.
history = yield experiments.getExperiments();
Assert.equal(history.length, 1, "Expired experiments must not be saved to cache.");
checkExperimentListsEqual(experimentListData.slice(0, 1), history);
// Test that experiments that are cached locally but never ran are removed from cache
// when they are removed from the manifest (this is cached data, not really history).
gManifestObject["experiments"] = gManifestObject["experiments"].slice(1, 1);
yield experiments.updateManifest();
validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID]);
// Cleanup.
yield experiments._toggleExperimentsEnabled(false);
yield promiseRestartManager();
yield removeCacheFile();
});

View file

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Timer.jsm");
const MANIFEST_HANDLER = "manifests/handler";
const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
var gHttpServer = null;
var gHttpRoot = null;
var gDataRoot = null;
var gPolicy = null;
var gManifestObject = null;
var gManifestHandlerURI = null;
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
loadAddonManager();
yield removeCacheFile();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let port = gHttpServer.identity.primaryPort;
gHttpRoot = "http://localhost:" + port + "/";
gDataRoot = gHttpRoot + "data/";
gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
gHttpServer.registerDirectory("/data/", do_get_cwd());
gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
response.setStatusLine(null, 200, "OK");
response.write(JSON.stringify(gManifestObject));
response.processAsync();
response.finish();
});
do_register_cleanup(() => gHttpServer.stop(() => {}));
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
let ExperimentsScope = Cu.import("resource:///modules/experiments/Experiments.jsm");
let Experiments = ExperimentsScope.Experiments;
gPolicy = new Experiments.Policy();
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
delayCacheWrite: (promise) => {
return new Promise((resolve, reject) => {
promise.then(
(result) => { setTimeout(() => resolve(result), 500); },
(err) => { reject(err); }
);
});
},
});
let now = new Date(2014, 5, 1, 12);
defineNow(gPolicy, now);
let experimentName = "experiment-racybranch.xpi";
let experimentPath = getExperimentPath(experimentName);
let experimentHash = "sha1:" + sha1File(experimentPath);
gManifestObject = {
version: 1,
experiments: [
{
id: "test-experiment-racybranch@tests.mozilla.org",
xpiURL: gDataRoot + "experiment-racybranch.xpi",
xpiHash: experimentHash,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
startTime: dateToSeconds(futureDate(now, -MS_IN_ONE_DAY)),
endTime: dateToSeconds(futureDate(now, MS_IN_ONE_DAY)),
},
],
};
do_print("gManifestObject: " + JSON.stringify(gManifestObject));
// In order for the addon manager to work properly, we hack
// Experiments.instance which is used by the XPIProvider
let experiments = new Experiments.Experiments(gPolicy);
Assert.strictEqual(ExperimentsScope.gExperiments, null);
ExperimentsScope.gExperiments = experiments;
yield experiments.updateManifest();
let active = experiments._getActiveExperiment();
Assert.ok(active);
Assert.equal(active.branch, "racy-set");
Assert.ok(!experiments._dirty);
});

View file

@ -0,0 +1,325 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/experiments/Experiments.jsm");
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
const SEC_IN_ONE_DAY = 24 * 60 * 60;
var gPolicy = null;
function ManifestEntry(data) {
this.id = EXPERIMENT1_ID;
this.xpiURL = "http://localhost:1/dummy.xpi";
this.xpiHash = EXPERIMENT1_XPI_SHA1;
this.startTime = new Date(2010, 0, 1, 12).getTime() / 1000;
this.endTime = new Date(9001, 0, 1, 12).getTime() / 1000;
this.maxActiveSeconds = SEC_IN_ONE_DAY;
this.appName = ["XPCShell"];
this.channel = ["nightly"];
data = data || {};
for (let k of Object.keys(data)) {
this[k] = data[k];
}
if (!this.endTime) {
this.endTime = this.startTime + 5 * SEC_IN_ONE_DAY;
}
}
function applicableFromManifestData(data, policy) {
let manifestData = new ManifestEntry(data);
let entry = new Experiments.ExperimentEntry(policy);
entry.initFromManifestData(manifestData);
return entry.isApplicable();
}
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
createAppInfo();
do_get_profile();
startAddonManagerOnly();
yield TelemetryController.testSetup();
gPolicy = new Experiments.Policy();
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
locale: () => "en-US",
random: () => 0.5,
});
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
});
function arraysEqual(a, b) {
if (a.length !== b.length) {
return false;
}
for (let i=0; i<a.length; ++i) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
// This function exists solely to be .toSource()d
const sanityFilter = function filter(c) {
if (c.telemetryEnvironment === undefined) {
throw Error("No .telemetryEnvironment");
}
if (c.telemetryEnvironment.build == undefined) {
throw Error("No .telemetryEnvironment.build");
}
return true;
}
// Utility function to generate build ID for previous/next date.
function addDate(buildId, diff) {
let m = /^([0-9]{4})([0-9]{2})([0-9]{2})(.*)$/.exec(buildId);
if (!m) {
throw Error("Unsupported build ID: " + buildId);
}
let year = Number.parseInt(m[1], 10);
let month = Number.parseInt(m[2], 10);
let date = Number.parseInt(m[3], 10);
let remainingParts = m[4];
let d = new Date();
d.setUTCFullYear(year, month - 1, date);
d.setTime(d.getTime() + diff * 24 * 60 * 60 * 1000);
let yearStr = String(d.getUTCFullYear());
let monthStr = ("0" + String(d.getUTCMonth() + 1)).slice(-2);
let dateStr = ("0" + String(d.getUTCDate())).slice(-2);
return yearStr + monthStr + dateStr + remainingParts;
}
function prevDate(buildId) {
return addDate(buildId, -1);
}
function nextDate(buildId) {
return addDate(buildId, 1);
}
add_task(function* test_simpleFields() {
let testData = [
// "expected applicable?", failure reason or null, manifest data
// misc. environment
[false, ["appName"], {appName: []}],
[false, ["appName"], {appName: ["foo", gAppInfo.name + "-invalid"]}],
[true, null, {appName: ["not-an-app-name", gAppInfo.name]}],
[false, ["os"], {os: []}],
[false, ["os"], {os: ["42", "abcdef"]}],
[true, null, {os: [gAppInfo.OS, "plan9"]}],
[false, ["channel"], {channel: []}],
[false, ["channel"], {channel: ["foo", gPolicy.updatechannel() + "-invalid"]}],
[true, null, {channel: ["not-a-channel", gPolicy.updatechannel()]}],
[false, ["locale"], {locale: []}],
[false, ["locale"], {locale: ["foo", gPolicy.locale + "-invalid"]}],
[true, null, {locale: ["not-a-locale", gPolicy.locale()]}],
// version
[false, ["version"], {version: []}],
[false, ["version"], {version: ["-1", gAppInfo.version + "-invalid", "asdf", "0,4", "99.99", "0.1.1.1"]}],
[true, null, {version: ["99999999.999", "-1", gAppInfo.version]}],
[false, ["minVersion"], {minVersion: "1.0.1"}],
[true, null, {minVersion: "1.0b1"}],
[true, null, {minVersion: "1.0"}],
[true, null, {minVersion: "0.9"}],
[false, ["maxVersion"], {maxVersion: "0.1"}],
[false, ["maxVersion"], {maxVersion: "0.9.9"}],
[false, ["maxVersion"], {maxVersion: "1.0b1"}],
[true, ["maxVersion"], {maxVersion: "1.0"}],
[true, ["maxVersion"], {maxVersion: "1.7pre"}],
// build id
[false, ["buildIDs"], {buildIDs: []}],
[false, ["buildIDs"], {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID + "-invalid"]}],
[true, null, {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID]}],
[true, null, {minBuildID: prevDate(gAppInfo.platformBuildID)}],
[true, null, {minBuildID: gAppInfo.platformBuildID}],
[false, ["minBuildID"], {minBuildID: nextDate(gAppInfo.platformBuildID)}],
[false, ["maxBuildID"], {maxBuildID: prevDate(gAppInfo.platformBuildID)}],
[true, null, {maxBuildID: gAppInfo.platformBuildID}],
[true, null, {maxBuildID: nextDate(gAppInfo.platformBuildID)}],
// sample
[false, ["sample"], {sample: -1 }],
[false, ["sample"], {sample: 0.0}],
[false, ["sample"], {sample: 0.1}],
[true, null, {sample: 0.5}],
[true, null, {sample: 0.6}],
[true, null, {sample: 1.0}],
[true, null, {sample: 0.5}],
// experiment control
[false, ["disabled"], {disabled: true}],
[true, null, {disabled: false}],
[false, ["frozen"], {frozen: true}],
[true, null, {frozen: false}],
[false, null, {frozen: true, disabled: true}],
[false, null, {frozen: true, disabled: false}],
[false, null, {frozen: false, disabled: true}],
[true, null, {frozen: false, disabled: false}],
// jsfilter
[true, null, {jsfilter: "function filter(c) { return true; }"}],
[false, ["jsfilter-false"], {jsfilter: "function filter(c) { return false; }"}],
[true, null, {jsfilter: "function filter(c) { return 123; }"}], // truthy
[false, ["jsfilter-false"], {jsfilter: "function filter(c) { return ''; }"}], // falsy
[false, ["jsfilter-false"], {jsfilter: "function filter(c) { var a = []; }"}], // undefined
[false, ["jsfilter-threw", "some error"], {jsfilter: "function filter(c) { throw new Error('some error'); }"}],
[false, ["jsfilter-evalfailed"], {jsfilter: "123, this won't work"}],
[true, null, {jsfilter: "var filter = " + sanityFilter.toSource()}],
];
for (let i=0; i<testData.length; ++i) {
let entry = testData[i];
let applicable;
let reason = null;
yield applicableFromManifestData(entry[2], gPolicy).then(
value => applicable = value,
value => {
applicable = false;
reason = value;
}
);
Assert.equal(applicable, entry[0],
"Experiment entry applicability should match for test "
+ i + ": " + JSON.stringify(entry[2]));
let expectedReason = entry[1];
if (!applicable && expectedReason) {
Assert.ok(arraysEqual(reason, expectedReason),
"Experiment rejection reasons should match for test " + i + ". "
+ "Got " + JSON.stringify(reason) + ", expected "
+ JSON.stringify(expectedReason));
}
}
});
add_task(function* test_times() {
let now = new Date(2014, 5, 6, 12);
let nowSec = now.getTime() / 1000;
let testData = [
// "expected applicable?", rejection reason or null, fake now date, manifest data
// start time
[true, null, now,
{startTime: nowSec - 5 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{startTime: nowSec,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[false, "startTime", now,
{startTime: nowSec + 5 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
// end time
[false, "endTime", now,
{startTime: nowSec - 5 * SEC_IN_ONE_DAY,
endTime: nowSec - 10 * SEC_IN_ONE_DAY}],
[false, "endTime", now,
{startTime: nowSec - 5 * SEC_IN_ONE_DAY,
endTime: nowSec - 5 * SEC_IN_ONE_DAY}],
// max start time
[false, "maxStartTime", now,
{maxStartTime: nowSec - 15 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[false, "maxStartTime", now,
{maxStartTime: nowSec - 1 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[false, "maxStartTime", now,
{maxStartTime: nowSec - 10 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxStartTime: nowSec,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxStartTime: nowSec + 1 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
// max active seconds
[true, null, now,
{maxActiveSeconds: 5 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxActiveSeconds: 15 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
[true, null, now,
{maxActiveSeconds: 20 * SEC_IN_ONE_DAY,
startTime: nowSec - 10 * SEC_IN_ONE_DAY,
endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
];
for (let i=0; i<testData.length; ++i) {
let entry = testData[i];
let applicable;
let reason = null;
defineNow(gPolicy, entry[2]);
yield applicableFromManifestData(entry[3], gPolicy).then(
value => applicable = value,
value => {
applicable = false;
reason = value;
}
);
Assert.equal(applicable, entry[0],
"Experiment entry applicability should match for test "
+ i + ": " + JSON.stringify([entry[2], entry[3]]));
if (!applicable && entry[1]) {
Assert.equal(reason, entry[1], "Experiment rejection reason should match for test " + i);
}
}
});
add_task(function* test_shutdown() {
yield TelemetryController.testShutdown();
});

View file

@ -0,0 +1,180 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://testing-common/AddonManagerTesting.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
"resource:///modules/experiments/Experiments.jsm");
const MANIFEST_HANDLER = "manifests/handler";
const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
var gHttpServer = null;
var gHttpRoot = null;
var gDataRoot = null;
var gPolicy = null;
var gManifestObject = null;
var gManifestHandlerURI = null;
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
loadAddonManager();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let port = gHttpServer.identity.primaryPort;
gHttpRoot = "http://localhost:" + port + "/";
gDataRoot = gHttpRoot + "data/";
gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
gHttpServer.registerDirectory("/data/", do_get_cwd());
gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
response.setStatusLine(null, 200, "OK");
response.write(JSON.stringify(gManifestObject));
response.processAsync();
response.finish();
});
do_register_cleanup(() => gHttpServer.stop(() => {}));
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
gPolicy = new Experiments.Policy();
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
oneshotTimer: (callback, timeout, thisObj, name) => {},
});
});
// Test disabling the feature stops current and future experiments.
add_task(function* test_disableExperiments() {
const OBSERVER_TOPIC = "experiments-changed";
let observerFireCount = 0;
let expectedObserverFireCount = 0;
let observer = () => ++observerFireCount;
Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
// Dates the following tests are based on.
let baseDate = new Date(2014, 5, 1, 12);
let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
// The manifest data we test with.
gManifestObject = {
"version": 1,
experiments: [
{
id: EXPERIMENT2_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
startTime: dateToSeconds(startDate2),
endTime: dateToSeconds(endDate2),
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
startTime: dateToSeconds(startDate1),
endTime: dateToSeconds(endDate1),
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
],
};
let experiments = new Experiments.Experiments(gPolicy);
// Trigger update, clock set to before any activation.
// Use updateManifest() to provide for coverage of that path.
let now = baseDate;
defineNow(gPolicy, now);
yield experiments.updateManifest();
Assert.equal(observerFireCount, ++expectedObserverFireCount,
"Experiments observer should have been called.");
let list = yield experiments.getExperiments();
Assert.equal(list.length, 0, "Experiment list should be empty.");
let addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are installed.");
// Trigger update, clock set for experiment 1 to start.
now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.updateManifest();
Assert.equal(observerFireCount, ++expectedObserverFireCount,
"Experiments observer should have been called.");
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
Assert.equal(list[0].active, true, "Experiment should be active.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 1, "An experiment add-on was installed.");
// Disable the experiments feature. Check that we stop the running experiment.
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, false);
yield experiments._mainTask;
Assert.equal(observerFireCount, ++expectedObserverFireCount,
"Experiments observer should have been called.");
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
Assert.equal(list[0].active, false, "Experiment entry should not be active.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "The experiment add-on should be uninstalled.");
// Trigger update, clock set for experiment 2 to start. Verify we don't start it.
now = startDate2;
defineNow(gPolicy, now);
try {
yield experiments.updateManifest();
} catch (e) {
// This exception is expected, we rethrow everything else
if (e.message != "experiments are disabled") {
throw e;
}
}
experiments.notify();
yield experiments._mainTask;
Assert.equal(observerFireCount, expectedObserverFireCount,
"Experiments observer should not have been called.");
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should still have 1 entry.");
Assert.equal(list[0].active, false, "Experiment entry should not be active.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "There should still be no experiment add-on installed.");
// Cleanup.
Services.obs.removeObserver(observer, OBSERVER_TOPIC);
yield promiseRestartManager();
yield removeCacheFile();
});

View file

@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource:///modules/experiments/Experiments.jsm");
var gHttpServer = null;
var gHttpRoot = null;
var gPolicy = new Experiments.Policy();
function run_test() {
loadAddonManager();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let port = gHttpServer.identity.primaryPort;
gHttpRoot = "http://localhost:" + port + "/";
gHttpServer.registerDirectory("/", do_get_cwd());
do_register_cleanup(() => gHttpServer.stop(() => {}));
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
});
run_next_test();
}
add_task(function* test_fetchAndCache() {
Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "experiments_1.manifest");
let ex = new Experiments.Experiments(gPolicy);
Assert.equal(ex._experiments, null, "There should be no cached experiments yet.");
yield ex.updateManifest();
Assert.notEqual(ex._experiments.size, 0, "There should be cached experiments now.");
yield promiseRestartManager();
});
add_task(function* test_checkCache() {
let ex = new Experiments.Experiments(gPolicy);
yield ex.notify();
Assert.notEqual(ex._experiments.size, 0, "There should be cached experiments now.");
yield promiseRestartManager();
});
add_task(function* test_fetchInvalid() {
yield removeCacheFile();
Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "experiments_1.manifest");
let ex = new Experiments.Experiments(gPolicy);
yield ex.updateManifest();
Assert.notEqual(ex._experiments.size, 0, "There should be experiments");
Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "invalid.manifest");
yield ex.updateManifest()
Assert.notEqual(ex._experiments.size, 0, "There should still be experiments: fetch failure shouldn't remove them.");
yield promiseRestartManager();
});

View file

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource:///modules/experiments/Experiments.jsm");
const MANIFEST_HANDLER = "manifests/handler";
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
loadAddonManager();
do_get_profile();
let httpServer = new HttpServer();
httpServer.start(-1);
let port = httpServer.identity.primaryPort;
let httpRoot = "http://localhost:" + port + "/";
let handlerURI = httpRoot + MANIFEST_HANDLER;
httpServer.registerPathHandler("/" + MANIFEST_HANDLER,
(request, response) => {
response.processAsync();
response.setStatus(null, 200, "OK");
response.write("["); // never finish!
});
do_register_cleanup(() => httpServer.stop(() => {}));
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
Services.prefs.setCharPref(PREF_MANIFEST_URI, handlerURI);
Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
let experiments = Experiments.instance();
experiments.updateManifest().then(
() => {
Assert.ok(true, "updateManifest finished successfully");
},
(e) => {
do_throw("updateManifest should not have failed: got error " + e);
});
yield experiments.uninit();
});

View file

@ -0,0 +1,179 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource:///modules/experiments/Experiments.jsm");
Cu.import("resource://testing-common/httpd.js");
var gDataRoot;
var gHttpServer;
var gManifestObject;
function run_test() {
run_next_test();
}
add_task(function test_setup() {
loadAddonManager();
do_get_profile();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let httpRoot = "http://localhost:" + gHttpServer.identity.primaryPort + "/";
gDataRoot = httpRoot + "data/";
gHttpServer.registerDirectory("/data/", do_get_cwd());
gHttpServer.registerPathHandler("/manifests/handler", (req, res) => {
res.setStatusLine(null, 200, "OK");
res.write(JSON.stringify(gManifestObject));
res.processAsync();
res.finish();
});
do_register_cleanup(() => gHttpServer.stop(() => {}));
Services.prefs.setBoolPref("experiments.enabled", true);
Services.prefs.setCharPref("experiments.manifest.uri",
httpRoot + "manifests/handler");
Services.prefs.setBoolPref("experiments.logging.dump", true);
Services.prefs.setCharPref("experiments.logging.level", "Trace");
});
add_task(function* test_provider_basic() {
let e = Experiments.instance();
let provider = new Experiments.PreviousExperimentProvider(e);
e._setPreviousExperimentsProvider(provider);
let deferred = Promise.defer();
provider.getAddonsByTypes(["experiment"], (addons) => {
deferred.resolve(addons);
});
let experimentAddons = yield deferred.promise;
Assert.ok(Array.isArray(experimentAddons), "getAddonsByTypes returns an Array.");
Assert.equal(experimentAddons.length, 0, "No previous add-ons returned.");
gManifestObject = {
version: 1,
experiments: [
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
startTime: Date.now() / 1000 - 60,
endTime: Date.now() / 1000 + 60,
maxActiveSeconds: 60,
appName: ["XPCShell"],
channel: [e._policy.updatechannel()],
},
],
};
yield e.updateManifest();
deferred = Promise.defer();
provider.getAddonsByTypes(["experiment"], (addons) => {
deferred.resolve(addons);
});
experimentAddons = yield deferred.promise;
Assert.equal(experimentAddons.length, 0, "Still no previous experiment.");
let experiments = yield e.getExperiments();
Assert.equal(experiments.length, 1, "1 experiment present.");
Assert.ok(experiments[0].active, "It is active.");
// Deactivate it.
defineNow(e._policy, new Date(gManifestObject.experiments[0].endTime * 1000 + 1000));
yield e.updateManifest();
experiments = yield e.getExperiments();
Assert.equal(experiments.length, 1, "1 experiment present.");
Assert.equal(experiments[0].active, false, "It isn't active.");
deferred = Promise.defer();
provider.getAddonsByTypes(["experiment"], (addons) => {
deferred.resolve(addons);
});
experimentAddons = yield deferred.promise;
Assert.equal(experimentAddons.length, 1, "1 previous add-on known.");
Assert.equal(experimentAddons[0].id, EXPERIMENT1_ID, "ID matches expected.");
deferred = Promise.defer();
provider.getAddonByID(EXPERIMENT1_ID, (addon) => {
deferred.resolve(addon);
});
let addon = yield deferred.promise;
Assert.ok(addon, "We got an add-on from its ID.");
Assert.equal(addon.id, EXPERIMENT1_ID, "ID matches expected.");
Assert.ok(addon.appDisabled, "Add-on is a previous experiment.");
Assert.ok(addon.userDisabled, "Add-on is disabled.");
Assert.equal(addon.type, "experiment", "Add-on is an experiment.");
Assert.equal(addon.isActive, false, "Add-on is not active.");
Assert.equal(addon.permissions, 0, "Add-on has no permissions.");
deferred = Promise.defer();
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
deferred.resolve(addons);
});
experimentAddons = yield deferred.promise;
Assert.equal(experimentAddons.length, 1, "Got 1 experiment from add-on manager.");
Assert.equal(experimentAddons[0].id, EXPERIMENT1_ID, "ID matches expected.");
Assert.ok(experimentAddons[0].appDisabled, "It is a previous experiment add-on.");
});
add_task(function* test_active_and_previous() {
// Building on the previous test, activate experiment 2.
let e = Experiments.instance();
let provider = new Experiments.PreviousExperimentProvider(e);
e._setPreviousExperimentsProvider(provider);
gManifestObject = {
version: 1,
experiments: [
{
id: EXPERIMENT2_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
startTime: Date.now() / 1000 - 60,
endTime: Date.now() / 1000 + 60,
maxActiveSeconds: 60,
appName: ["XPCShell"],
channel: [e._policy.updatechannel()],
},
],
};
defineNow(e._policy, new Date());
yield e.updateManifest();
let experiments = yield e.getExperiments();
Assert.equal(experiments.length, 2, "2 experiments known.");
let deferred = Promise.defer();
provider.getAddonsByTypes(["experiment"], (addons) => {
deferred.resolve(addons);
});
let experimentAddons = yield deferred.promise;
Assert.equal(experimentAddons.length, 1, "1 previous experiment.");
deferred = Promise.defer();
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
deferred.resolve(addons);
});
experimentAddons = yield deferred.promise;
Assert.equal(experimentAddons.length, 2, "2 experiment add-ons known.");
for (let addon of experimentAddons) {
if (addon.id == EXPERIMENT1_ID) {
Assert.equal(addon.isActive, false, "Add-on is not active.");
Assert.ok(addon.appDisabled, "Should be a previous experiment.");
}
else if (addon.id == EXPERIMENT2_ID) {
Assert.ok(addon.isActive, "Add-on is active.");
Assert.ok(!addon.appDisabled, "Should not be a previous experiment.");
}
else {
throw new Error("Unexpected add-on ID: " + addon.id);
}
}
});

View file

@ -0,0 +1,294 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/TelemetryLog.jsm");
var bsp = Cu.import("resource:///modules/experiments/Experiments.jsm");
const MANIFEST_HANDLER = "manifests/handler";
const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
var gHttpServer = null;
var gHttpRoot = null;
var gDataRoot = null;
var gPolicy = null;
var gManifestObject = null;
var gManifestHandlerURI = null;
const TLOG = bsp.TELEMETRY_LOG;
function checkEvent(event, id, data)
{
do_print("Checking message " + id);
Assert.equal(event[0], id, "id should match");
Assert.ok(event[1] > 0, "timestamp should be greater than 0");
if (data === undefined) {
Assert.equal(event.length, 2, "event array should have 2 entries");
} else {
Assert.equal(event.length, data.length + 2, "event entry count should match expected count");
for (var i = 0; i < data.length; ++i) {
Assert.equal(typeof(event[i + 2]), "string", "event entry should be a string");
Assert.equal(event[i + 2], data[i], "event entry should match expected entry");
}
}
}
function run_test() {
run_next_test();
}
add_task(function* test_setup() {
loadAddonManager();
gHttpServer = new HttpServer();
gHttpServer.start(-1);
let port = gHttpServer.identity.primaryPort;
gHttpRoot = "http://localhost:" + port + "/";
gDataRoot = gHttpRoot + "data/";
gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
gHttpServer.registerDirectory("/data/", do_get_cwd());
gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
response.setStatusLine(null, 200, "OK");
response.write(JSON.stringify(gManifestObject));
response.processAsync();
response.finish();
});
do_register_cleanup(() => gHttpServer.stop(() => {}));
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
gPolicy = new Experiments.Policy();
let dummyTimer = { cancel: () => {}, clear: () => {} };
patchPolicy(gPolicy, {
updatechannel: () => "nightly",
oneshotTimer: (callback, timeout, thisObj, name) => dummyTimer,
});
yield removeCacheFile();
});
// Test basic starting and stopping of experiments.
add_task(function* test_telemetryBasics() {
// Check TelemetryLog instead of TelemetrySession.getPayload().log because
// TelemetrySession gets Experiments.instance() and side-effects log entries.
let expectedLogLength = 0;
// Dates the following tests are based on.
let baseDate = new Date(2014, 5, 1, 12);
let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
// The manifest data we test with.
gManifestObject = {
"version": 1,
experiments: [
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
startTime: dateToSeconds(startDate1),
endTime: dateToSeconds(endDate1),
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
{
id: EXPERIMENT2_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
startTime: dateToSeconds(startDate2),
endTime: dateToSeconds(endDate2),
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
],
};
let experiments = new Experiments.Experiments(gPolicy);
// Trigger update, clock set to before any activation.
// Use updateManifest() to provide for coverage of that path.
let now = baseDate;
defineNow(gPolicy, now);
yield experiments.updateManifest();
let list = yield experiments.getExperiments();
Assert.equal(list.length, 0, "Experiment list should be empty.");
expectedLogLength += 2;
let log = TelemetryLog.entries();
do_print("Telemetry log: " + JSON.stringify(log));
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-2], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.REJECTED, EXPERIMENT1_ID, "startTime"]);
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.REJECTED, EXPERIMENT2_ID, "startTime"]);
// Trigger update, clock set for experiment 1 to start.
now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries. Got " + log.toSource());
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT1_ID]);
// Trigger update, clock set for experiment 1 to stop.
now = futureDate(endDate1, 1000);
defineNow(gPolicy, now);
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
expectedLogLength += 2;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-2], TLOG.TERMINATION_KEY,
[TLOG.TERMINATION.EXPIRED, EXPERIMENT1_ID]);
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.REJECTED, EXPERIMENT2_ID, "startTime"]);
// Trigger update, clock set for experiment 2 to start with invalid hash.
now = startDate2;
defineNow(gPolicy, now);
gManifestObject.experiments[1].xpiHash = "sha1:0000000000000000000000000000000000000000";
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.INSTALL_FAILURE, EXPERIMENT2_ID]);
// Trigger update, clock set for experiment 2 to properly start now.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
gManifestObject.experiments[1].xpiHash = EXPERIMENT2_XPI_SHA1;
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT2_ID]);
// Fake user uninstall of experiment via add-on manager.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.disableExperiment(TLOG.TERMINATION.ADDON_UNINSTALLED);
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
[TLOG.TERMINATION.ADDON_UNINSTALLED, EXPERIMENT2_ID]);
// Trigger update with experiment 1a ready to start.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
gManifestObject.experiments[0].id = EXPERIMENT3_ID;
gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY));
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT3_ID]);
// Trigger disable of an experiment via the API.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.disableExperiment(TLOG.TERMINATION.FROM_API);
list = yield experiments.getExperiments();
Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
[TLOG.TERMINATION.FROM_API, EXPERIMENT3_ID]);
// Trigger update with experiment 1a ready to start.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
gManifestObject.experiments[0].id = EXPERIMENT4_ID;
gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY));
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 4, "Experiment list should have 4 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
[TLOG.ACTIVATION.ACTIVATED, EXPERIMENT4_ID]);
// Trigger experiment termination by something other than expiry via the manifest.
now = futureDate(now, MS_IN_ONE_DAY);
defineNow(gPolicy, now);
gManifestObject.experiments[0].os = "Plan9";
yield experiments.updateManifest();
list = yield experiments.getExperiments();
Assert.equal(list.length, 4, "Experiment list should have 4 entries.");
expectedLogLength += 1;
log = TelemetryLog.entries();
Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
[TLOG.TERMINATION.RECHECK, EXPERIMENT4_ID, "os"]);
// Cleanup.
yield promiseRestartManager();
yield removeCacheFile();
});

View file

@ -0,0 +1,21 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/experiments/Experiments.jsm");
add_test(function test_experiments_activation() {
do_get_profile();
loadAddonManager();
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
let experiments = Experiments.instance();
Assert.ok(!experiments.enabled, "Experiments must be disabled if Telemetry is disabled.");
// TODO: Test that Experiments are turned back on when bug 1232648 lands.
run_next_test();
});

View file

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource:///modules/experiments/Experiments.jsm");
var cacheData = {
_enabled: true,
_manifestData: {
id: "foobartestid",
xpiURL: "http://example.com/foo.xpi",
xpiHash: "sha256:abcde",
startTime: 0,
endTime: 2000000000,
maxActiveSeconds: 40000000,
appName: "TestApp",
channel: "test-foo",
},
_needsUpdate: false,
_randomValue: 0.5,
_failedStart: false,
_name: "Foo",
_description: "Foobar",
_homepageURL: "",
_addonId: "foo@test",
_startDate: 0,
_endDate: 2000000000,
_branch: null
};
add_task(function* test_valid() {
let e = new Experiments.ExperimentEntry();
Assert.ok(e.initFromCacheData(cacheData));
Assert.ok(e.enabled);
});
add_task(function* test_upgrade() {
let e = new Experiments.ExperimentEntry();
delete cacheData._branch;
Assert.ok(e.initFromCacheData(cacheData));
Assert.ok(e.enabled);
});
add_task(function* test_missing() {
let e = new Experiments.ExperimentEntry();
delete cacheData._name;
Assert.ok(!e.initFromCacheData(cacheData));
});
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,31 @@
[DEFAULT]
head = head.js
tail =
tags = addons
firefox-appdir = browser
skip-if = toolkit == 'android'
support-files =
experiments_1.manifest
experiment-1.xpi
experiment-1a.xpi
experiment-2.xpi
experiment-racybranch.xpi
!/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
generated-files =
experiment-1.xpi
experiment-1a.xpi
experiment-2.xpi
experiment-racybranch.xpi
[test_activate.js]
[test_api.js]
[test_cache.js]
[test_cacherace.js]
[test_conditions.js]
[test_disableExperiments.js]
[test_fetch.js]
[test_telemetry.js]
[test_telemetry_disabled.js]
[test_previous_provider.js]
[test_upgrade.js]
[test_nethang_bug1012924.js]