Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | "use strict"; |
michael@0 | 5 | |
michael@0 | 6 | Cu.import("resource://testing-common/httpd.js"); |
michael@0 | 7 | Cu.import("resource://gre/modules/TelemetryLog.jsm"); |
michael@0 | 8 | let bsp = Cu.import("resource:///modules/experiments/Experiments.jsm"); |
michael@0 | 9 | |
michael@0 | 10 | |
michael@0 | 11 | const FILE_MANIFEST = "experiments.manifest"; |
michael@0 | 12 | const MANIFEST_HANDLER = "manifests/handler"; |
michael@0 | 13 | |
michael@0 | 14 | const SEC_IN_ONE_DAY = 24 * 60 * 60; |
michael@0 | 15 | const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000; |
michael@0 | 16 | |
michael@0 | 17 | |
michael@0 | 18 | let gProfileDir = null; |
michael@0 | 19 | let gHttpServer = null; |
michael@0 | 20 | let gHttpRoot = null; |
michael@0 | 21 | let gDataRoot = null; |
michael@0 | 22 | let gReporter = null; |
michael@0 | 23 | let gPolicy = null; |
michael@0 | 24 | let gManifestObject = null; |
michael@0 | 25 | let gManifestHandlerURI = null; |
michael@0 | 26 | |
michael@0 | 27 | const TLOG = bsp.TELEMETRY_LOG; |
michael@0 | 28 | |
michael@0 | 29 | let gGlobalScope = this; |
michael@0 | 30 | function loadAddonManager() { |
michael@0 | 31 | let ns = {}; |
michael@0 | 32 | Cu.import("resource://gre/modules/Services.jsm", ns); |
michael@0 | 33 | let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js"; |
michael@0 | 34 | let file = do_get_file(head); |
michael@0 | 35 | let uri = ns.Services.io.newFileURI(file); |
michael@0 | 36 | ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope); |
michael@0 | 37 | createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); |
michael@0 | 38 | startupManager(); |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | function checkEvent(event, id, data) |
michael@0 | 42 | { |
michael@0 | 43 | do_print("Checking message " + id); |
michael@0 | 44 | Assert.equal(event[0], id, "id should match"); |
michael@0 | 45 | Assert.ok(event[1] > 0, "timestamp should be greater than 0"); |
michael@0 | 46 | |
michael@0 | 47 | if (data === undefined) { |
michael@0 | 48 | Assert.equal(event.length, 2, "event array should have 2 entries"); |
michael@0 | 49 | } else { |
michael@0 | 50 | Assert.equal(event.length, data.length + 2, "event entry count should match expected count"); |
michael@0 | 51 | for (var i = 0; i < data.length; ++i) { |
michael@0 | 52 | Assert.equal(typeof(event[i + 2]), "string", "event entry should be a string"); |
michael@0 | 53 | Assert.equal(event[i + 2], data[i], "event entry should match expected entry"); |
michael@0 | 54 | } |
michael@0 | 55 | } |
michael@0 | 56 | } |
michael@0 | 57 | |
michael@0 | 58 | function run_test() { |
michael@0 | 59 | run_next_test(); |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | add_task(function* test_setup() { |
michael@0 | 63 | loadAddonManager(); |
michael@0 | 64 | gProfileDir = do_get_profile(); |
michael@0 | 65 | |
michael@0 | 66 | gHttpServer = new HttpServer(); |
michael@0 | 67 | gHttpServer.start(-1); |
michael@0 | 68 | let port = gHttpServer.identity.primaryPort; |
michael@0 | 69 | gHttpRoot = "http://localhost:" + port + "/"; |
michael@0 | 70 | gDataRoot = gHttpRoot + "data/"; |
michael@0 | 71 | gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER; |
michael@0 | 72 | gHttpServer.registerDirectory("/data/", do_get_cwd()); |
michael@0 | 73 | gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => { |
michael@0 | 74 | response.setStatusLine(null, 200, "OK"); |
michael@0 | 75 | response.write(JSON.stringify(gManifestObject)); |
michael@0 | 76 | response.processAsync(); |
michael@0 | 77 | response.finish(); |
michael@0 | 78 | }); |
michael@0 | 79 | do_register_cleanup(() => gHttpServer.stop(() => {})); |
michael@0 | 80 | |
michael@0 | 81 | disableCertificateChecks(); |
michael@0 | 82 | |
michael@0 | 83 | Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true); |
michael@0 | 84 | Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0); |
michael@0 | 85 | Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true); |
michael@0 | 86 | Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI); |
michael@0 | 87 | Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0); |
michael@0 | 88 | |
michael@0 | 89 | gReporter = yield getReporter("json_payload_simple"); |
michael@0 | 90 | yield gReporter.collectMeasurements(); |
michael@0 | 91 | let payload = yield gReporter.getJSONPayload(true); |
michael@0 | 92 | do_register_cleanup(() => gReporter._shutdown()); |
michael@0 | 93 | |
michael@0 | 94 | gPolicy = new Experiments.Policy(); |
michael@0 | 95 | let dummyTimer = { cancel: () => {}, clear: () => {} }; |
michael@0 | 96 | patchPolicy(gPolicy, { |
michael@0 | 97 | updatechannel: () => "nightly", |
michael@0 | 98 | healthReportPayload: () => Promise.resolve(payload), |
michael@0 | 99 | oneshotTimer: (callback, timeout, thisObj, name) => dummyTimer, |
michael@0 | 100 | }); |
michael@0 | 101 | |
michael@0 | 102 | yield removeCacheFile(); |
michael@0 | 103 | }); |
michael@0 | 104 | |
michael@0 | 105 | // Test basic starting and stopping of experiments. |
michael@0 | 106 | |
michael@0 | 107 | add_task(function* test_telemetryBasics() { |
michael@0 | 108 | // Check TelemetryLog instead of TelemetryPing.getPayload().log because |
michael@0 | 109 | // TelemetryPing gets Experiments.instance() and side-effects log entries. |
michael@0 | 110 | |
michael@0 | 111 | const OBSERVER_TOPIC = "experiments-changed"; |
michael@0 | 112 | let observerFireCount = 0; |
michael@0 | 113 | let expectedLogLength = 0; |
michael@0 | 114 | |
michael@0 | 115 | // Dates the following tests are based on. |
michael@0 | 116 | |
michael@0 | 117 | let baseDate = new Date(2014, 5, 1, 12); |
michael@0 | 118 | let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY); |
michael@0 | 119 | let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY); |
michael@0 | 120 | let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY); |
michael@0 | 121 | let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY); |
michael@0 | 122 | |
michael@0 | 123 | // The manifest data we test with. |
michael@0 | 124 | |
michael@0 | 125 | gManifestObject = { |
michael@0 | 126 | "version": 1, |
michael@0 | 127 | experiments: [ |
michael@0 | 128 | { |
michael@0 | 129 | id: EXPERIMENT1_ID, |
michael@0 | 130 | xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME, |
michael@0 | 131 | xpiHash: EXPERIMENT1_XPI_SHA1, |
michael@0 | 132 | startTime: dateToSeconds(startDate1), |
michael@0 | 133 | endTime: dateToSeconds(endDate1), |
michael@0 | 134 | maxActiveSeconds: 10 * SEC_IN_ONE_DAY, |
michael@0 | 135 | appName: ["XPCShell"], |
michael@0 | 136 | channel: ["nightly"], |
michael@0 | 137 | }, |
michael@0 | 138 | { |
michael@0 | 139 | id: EXPERIMENT2_ID, |
michael@0 | 140 | xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME, |
michael@0 | 141 | xpiHash: EXPERIMENT2_XPI_SHA1, |
michael@0 | 142 | startTime: dateToSeconds(startDate2), |
michael@0 | 143 | endTime: dateToSeconds(endDate2), |
michael@0 | 144 | maxActiveSeconds: 10 * SEC_IN_ONE_DAY, |
michael@0 | 145 | appName: ["XPCShell"], |
michael@0 | 146 | channel: ["nightly"], |
michael@0 | 147 | }, |
michael@0 | 148 | ], |
michael@0 | 149 | }; |
michael@0 | 150 | |
michael@0 | 151 | // Data to compare the result of Experiments.getExperiments() against. |
michael@0 | 152 | |
michael@0 | 153 | let experimentListData = [ |
michael@0 | 154 | { |
michael@0 | 155 | id: EXPERIMENT2_ID, |
michael@0 | 156 | name: "Test experiment 2", |
michael@0 | 157 | description: "And yet another experiment that experiments experimentally.", |
michael@0 | 158 | }, |
michael@0 | 159 | { |
michael@0 | 160 | id: EXPERIMENT1_ID, |
michael@0 | 161 | name: EXPERIMENT1_NAME, |
michael@0 | 162 | description: "Yet another experiment that experiments experimentally.", |
michael@0 | 163 | }, |
michael@0 | 164 | ]; |
michael@0 | 165 | |
michael@0 | 166 | let experiments = new Experiments.Experiments(gPolicy); |
michael@0 | 167 | |
michael@0 | 168 | // Trigger update, clock set to before any activation. |
michael@0 | 169 | // Use updateManifest() to provide for coverage of that path. |
michael@0 | 170 | |
michael@0 | 171 | let now = baseDate; |
michael@0 | 172 | defineNow(gPolicy, now); |
michael@0 | 173 | |
michael@0 | 174 | yield experiments.updateManifest(); |
michael@0 | 175 | let list = yield experiments.getExperiments(); |
michael@0 | 176 | Assert.equal(list.length, 0, "Experiment list should be empty."); |
michael@0 | 177 | |
michael@0 | 178 | expectedLogLength += 2; |
michael@0 | 179 | let log = TelemetryLog.entries(); |
michael@0 | 180 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 181 | checkEvent(log[log.length-2], TLOG.ACTIVATION_KEY, |
michael@0 | 182 | [TLOG.ACTIVATION.REJECTED, EXPERIMENT1_ID, "startTime"]); |
michael@0 | 183 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 184 | [TLOG.ACTIVATION.REJECTED, EXPERIMENT2_ID, "startTime"]); |
michael@0 | 185 | |
michael@0 | 186 | // Trigger update, clock set for experiment 1 to start. |
michael@0 | 187 | |
michael@0 | 188 | now = futureDate(startDate1, 5 * MS_IN_ONE_DAY); |
michael@0 | 189 | defineNow(gPolicy, now); |
michael@0 | 190 | |
michael@0 | 191 | yield experiments.updateManifest(); |
michael@0 | 192 | list = yield experiments.getExperiments(); |
michael@0 | 193 | Assert.equal(list.length, 1, "Experiment list should have 1 entry now."); |
michael@0 | 194 | |
michael@0 | 195 | expectedLogLength += 1; |
michael@0 | 196 | log = TelemetryLog.entries(); |
michael@0 | 197 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries. Got " + log.toSource()); |
michael@0 | 198 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 199 | [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT1_ID]); |
michael@0 | 200 | |
michael@0 | 201 | // Trigger update, clock set for experiment 1 to stop. |
michael@0 | 202 | |
michael@0 | 203 | now = futureDate(endDate1, 1000); |
michael@0 | 204 | defineNow(gPolicy, now); |
michael@0 | 205 | |
michael@0 | 206 | yield experiments.updateManifest(); |
michael@0 | 207 | list = yield experiments.getExperiments(); |
michael@0 | 208 | Assert.equal(list.length, 1, "Experiment list should have 1 entry."); |
michael@0 | 209 | |
michael@0 | 210 | expectedLogLength += 2; |
michael@0 | 211 | log = TelemetryLog.entries(); |
michael@0 | 212 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 213 | checkEvent(log[log.length-2], TLOG.TERMINATION_KEY, |
michael@0 | 214 | [TLOG.TERMINATION.EXPIRED, EXPERIMENT1_ID]); |
michael@0 | 215 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 216 | [TLOG.ACTIVATION.REJECTED, EXPERIMENT2_ID, "startTime"]); |
michael@0 | 217 | |
michael@0 | 218 | // Trigger update, clock set for experiment 2 to start with invalid hash. |
michael@0 | 219 | |
michael@0 | 220 | now = startDate2; |
michael@0 | 221 | defineNow(gPolicy, now); |
michael@0 | 222 | gManifestObject.experiments[1].xpiHash = "sha1:0000000000000000000000000000000000000000"; |
michael@0 | 223 | |
michael@0 | 224 | yield experiments.updateManifest(); |
michael@0 | 225 | list = yield experiments.getExperiments(); |
michael@0 | 226 | Assert.equal(list.length, 1, "Experiment list should have 1 entries."); |
michael@0 | 227 | |
michael@0 | 228 | expectedLogLength += 1; |
michael@0 | 229 | log = TelemetryLog.entries(); |
michael@0 | 230 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 231 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 232 | [TLOG.ACTIVATION.INSTALL_FAILURE, EXPERIMENT2_ID]); |
michael@0 | 233 | |
michael@0 | 234 | // Trigger update, clock set for experiment 2 to properly start now. |
michael@0 | 235 | |
michael@0 | 236 | now = futureDate(now, MS_IN_ONE_DAY); |
michael@0 | 237 | defineNow(gPolicy, now); |
michael@0 | 238 | gManifestObject.experiments[1].xpiHash = EXPERIMENT2_XPI_SHA1; |
michael@0 | 239 | |
michael@0 | 240 | yield experiments.updateManifest(); |
michael@0 | 241 | list = yield experiments.getExperiments(); |
michael@0 | 242 | Assert.equal(list.length, 2, "Experiment list should have 2 entries."); |
michael@0 | 243 | |
michael@0 | 244 | expectedLogLength += 1; |
michael@0 | 245 | log = TelemetryLog.entries(); |
michael@0 | 246 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 247 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 248 | [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT2_ID]); |
michael@0 | 249 | |
michael@0 | 250 | // Fake user uninstall of experiment via add-on manager. |
michael@0 | 251 | |
michael@0 | 252 | now = futureDate(now, MS_IN_ONE_DAY); |
michael@0 | 253 | defineNow(gPolicy, now); |
michael@0 | 254 | |
michael@0 | 255 | yield experiments.disableExperiment(TLOG.TERMINATION.ADDON_UNINSTALLED); |
michael@0 | 256 | list = yield experiments.getExperiments(); |
michael@0 | 257 | Assert.equal(list.length, 2, "Experiment list should have 2 entries."); |
michael@0 | 258 | |
michael@0 | 259 | expectedLogLength += 1; |
michael@0 | 260 | log = TelemetryLog.entries(); |
michael@0 | 261 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 262 | checkEvent(log[log.length-1], TLOG.TERMINATION_KEY, |
michael@0 | 263 | [TLOG.TERMINATION.ADDON_UNINSTALLED, EXPERIMENT2_ID]); |
michael@0 | 264 | |
michael@0 | 265 | // Trigger update with experiment 1a ready to start. |
michael@0 | 266 | |
michael@0 | 267 | now = futureDate(now, MS_IN_ONE_DAY); |
michael@0 | 268 | defineNow(gPolicy, now); |
michael@0 | 269 | gManifestObject.experiments[0].id = EXPERIMENT3_ID; |
michael@0 | 270 | gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY)); |
michael@0 | 271 | |
michael@0 | 272 | yield experiments.updateManifest(); |
michael@0 | 273 | list = yield experiments.getExperiments(); |
michael@0 | 274 | Assert.equal(list.length, 3, "Experiment list should have 3 entries."); |
michael@0 | 275 | |
michael@0 | 276 | expectedLogLength += 1; |
michael@0 | 277 | log = TelemetryLog.entries(); |
michael@0 | 278 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 279 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 280 | [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT3_ID]); |
michael@0 | 281 | |
michael@0 | 282 | // Trigger disable of an experiment via the API. |
michael@0 | 283 | |
michael@0 | 284 | now = futureDate(now, MS_IN_ONE_DAY); |
michael@0 | 285 | defineNow(gPolicy, now); |
michael@0 | 286 | |
michael@0 | 287 | yield experiments.disableExperiment(TLOG.TERMINATION.FROM_API); |
michael@0 | 288 | list = yield experiments.getExperiments(); |
michael@0 | 289 | Assert.equal(list.length, 3, "Experiment list should have 3 entries."); |
michael@0 | 290 | |
michael@0 | 291 | expectedLogLength += 1; |
michael@0 | 292 | log = TelemetryLog.entries(); |
michael@0 | 293 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 294 | checkEvent(log[log.length-1], TLOG.TERMINATION_KEY, |
michael@0 | 295 | [TLOG.TERMINATION.FROM_API, EXPERIMENT3_ID]); |
michael@0 | 296 | |
michael@0 | 297 | // Trigger update with experiment 1a ready to start. |
michael@0 | 298 | |
michael@0 | 299 | now = futureDate(now, MS_IN_ONE_DAY); |
michael@0 | 300 | defineNow(gPolicy, now); |
michael@0 | 301 | gManifestObject.experiments[0].id = EXPERIMENT4_ID; |
michael@0 | 302 | gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY)); |
michael@0 | 303 | |
michael@0 | 304 | yield experiments.updateManifest(); |
michael@0 | 305 | list = yield experiments.getExperiments(); |
michael@0 | 306 | Assert.equal(list.length, 4, "Experiment list should have 4 entries."); |
michael@0 | 307 | |
michael@0 | 308 | expectedLogLength += 1; |
michael@0 | 309 | log = TelemetryLog.entries(); |
michael@0 | 310 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 311 | checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY, |
michael@0 | 312 | [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT4_ID]); |
michael@0 | 313 | |
michael@0 | 314 | // Trigger experiment termination by something other than expiry via the manifest. |
michael@0 | 315 | |
michael@0 | 316 | now = futureDate(now, MS_IN_ONE_DAY); |
michael@0 | 317 | defineNow(gPolicy, now); |
michael@0 | 318 | gManifestObject.experiments[0].os = "Plan9"; |
michael@0 | 319 | |
michael@0 | 320 | yield experiments.updateManifest(); |
michael@0 | 321 | list = yield experiments.getExperiments(); |
michael@0 | 322 | Assert.equal(list.length, 4, "Experiment list should have 4 entries."); |
michael@0 | 323 | |
michael@0 | 324 | expectedLogLength += 1; |
michael@0 | 325 | log = TelemetryLog.entries(); |
michael@0 | 326 | Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries."); |
michael@0 | 327 | checkEvent(log[log.length-1], TLOG.TERMINATION_KEY, |
michael@0 | 328 | [TLOG.TERMINATION.RECHECK, EXPERIMENT4_ID, "os"]); |
michael@0 | 329 | |
michael@0 | 330 | // Cleanup. |
michael@0 | 331 | |
michael@0 | 332 | yield experiments.uninit(); |
michael@0 | 333 | yield removeCacheFile(); |
michael@0 | 334 | }); |