1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/healthreport/tests/xpcshell/test_healthreporter.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1197 @@ 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 +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; 1.10 + 1.11 +Cu.import("resource://services-common/observers.js"); 1.12 +Cu.import("resource://services-common/utils.js"); 1.13 +Cu.import("resource://gre/modules/Promise.jsm"); 1.14 +Cu.import("resource://gre/modules/Metrics.jsm"); 1.15 +Cu.import("resource://gre/modules/osfile.jsm"); 1.16 +Cu.import("resource://gre/modules/Preferences.jsm"); 1.17 +let bsp = Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm"); 1.18 +Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); 1.19 +Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); 1.20 +Cu.import("resource://gre/modules/Services.jsm"); 1.21 +Cu.import("resource://gre/modules/Task.jsm"); 1.22 +Cu.import("resource://testing-common/httpd.js"); 1.23 +Cu.import("resource://testing-common/services-common/bagheeraserver.js"); 1.24 +Cu.import("resource://testing-common/services/metrics/mocks.jsm"); 1.25 +Cu.import("resource://testing-common/services/healthreport/utils.jsm"); 1.26 +Cu.import("resource://testing-common/AppData.jsm"); 1.27 + 1.28 + 1.29 +const DUMMY_URI = "http://localhost:62013/"; 1.30 +const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; 1.31 + 1.32 +const HealthReporterState = bsp.HealthReporterState; 1.33 + 1.34 + 1.35 +function defineNow(policy, now) { 1.36 + print("Adjusting fake system clock to " + now); 1.37 + Object.defineProperty(policy, "now", { 1.38 + value: function customNow() { 1.39 + return now; 1.40 + }, 1.41 + writable: true, 1.42 + }); 1.43 +} 1.44 + 1.45 +function getReporter(name, uri, inspected) { 1.46 + return Task.spawn(function init() { 1.47 + let reporter = getHealthReporter(name, uri, inspected); 1.48 + yield reporter.init(); 1.49 + 1.50 + yield reporter._providerManager.registerProviderFromType( 1.51 + HealthReportProvider); 1.52 + 1.53 + throw new Task.Result(reporter); 1.54 + }); 1.55 +} 1.56 + 1.57 +function getReporterAndServer(name, namespace="test") { 1.58 + return Task.spawn(function get() { 1.59 + let server = new BagheeraServer(); 1.60 + server.createNamespace(namespace); 1.61 + server.start(); 1.62 + 1.63 + let reporter = yield getReporter(name, server.serverURI); 1.64 + reporter.serverNamespace = namespace; 1.65 + 1.66 + throw new Task.Result([reporter, server]); 1.67 + }); 1.68 +} 1.69 + 1.70 +function shutdownServer(server) { 1.71 + let deferred = Promise.defer(); 1.72 + server.stop(deferred.resolve.bind(deferred)); 1.73 + 1.74 + return deferred.promise; 1.75 +} 1.76 + 1.77 +function getHealthReportProviderValues(reporter, day=null) { 1.78 + return Task.spawn(function getValues() { 1.79 + let p = reporter.getProvider("org.mozilla.healthreport"); 1.80 + do_check_neq(p, null); 1.81 + let m = p.getMeasurement("submissions", 2); 1.82 + do_check_neq(m, null); 1.83 + 1.84 + let data = yield reporter._storage.getMeasurementValues(m.id); 1.85 + if (!day) { 1.86 + throw new Task.Result(data); 1.87 + } 1.88 + 1.89 + do_check_true(data.days.hasDay(day)); 1.90 + let serializer = m.serializer(m.SERIALIZE_JSON) 1.91 + let json = serializer.daily(data.days.getDay(day)); 1.92 + do_check_eq(json._v, 2); 1.93 + 1.94 + throw new Task.Result(json); 1.95 + }); 1.96 +} 1.97 + 1.98 +function run_test() { 1.99 + run_next_test(); 1.100 +} 1.101 + 1.102 +// run_test() needs to finish synchronously, so we do async init here. 1.103 +add_task(function test_init() { 1.104 + yield makeFakeAppDir(); 1.105 +}); 1.106 + 1.107 +add_task(function test_constructor() { 1.108 + let reporter = yield getReporter("constructor"); 1.109 + 1.110 + try { 1.111 + do_check_eq(reporter.lastPingDate.getTime(), 0); 1.112 + do_check_null(reporter.lastSubmitID); 1.113 + do_check_eq(typeof(reporter._state), "object"); 1.114 + do_check_eq(reporter._state.lastPingDate.getTime(), 0); 1.115 + do_check_eq(reporter._state.remoteIDs.length, 0); 1.116 + do_check_eq(reporter._state.clientIDVersion, 1); 1.117 + do_check_neq(reporter._state.clientID, null); 1.118 + 1.119 + let failed = false; 1.120 + try { 1.121 + new HealthReporter("foo.bar"); 1.122 + } catch (ex) { 1.123 + failed = true; 1.124 + do_check_true(ex.message.startsWith("Branch must end")); 1.125 + } finally { 1.126 + do_check_true(failed); 1.127 + failed = false; 1.128 + } 1.129 + } finally { 1.130 + reporter._shutdown(); 1.131 + } 1.132 +}); 1.133 + 1.134 +add_task(function test_shutdown_normal() { 1.135 + let reporter = yield getReporter("shutdown_normal"); 1.136 + 1.137 + // We can't send "quit-application" notification because the xpcshell runner 1.138 + // will shut down! 1.139 + reporter._initiateShutdown(); 1.140 + reporter._waitForShutdown(); 1.141 +}); 1.142 + 1.143 +add_task(function test_shutdown_storage_in_progress() { 1.144 + let reporter = yield getHealthReporter("shutdown_storage_in_progress", DUMMY_URI, true); 1.145 + 1.146 + reporter.onStorageCreated = function () { 1.147 + print("Faking shutdown during storage initialization."); 1.148 + reporter._initiateShutdown(); 1.149 + }; 1.150 + 1.151 + reporter.init(); 1.152 + 1.153 + reporter._waitForShutdown(); 1.154 + do_check_eq(reporter.providerManagerShutdownCount, 0); 1.155 + do_check_eq(reporter.storageCloseCount, 1); 1.156 +}); 1.157 + 1.158 +// Ensure that a shutdown triggered while provider manager is initializing 1.159 +// results in shutdown and storage closure. 1.160 +add_task(function test_shutdown_provider_manager_in_progress() { 1.161 + let reporter = yield getHealthReporter("shutdown_provider_manager_in_progress", 1.162 + DUMMY_URI, true); 1.163 + 1.164 + reporter.onProviderManagerInitialized = function () { 1.165 + print("Faking shutdown during provider manager initialization."); 1.166 + reporter._initiateShutdown(); 1.167 + }; 1.168 + 1.169 + reporter.init(); 1.170 + 1.171 + // This will hang if shutdown logic is busted. 1.172 + reporter._waitForShutdown(); 1.173 + do_check_eq(reporter.providerManagerShutdownCount, 1); 1.174 + do_check_eq(reporter.storageCloseCount, 1); 1.175 +}); 1.176 + 1.177 +// Simulates an error during provider manager initialization and verifies we shut down. 1.178 +add_task(function test_shutdown_when_provider_manager_errors() { 1.179 + let reporter = yield getHealthReporter("shutdown_when_provider_manager_errors", 1.180 + DUMMY_URI, true); 1.181 + 1.182 + reporter.onInitializeProviderManagerFinished = function () { 1.183 + print("Throwing fake error."); 1.184 + throw new Error("Fake error during provider manager initialization."); 1.185 + }; 1.186 + 1.187 + reporter.init(); 1.188 + 1.189 + // This will hang if shutdown logic is busted. 1.190 + reporter._waitForShutdown(); 1.191 + do_check_eq(reporter.providerManagerShutdownCount, 1); 1.192 + do_check_eq(reporter.storageCloseCount, 1); 1.193 +}); 1.194 + 1.195 +// Pull-only providers are only initialized at collect time. 1.196 +add_task(function test_pull_only_providers() { 1.197 + const category = "healthreporter-constant-only"; 1.198 + 1.199 + let cm = Cc["@mozilla.org/categorymanager;1"] 1.200 + .getService(Ci.nsICategoryManager); 1.201 + cm.addCategoryEntry(category, "DummyProvider", 1.202 + "resource://testing-common/services/metrics/mocks.jsm", 1.203 + false, true); 1.204 + cm.addCategoryEntry(category, "DummyConstantProvider", 1.205 + "resource://testing-common/services/metrics/mocks.jsm", 1.206 + false, true); 1.207 + 1.208 + let reporter = yield getReporter("constant_only_providers"); 1.209 + try { 1.210 + let initCount = reporter._providerManager.providers.length; 1.211 + yield reporter._providerManager.registerProvidersFromCategoryManager(category); 1.212 + do_check_eq(reporter._providerManager._providers.size, initCount + 1); 1.213 + do_check_true(reporter._storage.hasProvider("DummyProvider")); 1.214 + do_check_false(reporter._storage.hasProvider("DummyConstantProvider")); 1.215 + do_check_neq(reporter.getProvider("DummyProvider"), null); 1.216 + do_check_null(reporter.getProvider("DummyConstantProvider")); 1.217 + 1.218 + yield reporter.collectMeasurements(); 1.219 + 1.220 + do_check_eq(reporter._providerManager._providers.size, initCount + 1); 1.221 + do_check_true(reporter._storage.hasProvider("DummyConstantProvider")); 1.222 + 1.223 + let mID = reporter._storage.measurementID("DummyConstantProvider", "DummyMeasurement", 1); 1.224 + let values = yield reporter._storage.getMeasurementValues(mID); 1.225 + do_check_true(values.singular.size > 0); 1.226 + } finally { 1.227 + reporter._shutdown(); 1.228 + } 1.229 +}); 1.230 + 1.231 +add_task(function test_collect_daily() { 1.232 + let reporter = yield getReporter("collect_daily"); 1.233 + 1.234 + try { 1.235 + let now = new Date(); 1.236 + let provider = new DummyProvider(); 1.237 + yield reporter._providerManager.registerProvider(provider); 1.238 + yield reporter.collectMeasurements(); 1.239 + 1.240 + do_check_eq(provider.collectConstantCount, 1); 1.241 + do_check_eq(provider.collectDailyCount, 1); 1.242 + 1.243 + yield reporter.collectMeasurements(); 1.244 + do_check_eq(provider.collectConstantCount, 1); 1.245 + do_check_eq(provider.collectDailyCount, 1); 1.246 + 1.247 + yield reporter.collectMeasurements(); 1.248 + do_check_eq(provider.collectDailyCount, 1); // Too soon. 1.249 + 1.250 + reporter._lastDailyDate = now.getTime() - MILLISECONDS_PER_DAY - 1; 1.251 + yield reporter.collectMeasurements(); 1.252 + do_check_eq(provider.collectDailyCount, 2); 1.253 + 1.254 + reporter._lastDailyDate = null; 1.255 + yield reporter.collectMeasurements(); 1.256 + do_check_eq(provider.collectDailyCount, 3); 1.257 + } finally { 1.258 + reporter._shutdown(); 1.259 + } 1.260 +}); 1.261 + 1.262 +add_task(function test_remove_old_lastpayload() { 1.263 + let reporter = getHealthReporter("remove-old-lastpayload"); 1.264 + let lastPayloadPath = reporter._state._lastPayloadPath; 1.265 + let paths = [lastPayloadPath, lastPayloadPath + ".tmp"]; 1.266 + let createFiles = function () { 1.267 + return Task.spawn(function createFiles() { 1.268 + for (let path of paths) { 1.269 + yield OS.File.writeAtomic(path, "delete-me", {tmpPath: path + ".tmp"}); 1.270 + do_check_true(yield OS.File.exists(path)); 1.271 + } 1.272 + }); 1.273 + }; 1.274 + try { 1.275 + do_check_true(!reporter._state.removedOutdatedLastpayload); 1.276 + yield createFiles(); 1.277 + yield reporter.init(); 1.278 + for (let path of paths) { 1.279 + do_check_false(yield OS.File.exists(path)); 1.280 + } 1.281 + yield reporter._state.save(); 1.282 + reporter._shutdown(); 1.283 + 1.284 + let o = yield CommonUtils.readJSON(reporter._state._filename); 1.285 + do_check_true(o.removedOutdatedLastpayload); 1.286 + 1.287 + yield createFiles(); 1.288 + reporter = getHealthReporter("remove-old-lastpayload"); 1.289 + yield reporter.init(); 1.290 + for (let path of paths) { 1.291 + do_check_true(yield OS.File.exists(path)); 1.292 + } 1.293 + } finally { 1.294 + reporter._shutdown(); 1.295 + } 1.296 +}); 1.297 + 1.298 +add_task(function test_json_payload_simple() { 1.299 + let reporter = yield getReporter("json_payload_simple"); 1.300 + 1.301 + let clientID = reporter._state.clientID; 1.302 + do_check_neq(clientID, null); 1.303 + 1.304 + try { 1.305 + let now = new Date(); 1.306 + let payload = yield reporter.getJSONPayload(); 1.307 + do_check_eq(typeof payload, "string"); 1.308 + let original = JSON.parse(payload); 1.309 + 1.310 + do_check_eq(original.version, 2); 1.311 + do_check_eq(original.thisPingDate, reporter._formatDate(now)); 1.312 + do_check_eq(original.clientID, clientID); 1.313 + do_check_eq(original.clientIDVersion, reporter._state.clientIDVersion); 1.314 + do_check_eq(original.clientIDVersion, 1); 1.315 + do_check_eq(Object.keys(original.data.last).length, 0); 1.316 + do_check_eq(Object.keys(original.data.days).length, 0); 1.317 + do_check_false("notInitialized" in original); 1.318 + 1.319 + yield reporter._state.setLastPingDate( 1.320 + new Date(now.getTime() - 24 * 60 * 60 * 1000 - 10)); 1.321 + 1.322 + original = JSON.parse(yield reporter.getJSONPayload()); 1.323 + do_check_eq(original.lastPingDate, reporter._formatDate(reporter.lastPingDate)); 1.324 + do_check_eq(original.clientID, clientID); 1.325 + 1.326 + // This could fail if we cross UTC day boundaries at the exact instance the 1.327 + // test is executed. Let's tempt fate. 1.328 + do_check_eq(original.thisPingDate, reporter._formatDate(now)); 1.329 + 1.330 + payload = yield reporter.getJSONPayload(true); 1.331 + do_check_eq(typeof payload, "object"); 1.332 + } finally { 1.333 + reporter._shutdown(); 1.334 + } 1.335 +}); 1.336 + 1.337 +add_task(function test_json_payload_dummy_provider() { 1.338 + let reporter = yield getReporter("json_payload_dummy_provider"); 1.339 + 1.340 + try { 1.341 + yield reporter._providerManager.registerProvider(new DummyProvider()); 1.342 + yield reporter.collectMeasurements(); 1.343 + let payload = yield reporter.getJSONPayload(); 1.344 + print(payload); 1.345 + let o = JSON.parse(payload); 1.346 + 1.347 + let name = "DummyProvider.DummyMeasurement"; 1.348 + do_check_eq(Object.keys(o.data.last).length, 1); 1.349 + do_check_true(name in o.data.last); 1.350 + do_check_eq(o.data.last[name]._v, 1); 1.351 + } finally { 1.352 + reporter._shutdown(); 1.353 + } 1.354 +}); 1.355 + 1.356 +add_task(function test_collect_and_obtain_json_payload() { 1.357 + let reporter = yield getReporter("collect_and_obtain_json_payload"); 1.358 + 1.359 + try { 1.360 + yield reporter._providerManager.registerProvider(new DummyProvider()); 1.361 + let payload = yield reporter.collectAndObtainJSONPayload(); 1.362 + do_check_eq(typeof payload, "string"); 1.363 + 1.364 + let o = JSON.parse(payload); 1.365 + do_check_true("DummyProvider.DummyMeasurement" in o.data.last); 1.366 + 1.367 + payload = yield reporter.collectAndObtainJSONPayload(true); 1.368 + do_check_eq(typeof payload, "object"); 1.369 + } finally { 1.370 + reporter._shutdown(); 1.371 + } 1.372 +}); 1.373 + 1.374 +// Ensure constant-only providers make their way into the JSON payload. 1.375 +add_task(function test_constant_only_providers_in_json_payload() { 1.376 + const category = "healthreporter-constant-only-in-payload"; 1.377 + 1.378 + let cm = Cc["@mozilla.org/categorymanager;1"] 1.379 + .getService(Ci.nsICategoryManager); 1.380 + cm.addCategoryEntry(category, "DummyProvider", 1.381 + "resource://testing-common/services/metrics/mocks.jsm", 1.382 + false, true); 1.383 + cm.addCategoryEntry(category, "DummyConstantProvider", 1.384 + "resource://testing-common/services/metrics/mocks.jsm", 1.385 + false, true); 1.386 + 1.387 + let reporter = yield getReporter("constant_only_providers_in_json_payload"); 1.388 + try { 1.389 + let initCount = reporter._providerManager.providers.length; 1.390 + yield reporter._providerManager.registerProvidersFromCategoryManager(category); 1.391 + 1.392 + let payload = yield reporter.collectAndObtainJSONPayload(); 1.393 + let o = JSON.parse(payload); 1.394 + do_check_true("DummyProvider.DummyMeasurement" in o.data.last); 1.395 + do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); 1.396 + 1.397 + let providers = reporter._providerManager.providers; 1.398 + do_check_eq(providers.length, initCount + 1); 1.399 + 1.400 + // Do it again for good measure. 1.401 + payload = yield reporter.collectAndObtainJSONPayload(); 1.402 + o = JSON.parse(payload); 1.403 + do_check_true("DummyProvider.DummyMeasurement" in o.data.last); 1.404 + do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); 1.405 + 1.406 + providers = reporter._providerManager.providers; 1.407 + do_check_eq(providers.length, initCount + 1); 1.408 + 1.409 + // Ensure throwing getJSONPayload is handled properly. 1.410 + Object.defineProperty(reporter, "_getJSONPayload", { 1.411 + value: function () { 1.412 + throw new Error("Silly error."); 1.413 + }, 1.414 + }); 1.415 + 1.416 + let deferred = Promise.defer(); 1.417 + 1.418 + reporter.collectAndObtainJSONPayload().then(do_throw, function onError() { 1.419 + providers = reporter._providerManager.providers; 1.420 + do_check_eq(providers.length, initCount + 1); 1.421 + deferred.resolve(); 1.422 + }); 1.423 + 1.424 + yield deferred.promise; 1.425 + } finally { 1.426 + reporter._shutdown(); 1.427 + } 1.428 +}); 1.429 + 1.430 +add_task(function test_json_payload_multiple_days() { 1.431 + let reporter = yield getReporter("json_payload_multiple_days"); 1.432 + 1.433 + try { 1.434 + let provider = new DummyProvider(); 1.435 + yield reporter._providerManager.registerProvider(provider); 1.436 + 1.437 + let now = new Date(); 1.438 + 1.439 + let m = provider.getMeasurement("DummyMeasurement", 1); 1.440 + for (let i = 0; i < 200; i++) { 1.441 + let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); 1.442 + yield m.incrementDailyCounter("daily-counter", date); 1.443 + } 1.444 + 1.445 + // This test could fail if we cross a UTC day boundary when running. So, 1.446 + // we ensure this doesn't occur. 1.447 + Object.defineProperty(reporter, "_now", { 1.448 + value: function () { 1.449 + return now; 1.450 + }, 1.451 + }); 1.452 + 1.453 + let payload = yield reporter.getJSONPayload(); 1.454 + print(payload); 1.455 + let o = JSON.parse(payload); 1.456 + 1.457 + do_check_eq(Object.keys(o.data.days).length, 180); 1.458 + let today = reporter._formatDate(now); 1.459 + do_check_true(today in o.data.days); 1.460 + } finally { 1.461 + reporter._shutdown(); 1.462 + } 1.463 +}); 1.464 + 1.465 +add_task(function test_json_payload_newer_version_overwrites() { 1.466 + let reporter = yield getReporter("json_payload_newer_version_overwrites"); 1.467 + 1.468 + try { 1.469 + let now = new Date(); 1.470 + // Instead of hacking up the internals to ensure consistent order in Map 1.471 + // iteration (which would be difficult), we instead opt to generate a lot 1.472 + // of measurements of different versions and verify their iterable order 1.473 + // is not increasing. 1.474 + let versions = [1, 6, 3, 9, 2, 3, 7, 4, 10, 8]; 1.475 + let protos = []; 1.476 + for (let version of versions) { 1.477 + let m = function () { 1.478 + Metrics.Measurement.call(this); 1.479 + }; 1.480 + m.prototype = { 1.481 + __proto__: DummyMeasurement.prototype, 1.482 + name: "DummyMeasurement", 1.483 + version: version, 1.484 + }; 1.485 + 1.486 + protos.push(m); 1.487 + } 1.488 + 1.489 + let ctor = function () { 1.490 + Metrics.Provider.call(this); 1.491 + }; 1.492 + ctor.prototype = { 1.493 + __proto__: DummyProvider.prototype, 1.494 + 1.495 + name: "MultiMeasurementProvider", 1.496 + measurementTypes: protos, 1.497 + }; 1.498 + 1.499 + let provider = new ctor(); 1.500 + 1.501 + yield reporter._providerManager.registerProvider(provider); 1.502 + 1.503 + let haveUnordered = false; 1.504 + let last = -1; 1.505 + let highestVersion = -1; 1.506 + for (let [key, measurement] of provider.measurements) { 1.507 + yield measurement.setDailyLastNumeric("daily-last-numeric", 1.508 + measurement.version, now); 1.509 + yield measurement.setLastNumeric("last-numeric", 1.510 + measurement.version, now); 1.511 + 1.512 + if (measurement.version > highestVersion) { 1.513 + highestVersion = measurement.version; 1.514 + } 1.515 + 1.516 + if (measurement.version < last) { 1.517 + haveUnordered = true; 1.518 + } 1.519 + 1.520 + last = measurement.version; 1.521 + } 1.522 + 1.523 + // Ensure Map traversal isn't ordered. If this ever fails, then we'll need 1.524 + // to monkeypatch. 1.525 + do_check_true(haveUnordered); 1.526 + 1.527 + let payload = yield reporter.getJSONPayload(); 1.528 + let o = JSON.parse(payload); 1.529 + do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.last); 1.530 + do_check_eq(o.data.last["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion); 1.531 + 1.532 + let day = reporter._formatDate(now); 1.533 + do_check_true(day in o.data.days); 1.534 + do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.days[day]); 1.535 + do_check_eq(o.data.days[day]["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion); 1.536 + 1.537 + } finally { 1.538 + reporter._shutdown(); 1.539 + } 1.540 +}); 1.541 + 1.542 +add_task(function test_idle_daily() { 1.543 + let reporter = yield getReporter("idle_daily"); 1.544 + try { 1.545 + let provider = new DummyProvider(); 1.546 + yield reporter._providerManager.registerProvider(provider); 1.547 + 1.548 + let now = new Date(); 1.549 + let m = provider.getMeasurement("DummyMeasurement", 1); 1.550 + for (let i = 0; i < 200; i++) { 1.551 + let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); 1.552 + yield m.incrementDailyCounter("daily-counter", date); 1.553 + } 1.554 + 1.555 + let values = yield m.getValues(); 1.556 + do_check_eq(values.days.size, 200); 1.557 + 1.558 + Services.obs.notifyObservers(null, "idle-daily", null); 1.559 + 1.560 + values = yield m.getValues(); 1.561 + do_check_eq(values.days.size, 180); 1.562 + } finally { 1.563 + reporter._shutdown(); 1.564 + } 1.565 +}); 1.566 + 1.567 +add_task(function test_data_submission_transport_failure() { 1.568 + let reporter = yield getReporter("data_submission_transport_failure"); 1.569 + try { 1.570 + reporter.serverURI = DUMMY_URI; 1.571 + reporter.serverNamespace = "test00"; 1.572 + 1.573 + let deferred = Promise.defer(); 1.574 + let request = new DataSubmissionRequest(deferred, new Date(Date.now + 30000)); 1.575 + reporter.requestDataUpload(request); 1.576 + 1.577 + yield deferred.promise; 1.578 + do_check_eq(request.state, request.SUBMISSION_FAILURE_SOFT); 1.579 + 1.580 + let data = yield getHealthReportProviderValues(reporter, new Date()); 1.581 + do_check_eq(data.firstDocumentUploadAttempt, 1); 1.582 + do_check_eq(data.uploadTransportFailure, 1); 1.583 + do_check_eq(Object.keys(data).length, 3); 1.584 + } finally { 1.585 + reporter._shutdown(); 1.586 + } 1.587 +}); 1.588 + 1.589 +add_task(function test_data_submission_server_failure() { 1.590 + let [reporter, server] = yield getReporterAndServer("data_submission_server_failure"); 1.591 + try { 1.592 + Object.defineProperty(server, "_handleNamespaceSubmitPost", { 1.593 + value: function (ns, id, request, response) { 1.594 + throw HTTP_500; 1.595 + }, 1.596 + writable: true, 1.597 + }); 1.598 + 1.599 + let deferred = Promise.defer(); 1.600 + let now = new Date(); 1.601 + let request = new DataSubmissionRequest(deferred, now); 1.602 + reporter.requestDataUpload(request); 1.603 + yield deferred.promise; 1.604 + do_check_eq(request.state, request.SUBMISSION_FAILURE_HARD); 1.605 + 1.606 + let data = yield getHealthReportProviderValues(reporter, now); 1.607 + do_check_eq(data.firstDocumentUploadAttempt, 1); 1.608 + do_check_eq(data.uploadServerFailure, 1); 1.609 + do_check_eq(Object.keys(data).length, 3); 1.610 + } finally { 1.611 + yield shutdownServer(server); 1.612 + reporter._shutdown(); 1.613 + } 1.614 +}); 1.615 + 1.616 +add_task(function test_data_submission_success() { 1.617 + let [reporter, server] = yield getReporterAndServer("data_submission_success"); 1.618 + try { 1.619 + yield reporter._providerManager.registerProviderFromType(DummyProvider); 1.620 + yield reporter._providerManager.registerProviderFromType(DummyConstantProvider); 1.621 + 1.622 + do_check_eq(reporter.lastPingDate.getTime(), 0); 1.623 + do_check_false(reporter.haveRemoteData()); 1.624 + 1.625 + let deferred = Promise.defer(); 1.626 + 1.627 + let now = new Date(); 1.628 + let request = new DataSubmissionRequest(deferred, now); 1.629 + reporter._state.addRemoteID("foo"); 1.630 + reporter.requestDataUpload(request); 1.631 + yield deferred.promise; 1.632 + do_check_eq(request.state, request.SUBMISSION_SUCCESS); 1.633 + do_check_true(reporter.lastPingDate.getTime() > 0); 1.634 + do_check_true(reporter.haveRemoteData()); 1.635 + for (let remoteID of reporter._state.remoteIDs) { 1.636 + do_check_neq(remoteID, "foo"); 1.637 + } 1.638 + 1.639 + // Ensure data from providers made it to payload. 1.640 + let o = yield reporter.getJSONPayload(true); 1.641 + do_check_true("DummyProvider.DummyMeasurement" in o.data.last); 1.642 + do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); 1.643 + 1.644 + let data = yield getHealthReportProviderValues(reporter, now); 1.645 + do_check_eq(data.continuationUploadAttempt, 1); 1.646 + do_check_eq(data.uploadSuccess, 1); 1.647 + do_check_eq(Object.keys(data).length, 3); 1.648 + 1.649 + let d = reporter.lastPingDate; 1.650 + let id = reporter.lastSubmitID; 1.651 + let clientID = reporter._state.clientID; 1.652 + 1.653 + reporter._shutdown(); 1.654 + 1.655 + // Ensure reloading state works. 1.656 + reporter = yield getReporter("data_submission_success"); 1.657 + do_check_eq(reporter.lastSubmitID, id); 1.658 + do_check_eq(reporter.lastPingDate.getTime(), d.getTime()); 1.659 + do_check_eq(reporter._state.clientID, clientID); 1.660 + 1.661 + reporter._shutdown(); 1.662 + } finally { 1.663 + yield shutdownServer(server); 1.664 + } 1.665 +}); 1.666 + 1.667 +add_task(function test_recurring_daily_pings() { 1.668 + let [reporter, server] = yield getReporterAndServer("recurring_daily_pings"); 1.669 + try { 1.670 + reporter._providerManager.registerProvider(new DummyProvider()); 1.671 + 1.672 + let policy = reporter._policy; 1.673 + 1.674 + defineNow(policy, policy._futureDate(-24 * 60 * 68 * 1000)); 1.675 + policy.recordUserAcceptance(); 1.676 + defineNow(policy, policy.nextDataSubmissionDate); 1.677 + let promise = policy.checkStateAndTrigger(); 1.678 + do_check_neq(promise, null); 1.679 + yield promise; 1.680 + 1.681 + let lastID = reporter.lastSubmitID; 1.682 + do_check_neq(lastID, null); 1.683 + do_check_true(server.hasDocument(reporter.serverNamespace, lastID)); 1.684 + 1.685 + // Skip forward to next scheduled submission time. 1.686 + defineNow(policy, policy.nextDataSubmissionDate); 1.687 + promise = policy.checkStateAndTrigger(); 1.688 + do_check_neq(promise, null); 1.689 + yield promise; 1.690 + do_check_neq(reporter.lastSubmitID, lastID); 1.691 + do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); 1.692 + do_check_false(server.hasDocument(reporter.serverNamespace, lastID)); 1.693 + 1.694 + // now() on the health reporter instance wasn't munged. So, we should see 1.695 + // both requests attributed to the same day. 1.696 + let data = yield getHealthReportProviderValues(reporter, new Date()); 1.697 + do_check_eq(data.firstDocumentUploadAttempt, 1); 1.698 + do_check_eq(data.continuationUploadAttempt, 1); 1.699 + do_check_eq(data.uploadSuccess, 2); 1.700 + do_check_eq(Object.keys(data).length, 4); 1.701 + } finally { 1.702 + reporter._shutdown(); 1.703 + yield shutdownServer(server); 1.704 + } 1.705 +}); 1.706 + 1.707 +add_task(function test_request_remote_data_deletion() { 1.708 + let [reporter, server] = yield getReporterAndServer("request_remote_data_deletion"); 1.709 + 1.710 + try { 1.711 + let policy = reporter._policy; 1.712 + defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); 1.713 + policy.recordUserAcceptance(); 1.714 + defineNow(policy, policy.nextDataSubmissionDate); 1.715 + yield policy.checkStateAndTrigger(); 1.716 + let id = reporter.lastSubmitID; 1.717 + do_check_neq(id, null); 1.718 + do_check_true(server.hasDocument(reporter.serverNamespace, id)); 1.719 + 1.720 + let clientID = reporter._state.clientID; 1.721 + do_check_neq(clientID, null); 1.722 + 1.723 + defineNow(policy, policy._futureDate(10 * 1000)); 1.724 + 1.725 + let promise = reporter.requestDeleteRemoteData(); 1.726 + do_check_neq(promise, null); 1.727 + yield promise; 1.728 + do_check_null(reporter.lastSubmitID); 1.729 + do_check_false(reporter.haveRemoteData()); 1.730 + do_check_false(server.hasDocument(reporter.serverNamespace, id)); 1.731 + 1.732 + // Client ID should be updated. 1.733 + do_check_neq(reporter._state.clientID, null); 1.734 + do_check_neq(reporter._state.clientID, clientID); 1.735 + do_check_eq(reporter._state.clientIDVersion, 1); 1.736 + 1.737 + // And it should be persisted to disk. 1.738 + let o = yield CommonUtils.readJSON(reporter._state._filename); 1.739 + do_check_eq(o.clientID, reporter._state.clientID); 1.740 + do_check_eq(o.clientIDVersion, 1); 1.741 + } finally { 1.742 + reporter._shutdown(); 1.743 + yield shutdownServer(server); 1.744 + } 1.745 +}); 1.746 + 1.747 +add_task(function test_multiple_simultaneous_uploads() { 1.748 + let [reporter, server] = yield getReporterAndServer("multiple_simultaneous_uploads"); 1.749 + 1.750 + try { 1.751 + let d1 = Promise.defer(); 1.752 + let d2 = Promise.defer(); 1.753 + let t1 = new Date(Date.now() - 1000); 1.754 + let t2 = new Date(t1.getTime() + 500); 1.755 + let r1 = new DataSubmissionRequest(d1, t1); 1.756 + let r2 = new DataSubmissionRequest(d2, t2); 1.757 + 1.758 + let getPayloadDeferred = Promise.defer(); 1.759 + 1.760 + Object.defineProperty(reporter, "getJSONPayload", { 1.761 + configurable: true, 1.762 + value: () => { 1.763 + getPayloadDeferred.resolve(); 1.764 + delete reporter["getJSONPayload"]; 1.765 + return reporter.getJSONPayload(); 1.766 + }, 1.767 + }); 1.768 + 1.769 + let p1 = reporter.requestDataUpload(r1); 1.770 + yield getPayloadDeferred.promise; 1.771 + do_check_true(reporter._uploadInProgress); 1.772 + let p2 = reporter.requestDataUpload(r2); 1.773 + 1.774 + yield p1; 1.775 + yield p2; 1.776 + 1.777 + do_check_eq(r1.state, r1.SUBMISSION_SUCCESS); 1.778 + do_check_eq(r2.state, r2.UPLOAD_IN_PROGRESS); 1.779 + 1.780 + // They should both be resolved already. 1.781 + yield d1; 1.782 + yield d2; 1.783 + 1.784 + let data = yield getHealthReportProviderValues(reporter, t1); 1.785 + do_check_eq(data.firstDocumentUploadAttempt, 1); 1.786 + do_check_false("continuationUploadAttempt" in data); 1.787 + do_check_eq(data.uploadSuccess, 1); 1.788 + do_check_eq(data.uploadAlreadyInProgress, 1); 1.789 + } finally { 1.790 + reporter._shutdown(); 1.791 + yield shutdownServer(server); 1.792 + } 1.793 +}); 1.794 + 1.795 +add_task(function test_policy_accept_reject() { 1.796 + let [reporter, server] = yield getReporterAndServer("policy_accept_reject"); 1.797 + 1.798 + try { 1.799 + let policy = reporter._policy; 1.800 + 1.801 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.802 + do_check_false(reporter.willUploadData); 1.803 + 1.804 + policy.recordUserAcceptance(); 1.805 + do_check_true(policy.dataSubmissionPolicyAccepted); 1.806 + do_check_true(reporter.willUploadData); 1.807 + 1.808 + policy.recordUserRejection(); 1.809 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.810 + do_check_false(reporter.willUploadData); 1.811 + } finally { 1.812 + reporter._shutdown(); 1.813 + yield shutdownServer(server); 1.814 + } 1.815 +}); 1.816 + 1.817 +add_task(function test_error_message_scrubbing() { 1.818 + let reporter = yield getReporter("error_message_scrubbing"); 1.819 + 1.820 + try { 1.821 + let profile = Services.dirsvc.get("ProfD", Ci.nsIFile).path; 1.822 + reporter._recordError("Foo " + profile); 1.823 + 1.824 + do_check_eq(reporter._errors.length, 1); 1.825 + do_check_eq(reporter._errors[0], "Foo <ProfilePath>"); 1.826 + 1.827 + reporter._errors = []; 1.828 + 1.829 + let appdata = Services.dirsvc.get("UAppData", Ci.nsIFile); 1.830 + let uri = Services.io.newFileURI(appdata); 1.831 + 1.832 + reporter._recordError("Foo " + uri.spec); 1.833 + do_check_eq(reporter._errors[0], "Foo <AppDataURI>"); 1.834 + } finally { 1.835 + reporter._shutdown(); 1.836 + } 1.837 +}); 1.838 + 1.839 +add_task(function test_basic_appinfo() { 1.840 + function verify(d) { 1.841 + do_check_eq(d["_v"], 1); 1.842 + do_check_eq(d._v, 1); 1.843 + do_check_eq(d.vendor, "Mozilla"); 1.844 + do_check_eq(d.name, "xpcshell"); 1.845 + do_check_eq(d.id, "xpcshell@tests.mozilla.org"); 1.846 + do_check_eq(d.version, "1"); 1.847 + do_check_eq(d.appBuildID, "20121107"); 1.848 + do_check_eq(d.platformVersion, "p-ver"); 1.849 + do_check_eq(d.platformBuildID, "20121106"); 1.850 + do_check_eq(d.os, "XPCShell"); 1.851 + do_check_eq(d.xpcomabi, "noarch-spidermonkey"); 1.852 + do_check_true("updateChannel" in d); 1.853 + } 1.854 + let reporter = yield getReporter("basic_appinfo"); 1.855 + try { 1.856 + verify(reporter.obtainAppInfo()); 1.857 + let payload = yield reporter.collectAndObtainJSONPayload(true); 1.858 + do_check_eq(payload["version"], 2); 1.859 + verify(payload["geckoAppInfo"]); 1.860 + } finally { 1.861 + reporter._shutdown(); 1.862 + } 1.863 +}); 1.864 + 1.865 +// Ensure collection occurs if upload is disabled. 1.866 +add_task(function test_collect_when_upload_disabled() { 1.867 + let reporter = getHealthReporter("collect_when_upload_disabled"); 1.868 + reporter._policy.recordHealthReportUploadEnabled(false, "testing-collect"); 1.869 + do_check_false(reporter._policy.healthReportUploadEnabled); 1.870 + 1.871 + let name = "healthreport-testing-collect_when_upload_disabled-healthreport-lastDailyCollection"; 1.872 + let pref = "app.update.lastUpdateTime." + name; 1.873 + do_check_false(Services.prefs.prefHasUserValue(pref)); 1.874 + 1.875 + try { 1.876 + yield reporter.init(); 1.877 + do_check_true(Services.prefs.prefHasUserValue(pref)); 1.878 + 1.879 + // We would ideally ensure the timer fires and does the right thing. 1.880 + // However, testing the update timer manager is quite involved. 1.881 + } finally { 1.882 + reporter._shutdown(); 1.883 + } 1.884 +}); 1.885 + 1.886 +add_task(function test_failure_if_not_initialized() { 1.887 + let reporter = yield getReporter("failure_if_not_initialized"); 1.888 + reporter._shutdown(); 1.889 + 1.890 + let error = false; 1.891 + try { 1.892 + yield reporter.requestDataUpload(); 1.893 + } catch (ex) { 1.894 + error = true; 1.895 + do_check_true(ex.message.contains("Not initialized.")); 1.896 + } finally { 1.897 + do_check_true(error); 1.898 + error = false; 1.899 + } 1.900 + 1.901 + try { 1.902 + yield reporter.collectMeasurements(); 1.903 + } catch (ex) { 1.904 + error = true; 1.905 + do_check_true(ex.message.contains("Not initialized.")); 1.906 + } finally { 1.907 + do_check_true(error); 1.908 + error = false; 1.909 + } 1.910 + 1.911 + // getJSONPayload always works (to facilitate error upload). 1.912 + yield reporter.getJSONPayload(); 1.913 +}); 1.914 + 1.915 +add_task(function test_upload_on_init_failure() { 1.916 + let server = new BagheeraServer(); 1.917 + server.start(); 1.918 + let reporter = yield getHealthReporter("upload_on_init_failure", server.serverURI, true); 1.919 + server.createNamespace(reporter.serverNamespace); 1.920 + 1.921 + reporter.onInitializeProviderManagerFinished = function () { 1.922 + throw new Error("Fake error during provider manager initialization."); 1.923 + }; 1.924 + 1.925 + let deferred = Promise.defer(); 1.926 + 1.927 + let oldOnResult = reporter._onBagheeraResult; 1.928 + Object.defineProperty(reporter, "_onBagheeraResult", { 1.929 + value: function (request, isDelete, date, result) { 1.930 + do_check_false(isDelete); 1.931 + do_check_true(result.transportSuccess); 1.932 + do_check_true(result.serverSuccess); 1.933 + 1.934 + oldOnResult.call(reporter, request, isDelete, new Date(), result); 1.935 + deferred.resolve(); 1.936 + }, 1.937 + }); 1.938 + 1.939 + reporter._policy.recordUserAcceptance(); 1.940 + let error = false; 1.941 + try { 1.942 + yield reporter.init(); 1.943 + } catch (ex) { 1.944 + error = true; 1.945 + } finally { 1.946 + do_check_true(error); 1.947 + } 1.948 + 1.949 + // At this point the emergency upload should have been initiated. We 1.950 + // wait for our monkeypatched response handler to signal request 1.951 + // completion. 1.952 + yield deferred.promise; 1.953 + 1.954 + do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); 1.955 + let doc = server.getDocument(reporter.serverNamespace, reporter.lastSubmitID); 1.956 + do_check_true("notInitialized" in doc); 1.957 + do_check_eq(doc.notInitialized, 1); 1.958 + do_check_true("errors" in doc); 1.959 + do_check_eq(doc.errors.length, 1); 1.960 + do_check_true(doc.errors[0].contains("Fake error during provider manager initialization")); 1.961 + 1.962 + reporter._shutdown(); 1.963 + yield shutdownServer(server); 1.964 +}); 1.965 + 1.966 +add_task(function test_state_prefs_conversion_simple() { 1.967 + let reporter = getHealthReporter("state_prefs_conversion"); 1.968 + let prefs = reporter._prefs; 1.969 + 1.970 + let lastSubmit = new Date(); 1.971 + prefs.set("lastSubmitID", "lastID"); 1.972 + CommonUtils.setDatePref(prefs, "lastPingTime", lastSubmit); 1.973 + 1.974 + try { 1.975 + yield reporter.init(); 1.976 + 1.977 + do_check_eq(reporter._state.lastSubmitID, "lastID"); 1.978 + do_check_eq(reporter._state.remoteIDs.length, 1); 1.979 + do_check_eq(reporter._state.lastPingDate.getTime(), lastSubmit.getTime()); 1.980 + do_check_eq(reporter._state.lastPingDate.getTime(), reporter.lastPingDate.getTime()); 1.981 + do_check_eq(reporter._state.lastSubmitID, reporter.lastSubmitID); 1.982 + do_check_true(reporter.haveRemoteData()); 1.983 + 1.984 + // User set preferences should have been wiped out. 1.985 + do_check_false(prefs.isSet("lastSubmitID")); 1.986 + do_check_false(prefs.isSet("lastPingTime")); 1.987 + } finally { 1.988 + reporter._shutdown(); 1.989 + } 1.990 +}); 1.991 + 1.992 +// If the saved JSON file does not contain an object, we should reset 1.993 +// automatically. 1.994 +add_task(function test_state_no_json_object() { 1.995 + let reporter = getHealthReporter("state_shared"); 1.996 + yield CommonUtils.writeJSON("hello", reporter._state._filename); 1.997 + 1.998 + try { 1.999 + yield reporter.init(); 1.1000 + 1.1001 + do_check_eq(reporter.lastPingDate.getTime(), 0); 1.1002 + do_check_null(reporter.lastSubmitID); 1.1003 + 1.1004 + let o = yield CommonUtils.readJSON(reporter._state._filename); 1.1005 + do_check_eq(typeof(o), "object"); 1.1006 + do_check_eq(o.v, 1); 1.1007 + do_check_eq(o.lastPingTime, 0); 1.1008 + do_check_eq(o.remoteIDs.length, 0); 1.1009 + } finally { 1.1010 + reporter._shutdown(); 1.1011 + } 1.1012 +}); 1.1013 + 1.1014 +// If we encounter a future version, we reset state to the current version. 1.1015 +add_task(function test_state_future_version() { 1.1016 + let reporter = getHealthReporter("state_shared"); 1.1017 + yield CommonUtils.writeJSON({v: 2, remoteIDs: ["foo"], lastPingTime: 2412}, 1.1018 + reporter._state._filename); 1.1019 + try { 1.1020 + yield reporter.init(); 1.1021 + 1.1022 + do_check_eq(reporter.lastPingDate.getTime(), 0); 1.1023 + do_check_null(reporter.lastSubmitID); 1.1024 + 1.1025 + // While the object is updated, we don't save the file. 1.1026 + let o = yield CommonUtils.readJSON(reporter._state._filename); 1.1027 + do_check_eq(o.v, 2); 1.1028 + do_check_eq(o.lastPingTime, 2412); 1.1029 + do_check_eq(o.remoteIDs.length, 1); 1.1030 + } finally { 1.1031 + reporter._shutdown(); 1.1032 + } 1.1033 +}); 1.1034 + 1.1035 +// Test recovery if the state file contains invalid JSON. 1.1036 +add_task(function test_state_invalid_json() { 1.1037 + let reporter = getHealthReporter("state_shared"); 1.1038 + 1.1039 + let encoder = new TextEncoder(); 1.1040 + let arr = encoder.encode("{foo: bad value, 'bad': as2,}"); 1.1041 + let path = reporter._state._filename; 1.1042 + yield OS.File.writeAtomic(path, arr, {tmpPath: path + ".tmp"}); 1.1043 + 1.1044 + try { 1.1045 + yield reporter.init(); 1.1046 + 1.1047 + do_check_eq(reporter.lastPingDate.getTime(), 0); 1.1048 + do_check_null(reporter.lastSubmitID); 1.1049 + } finally { 1.1050 + reporter._shutdown(); 1.1051 + } 1.1052 +}); 1.1053 + 1.1054 +add_task(function test_state_multiple_remote_ids() { 1.1055 + let [reporter, server] = yield getReporterAndServer("state_multiple_remote_ids"); 1.1056 + let documents = [ 1.1057 + [reporter.serverNamespace, "one", "{v:1}"], 1.1058 + [reporter.serverNamespace, "two", "{v:2}"], 1.1059 + ]; 1.1060 + let now = new Date(Date.now() - 5000); 1.1061 + 1.1062 + try { 1.1063 + for (let [ns, id, payload] of documents) { 1.1064 + server.setDocument(ns, id, payload); 1.1065 + do_check_true(server.hasDocument(ns, id)); 1.1066 + yield reporter._state.addRemoteID(id); 1.1067 + do_check_eq(reporter._state.remoteIDs.indexOf(id), reporter._state.remoteIDs.length - 1); 1.1068 + } 1.1069 + yield reporter._state.setLastPingDate(now); 1.1070 + do_check_eq(reporter._state.remoteIDs.length, 2); 1.1071 + do_check_eq(reporter.lastSubmitID, documents[0][1]); 1.1072 + 1.1073 + let deferred = Promise.defer(); 1.1074 + let request = new DataSubmissionRequest(deferred, now); 1.1075 + reporter.requestDataUpload(request); 1.1076 + yield deferred.promise; 1.1077 + 1.1078 + do_check_eq(reporter._state.remoteIDs.length, 1); 1.1079 + for (let [,id,] of documents) { 1.1080 + do_check_eq(reporter._state.remoteIDs.indexOf(id), -1); 1.1081 + do_check_false(server.hasDocument(reporter.serverNamespace, id)); 1.1082 + } 1.1083 + do_check_true(reporter.lastPingDate.getTime() > now.getTime()); 1.1084 + 1.1085 + let o = yield CommonUtils.readJSON(reporter._state._filename); 1.1086 + do_check_eq(o.remoteIDs.length, 1); 1.1087 + do_check_eq(o.remoteIDs[0], reporter._state.remoteIDs[0]); 1.1088 + do_check_eq(o.lastPingTime, reporter.lastPingDate.getTime()); 1.1089 + } finally { 1.1090 + yield shutdownServer(server); 1.1091 + reporter._shutdown(); 1.1092 + } 1.1093 +}); 1.1094 + 1.1095 +// If we have a state file then downgrade to prefs, the prefs should be 1.1096 +// reimported and should supplement existing state. 1.1097 +add_task(function test_state_downgrade_upgrade() { 1.1098 + let reporter = getHealthReporter("state_shared"); 1.1099 + 1.1100 + let now = new Date(); 1.1101 + 1.1102 + yield CommonUtils.writeJSON({v: 1, remoteIDs: ["id1", "id2"], lastPingTime: now.getTime()}, 1.1103 + reporter._state._filename); 1.1104 + 1.1105 + let prefs = reporter._prefs; 1.1106 + prefs.set("lastSubmitID", "prefID"); 1.1107 + prefs.set("lastPingTime", "" + (now.getTime() + 1000)); 1.1108 + 1.1109 + try { 1.1110 + yield reporter.init(); 1.1111 + 1.1112 + do_check_eq(reporter.lastSubmitID, "id1"); 1.1113 + do_check_eq(reporter._state.remoteIDs.length, 3); 1.1114 + do_check_eq(reporter._state.remoteIDs[2], "prefID"); 1.1115 + do_check_eq(reporter.lastPingDate.getTime(), now.getTime() + 1000); 1.1116 + do_check_false(prefs.isSet("lastSubmitID")); 1.1117 + do_check_false(prefs.isSet("lastPingTime")); 1.1118 + 1.1119 + let o = yield CommonUtils.readJSON(reporter._state._filename); 1.1120 + do_check_eq(o.remoteIDs.length, 3); 1.1121 + do_check_eq(o.lastPingTime, now.getTime() + 1000); 1.1122 + } finally { 1.1123 + reporter._shutdown(); 1.1124 + } 1.1125 +}); 1.1126 + 1.1127 +// Missing client ID in state should be created on state load. 1.1128 +add_task(function* test_state_create_client_id() { 1.1129 + let reporter = getHealthReporter("state_create_client_id"); 1.1130 + 1.1131 + yield CommonUtils.writeJSON({ 1.1132 + v: 1, 1.1133 + remoteIDs: ["id1", "id2"], 1.1134 + lastPingTime: Date.now(), 1.1135 + removeOutdatedLastPayload: true, 1.1136 + }, reporter._state._filename); 1.1137 + 1.1138 + try { 1.1139 + yield reporter.init(); 1.1140 + 1.1141 + do_check_eq(reporter.lastSubmitID, "id1"); 1.1142 + do_check_neq(reporter._state.clientID, null); 1.1143 + do_check_eq(reporter._state.clientID.length, 36); 1.1144 + do_check_eq(reporter._state.clientIDVersion, 1); 1.1145 + 1.1146 + let clientID = reporter._state.clientID; 1.1147 + 1.1148 + // The client ID should be persisted as soon as it is created. 1.1149 + reporter._shutdown(); 1.1150 + 1.1151 + reporter = getHealthReporter("state_create_client_id"); 1.1152 + yield reporter.init(); 1.1153 + do_check_eq(reporter._state.clientID, clientID); 1.1154 + } finally { 1.1155 + reporter._shutdown(); 1.1156 + } 1.1157 +}); 1.1158 + 1.1159 +// Invalid stored client ID is reset automatically. 1.1160 +add_task(function* test_empty_client_id() { 1.1161 + let reporter = getHealthReporter("state_empty_client_id"); 1.1162 + 1.1163 + yield CommonUtils.writeJSON({ 1.1164 + v: 1, 1.1165 + clientID: "", 1.1166 + remoteIDs: ["id1", "id2"], 1.1167 + lastPingTime: Date.now(), 1.1168 + removeOutdatedLastPayload: true, 1.1169 + }, reporter._state._filename); 1.1170 + 1.1171 + try { 1.1172 + yield reporter.init(); 1.1173 + 1.1174 + do_check_neq(reporter._state.clientID, null); 1.1175 + do_check_eq(reporter._state.clientID.length, 36); 1.1176 + } finally { 1.1177 + reporter._shutdown(); 1.1178 + } 1.1179 +}); 1.1180 + 1.1181 +add_task(function* test_nonstring_client_id() { 1.1182 + let reporter = getHealthReporter("state_nonstring_client_id"); 1.1183 + 1.1184 + yield CommonUtils.writeJSON({ 1.1185 + v: 1, 1.1186 + clientID: 42, 1.1187 + remoteIDs: ["id1", "id2"], 1.1188 + lastPingTime: Date.now(), 1.1189 + remoteOutdatedLastPayload: true, 1.1190 + }, reporter._state._filename); 1.1191 + 1.1192 + try { 1.1193 + yield reporter.init(); 1.1194 + 1.1195 + do_check_neq(reporter._state.clientID, null); 1.1196 + do_check_eq(reporter._state.clientID.length, 36); 1.1197 + } finally { 1.1198 + reporter._shutdown(); 1.1199 + } 1.1200 +});