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 +});