forked from mirrors/gecko-dev
		
	 a53020d022
			
		
	
	
		a53020d022
		
	
	
	
	
		
			
			MozReview-Commit-ID: 35MaseieNUk --HG-- extra : rebase_source : 98eaec6a67fd3b30ea6b0be641f26c3911012fab
		
			
				
	
	
		
			321 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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();
 | |
| }
 | |
| 
 | |
| add_task(async function test_setup() {
 | |
|   createAppInfo();
 | |
|   do_get_profile();
 | |
|   startAddonManagerOnly();
 | |
|   await 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(async 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;
 | |
| 
 | |
|     await 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(async 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]);
 | |
| 
 | |
|     await 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(async function test_shutdown() {
 | |
|   await TelemetryController.testShutdown();
 | |
| });
 |