browser/experiments/test/xpcshell/test_conditions.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/experiments/test/xpcshell/test_conditions.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,312 @@
     1.4 +/* Any copyright is dedicated to the Public Domain.
     1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */
     1.6 +
     1.7 +"use strict";
     1.8 +
     1.9 +
    1.10 +Cu.import("resource:///modules/experiments/Experiments.jsm");
    1.11 +
    1.12 +const FILE_MANIFEST            = "experiments.manifest";
    1.13 +const SEC_IN_ONE_DAY = 24 * 60 * 60;
    1.14 +const MS_IN_ONE_DAY  = SEC_IN_ONE_DAY * 1000;
    1.15 +
    1.16 +let gProfileDir = null;
    1.17 +let gHttpServer = null;
    1.18 +let gHttpRoot   = null;
    1.19 +let gReporter   = null;
    1.20 +let gPolicy     = null;
    1.21 +
    1.22 +
    1.23 +function ManifestEntry(data) {
    1.24 +  this.id = EXPERIMENT1_ID;
    1.25 +  this.xpiURL = "http://localhost:1/dummy.xpi";
    1.26 +  this.xpiHash = EXPERIMENT1_XPI_SHA1;
    1.27 +  this.startTime = new Date(2010, 0, 1, 12).getTime() / 1000;
    1.28 +  this.endTime = new Date(9001, 0, 1, 12).getTime() / 1000;
    1.29 +  this.maxActiveSeconds = SEC_IN_ONE_DAY;
    1.30 +  this.appName = ["XPCShell"];
    1.31 +  this.channel = ["nightly"];
    1.32 +
    1.33 +  data = data || {};
    1.34 +  for (let k of Object.keys(data)) {
    1.35 +    this[k] = data[k];
    1.36 +  }
    1.37 +
    1.38 +  if (!this.endTime) {
    1.39 +    this.endTime = this.startTime + 5 * SEC_IN_ONE_DAY;
    1.40 +  }
    1.41 +}
    1.42 +
    1.43 +function applicableFromManifestData(data, policy) {
    1.44 +  let manifestData = new ManifestEntry(data);
    1.45 +  let entry = new Experiments.ExperimentEntry(policy);
    1.46 +  entry.initFromManifestData(manifestData);
    1.47 +  return entry.isApplicable();
    1.48 +}
    1.49 +
    1.50 +function run_test() {
    1.51 +  run_next_test();
    1.52 +}
    1.53 +
    1.54 +add_task(function* test_setup() {
    1.55 +  createAppInfo();
    1.56 +  gProfileDir = do_get_profile();
    1.57 +  gPolicy = new Experiments.Policy();
    1.58 +
    1.59 +  gReporter = yield getReporter("json_payload_simple");
    1.60 +  yield gReporter.collectMeasurements();
    1.61 +  let payload = yield gReporter.getJSONPayload(true);
    1.62 +  do_register_cleanup(() => gReporter._shutdown());
    1.63 +
    1.64 +  patchPolicy(gPolicy, {
    1.65 +    updatechannel: () => "nightly",
    1.66 +    locale: () => "en-US",
    1.67 +    healthReportPayload: () => Promise.resolve(payload),
    1.68 +    random: () => 0.5,
    1.69 +  });
    1.70 +
    1.71 +  Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
    1.72 +  Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
    1.73 +  Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
    1.74 +
    1.75 +  let experiments = new Experiments.Experiments();
    1.76 +});
    1.77 +
    1.78 +function arraysEqual(a, b) {
    1.79 +  if (a.length !== b.length) {
    1.80 +    return false;
    1.81 +  }
    1.82 +
    1.83 +  for (let i=0; i<a.length; ++i) {
    1.84 +    if (a[i] !== b[i]) {
    1.85 +      return false;
    1.86 +    }
    1.87 +  }
    1.88 +
    1.89 +  return true;
    1.90 +}
    1.91 +
    1.92 +// This function exists solely to be .toSource()d
    1.93 +const sanityFilter = function filter(c) {
    1.94 +  if (c.telemetryPayload === undefined) {
    1.95 +    throw Error("No .telemetryPayload");
    1.96 +  }
    1.97 +  if (c.telemetryPayload.simpleMeasurements === undefined) {
    1.98 +    throw Error("No .simpleMeasurements");
    1.99 +  }
   1.100 +  if (c.healthReportPayload === undefined) {
   1.101 +    throw Error("No .healthReportPayload");
   1.102 +  }
   1.103 +  if (c.healthReportPayload.geckoAppInfo == undefined) {
   1.104 +    throw Error("No .geckoAppInfo");
   1.105 +  }
   1.106 +  return true;
   1.107 +}
   1.108 +
   1.109 +add_task(function* test_simpleFields() {
   1.110 +  let testData = [
   1.111 +    // "expected applicable?", failure reason or null, manifest data
   1.112 +
   1.113 +    // misc. environment
   1.114 +
   1.115 +    [false, ["appName"], {appName: []}],
   1.116 +    [false, ["appName"], {appName: ["foo", gAppInfo.name + "-invalid"]}],
   1.117 +    [true,  null,        {appName: ["not-an-app-name", gAppInfo.name]}],
   1.118 +
   1.119 +    [false, ["os"], {os: []}],
   1.120 +    [false, ["os"], {os: ["42", "abcdef"]}],
   1.121 +    [true,  null,   {os: [gAppInfo.OS, "plan9"]}],
   1.122 +
   1.123 +    [false, ["channel"], {channel: []}],
   1.124 +    [false, ["channel"], {channel: ["foo", gPolicy.updatechannel() + "-invalid"]}],
   1.125 +    [true,  null,        {channel: ["not-a-channel", gPolicy.updatechannel()]}],
   1.126 +
   1.127 +    [false, ["locale"], {locale: []}],
   1.128 +    [false, ["locale"], {locale: ["foo", gPolicy.locale + "-invalid"]}],
   1.129 +    [true,  null,       {locale: ["not-a-locale", gPolicy.locale()]}],
   1.130 +
   1.131 +    // version
   1.132 +
   1.133 +    [false, ["version"], {version: []}],
   1.134 +    [false, ["version"], {version: ["-1", gAppInfo.version + "-invalid", "asdf", "0,4", "99.99", "0.1.1.1"]}],
   1.135 +    [true,  null,        {version: ["99999999.999", "-1", gAppInfo.version]}],
   1.136 +
   1.137 +    [false, ["minVersion"], {minVersion: "1.0.1"}],
   1.138 +    [true,  null,           {minVersion: "1.0b1"}],
   1.139 +    [true,  null,           {minVersion: "1.0"}],
   1.140 +    [true,  null,           {minVersion: "0.9"}],
   1.141 +
   1.142 +    [false, ["maxVersion"], {maxVersion: "0.1"}],
   1.143 +    [false, ["maxVersion"], {maxVersion: "0.9.9"}],
   1.144 +    [false, ["maxVersion"], {maxVersion: "1.0b1"}],
   1.145 +    [true,  ["maxVersion"], {maxVersion: "1.0"}],
   1.146 +    [true,  ["maxVersion"], {maxVersion: "1.7pre"}],
   1.147 +
   1.148 +    // build id
   1.149 +
   1.150 +    [false, ["buildIDs"], {buildIDs: []}],
   1.151 +    [false, ["buildIDs"], {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID + "-invalid"]}],
   1.152 +    [true,  null,         {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID]}],
   1.153 +
   1.154 +    [true,  null,           {minBuildID: "2014060501"}],
   1.155 +    [true,  null,           {minBuildID: "2014060601"}],
   1.156 +    [false, ["minBuildID"], {minBuildID: "2014060701"}],
   1.157 +
   1.158 +    [false, ["maxBuildID"], {maxBuildID: "2014010101"}],
   1.159 +    [true,  null,           {maxBuildID: "2014060601"}],
   1.160 +    [true,  null,           {maxBuildID: "2014060901"}],
   1.161 +
   1.162 +    // sample
   1.163 +
   1.164 +    [false, ["sample"], {sample: -1 }],
   1.165 +    [false, ["sample"], {sample: 0.0}],
   1.166 +    [false, ["sample"], {sample: 0.1}],
   1.167 +    [true,  null,       {sample: 0.5}],
   1.168 +    [true,  null,       {sample: 0.6}],
   1.169 +    [true,  null,       {sample: 1.0}],
   1.170 +    [true,  null,       {sample: 0.5}],
   1.171 +
   1.172 +    // experiment control
   1.173 +
   1.174 +    [false, ["disabled"], {disabled: true}],
   1.175 +    [true,  null,         {disabled: false}],
   1.176 +
   1.177 +    [false, ["frozen"], {frozen: true}],
   1.178 +    [true,  null,       {frozen: false}],
   1.179 +
   1.180 +    [false, null, {frozen: true,  disabled: true}],
   1.181 +    [false, null, {frozen: true,  disabled: false}],
   1.182 +    [false, null, {frozen: false, disabled: true}],
   1.183 +    [true,  null, {frozen: false, disabled: false}],
   1.184 +
   1.185 +    // jsfilter
   1.186 +
   1.187 +    [true,  null, {jsfilter: "function filter(c) { return true; }"}],
   1.188 +    [false, ["jsfilter-false"], {jsfilter: "function filter(c) { return false; }"}],
   1.189 +    [true,  null, {jsfilter: "function filter(c) { return 123; }"}], // truthy
   1.190 +    [false, ["jsfilter-false"], {jsfilter: "function filter(c) { return ''; }"}], // falsy
   1.191 +    [false, ["jsfilter-false"], {jsfilter: "function filter(c) { var a = []; }"}], // undefined
   1.192 +    [false, ["jsfilter-threw", "some error"], {jsfilter: "function filter(c) { throw new Error('some error'); }"}],
   1.193 +    [false, ["jsfilter-evalfailed"], {jsfilter: "123, this won't work"}],
   1.194 +    [true,  null, {jsfilter: "var filter = " + sanityFilter.toSource()}],
   1.195 +  ];
   1.196 +
   1.197 +  for (let i=0; i<testData.length; ++i) {
   1.198 +    let entry = testData[i];
   1.199 +    let applicable;
   1.200 +    let reason = null;
   1.201 +
   1.202 +    yield applicableFromManifestData(entry[2], gPolicy).then(
   1.203 +      value => applicable = value,
   1.204 +      value => {
   1.205 +        applicable = false;
   1.206 +        reason = value;
   1.207 +      }
   1.208 +    );
   1.209 +
   1.210 +    Assert.equal(applicable, entry[0],
   1.211 +      "Experiment entry applicability should match for test "
   1.212 +      + i + ": " + JSON.stringify(entry[2]));
   1.213 +
   1.214 +    let expectedReason = entry[1];
   1.215 +    if (!applicable && expectedReason) {
   1.216 +      Assert.ok(arraysEqual(reason, expectedReason),
   1.217 +        "Experiment rejection reasons should match for test " + i + ". "
   1.218 +        + "Got " + JSON.stringify(reason) + ", expected "
   1.219 +        + JSON.stringify(expectedReason));
   1.220 +    }
   1.221 +  }
   1.222 +});
   1.223 +
   1.224 +add_task(function* test_times() {
   1.225 +  let now = new Date(2014, 5, 6, 12);
   1.226 +  let nowSec = now.getTime() / 1000;
   1.227 +  let testData = [
   1.228 +    // "expected applicable?", rejection reason or null, fake now date, manifest data
   1.229 +
   1.230 +    // start time
   1.231 +
   1.232 +    [true,  null, now,
   1.233 +      {startTime: nowSec -  5 * SEC_IN_ONE_DAY,
   1.234 +         endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.235 +    [true,  null, now,
   1.236 +      {startTime: nowSec ,
   1.237 +         endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.238 +    [false,  "startTime", now,
   1.239 +      {startTime: nowSec +  5 * SEC_IN_ONE_DAY,
   1.240 +         endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.241 +
   1.242 +    // end time
   1.243 +
   1.244 +    [false,  "endTime", now,
   1.245 +      {startTime: nowSec -  5 * SEC_IN_ONE_DAY,
   1.246 +         endTime: nowSec - 10 * SEC_IN_ONE_DAY}],
   1.247 +    [false,  "endTime", now,
   1.248 +      {startTime: nowSec -  5 * SEC_IN_ONE_DAY,
   1.249 +         endTime: nowSec -  5 * SEC_IN_ONE_DAY}],
   1.250 +
   1.251 +    // max start time
   1.252 +
   1.253 +    [false,  "maxStartTime", now,
   1.254 +      {maxStartTime: nowSec - 15 * SEC_IN_ONE_DAY,
   1.255 +          startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.256 +            endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.257 +    [false,  "maxStartTime", now,
   1.258 +      {maxStartTime: nowSec -  1 * SEC_IN_ONE_DAY,
   1.259 +          startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.260 +            endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.261 +    [false,  "maxStartTime", now,
   1.262 +      {maxStartTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.263 +          startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.264 +            endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.265 +    [true,  null, now,
   1.266 +      {maxStartTime: nowSec,
   1.267 +          startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.268 +            endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.269 +    [true,  null, now,
   1.270 +      {maxStartTime: nowSec +  1 * SEC_IN_ONE_DAY,
   1.271 +          startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.272 +            endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.273 +
   1.274 +    // max active seconds
   1.275 +
   1.276 +    [true,  null, now,
   1.277 +      {maxActiveSeconds:           5 * SEC_IN_ONE_DAY,
   1.278 +              startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.279 +                endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.280 +    [true,  null, now,
   1.281 +      {maxActiveSeconds:          10 * SEC_IN_ONE_DAY,
   1.282 +              startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.283 +                endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.284 +    [true,  null, now,
   1.285 +      {maxActiveSeconds:          15 * SEC_IN_ONE_DAY,
   1.286 +              startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.287 +                endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.288 +    [true,  null, now,
   1.289 +      {maxActiveSeconds:          20 * SEC_IN_ONE_DAY,
   1.290 +              startTime: nowSec - 10 * SEC_IN_ONE_DAY,
   1.291 +                endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
   1.292 +  ];
   1.293 +
   1.294 +  for (let i=0; i<testData.length; ++i) {
   1.295 +    let entry = testData[i];
   1.296 +    let applicable;
   1.297 +    let reason = null;
   1.298 +    defineNow(gPolicy, entry[2]);
   1.299 +
   1.300 +    yield applicableFromManifestData(entry[3], gPolicy).then(
   1.301 +      value => applicable = value,
   1.302 +      value => {
   1.303 +        applicable = false;
   1.304 +        reason = value;
   1.305 +      }
   1.306 +    );
   1.307 +
   1.308 +    Assert.equal(applicable, entry[0],
   1.309 +      "Experiment entry applicability should match for test "
   1.310 +      + i + ": " + JSON.stringify([entry[2], entry[3]]));
   1.311 +    if (!applicable && entry[1]) {
   1.312 +      Assert.equal(reason, entry[1], "Experiment rejection reason should match for test " + i);
   1.313 +    }
   1.314 +  }
   1.315 +});

mercurial