summaryrefslogtreecommitdiffstats
path: root/browser/experiments/test/xpcshell/test_conditions.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/experiments/test/xpcshell/test_conditions.js')
-rw-r--r--browser/experiments/test/xpcshell/test_conditions.js325
1 files changed, 325 insertions, 0 deletions
diff --git a/browser/experiments/test/xpcshell/test_conditions.js b/browser/experiments/test/xpcshell/test_conditions.js
new file mode 100644
index 000000000..23c147fdb
--- /dev/null
+++ b/browser/experiments/test/xpcshell/test_conditions.js
@@ -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();
+});