michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: "use strict"; michael@0: michael@0: const {classes: Cc, interfaces: Ci, utils: Cu} = Components; michael@0: michael@0: Cu.import("resource://services-common/observers.js"); michael@0: Cu.import("resource://services-common/utils.js"); michael@0: Cu.import("resource://gre/modules/Promise.jsm"); michael@0: Cu.import("resource://gre/modules/Metrics.jsm"); michael@0: Cu.import("resource://gre/modules/osfile.jsm"); michael@0: Cu.import("resource://gre/modules/Preferences.jsm"); michael@0: let bsp = Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm"); michael@0: Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); michael@0: Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/Task.jsm"); michael@0: Cu.import("resource://testing-common/httpd.js"); michael@0: Cu.import("resource://testing-common/services-common/bagheeraserver.js"); michael@0: Cu.import("resource://testing-common/services/metrics/mocks.jsm"); michael@0: Cu.import("resource://testing-common/services/healthreport/utils.jsm"); michael@0: Cu.import("resource://testing-common/AppData.jsm"); michael@0: michael@0: michael@0: const DUMMY_URI = "http://localhost:62013/"; michael@0: const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; michael@0: michael@0: const HealthReporterState = bsp.HealthReporterState; michael@0: michael@0: michael@0: function defineNow(policy, now) { michael@0: print("Adjusting fake system clock to " + now); michael@0: Object.defineProperty(policy, "now", { michael@0: value: function customNow() { michael@0: return now; michael@0: }, michael@0: writable: true, michael@0: }); michael@0: } michael@0: michael@0: function getReporter(name, uri, inspected) { michael@0: return Task.spawn(function init() { michael@0: let reporter = getHealthReporter(name, uri, inspected); michael@0: yield reporter.init(); michael@0: michael@0: yield reporter._providerManager.registerProviderFromType( michael@0: HealthReportProvider); michael@0: michael@0: throw new Task.Result(reporter); michael@0: }); michael@0: } michael@0: michael@0: function getReporterAndServer(name, namespace="test") { michael@0: return Task.spawn(function get() { michael@0: let server = new BagheeraServer(); michael@0: server.createNamespace(namespace); michael@0: server.start(); michael@0: michael@0: let reporter = yield getReporter(name, server.serverURI); michael@0: reporter.serverNamespace = namespace; michael@0: michael@0: throw new Task.Result([reporter, server]); michael@0: }); michael@0: } michael@0: michael@0: function shutdownServer(server) { michael@0: let deferred = Promise.defer(); michael@0: server.stop(deferred.resolve.bind(deferred)); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: function getHealthReportProviderValues(reporter, day=null) { michael@0: return Task.spawn(function getValues() { michael@0: let p = reporter.getProvider("org.mozilla.healthreport"); michael@0: do_check_neq(p, null); michael@0: let m = p.getMeasurement("submissions", 2); michael@0: do_check_neq(m, null); michael@0: michael@0: let data = yield reporter._storage.getMeasurementValues(m.id); michael@0: if (!day) { michael@0: throw new Task.Result(data); michael@0: } michael@0: michael@0: do_check_true(data.days.hasDay(day)); michael@0: let serializer = m.serializer(m.SERIALIZE_JSON) michael@0: let json = serializer.daily(data.days.getDay(day)); michael@0: do_check_eq(json._v, 2); michael@0: michael@0: throw new Task.Result(json); michael@0: }); michael@0: } michael@0: michael@0: function run_test() { michael@0: run_next_test(); michael@0: } michael@0: michael@0: // run_test() needs to finish synchronously, so we do async init here. michael@0: add_task(function test_init() { michael@0: yield makeFakeAppDir(); michael@0: }); michael@0: michael@0: add_task(function test_constructor() { michael@0: let reporter = yield getReporter("constructor"); michael@0: michael@0: try { michael@0: do_check_eq(reporter.lastPingDate.getTime(), 0); michael@0: do_check_null(reporter.lastSubmitID); michael@0: do_check_eq(typeof(reporter._state), "object"); michael@0: do_check_eq(reporter._state.lastPingDate.getTime(), 0); michael@0: do_check_eq(reporter._state.remoteIDs.length, 0); michael@0: do_check_eq(reporter._state.clientIDVersion, 1); michael@0: do_check_neq(reporter._state.clientID, null); michael@0: michael@0: let failed = false; michael@0: try { michael@0: new HealthReporter("foo.bar"); michael@0: } catch (ex) { michael@0: failed = true; michael@0: do_check_true(ex.message.startsWith("Branch must end")); michael@0: } finally { michael@0: do_check_true(failed); michael@0: failed = false; michael@0: } michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_shutdown_normal() { michael@0: let reporter = yield getReporter("shutdown_normal"); michael@0: michael@0: // We can't send "quit-application" notification because the xpcshell runner michael@0: // will shut down! michael@0: reporter._initiateShutdown(); michael@0: reporter._waitForShutdown(); michael@0: }); michael@0: michael@0: add_task(function test_shutdown_storage_in_progress() { michael@0: let reporter = yield getHealthReporter("shutdown_storage_in_progress", DUMMY_URI, true); michael@0: michael@0: reporter.onStorageCreated = function () { michael@0: print("Faking shutdown during storage initialization."); michael@0: reporter._initiateShutdown(); michael@0: }; michael@0: michael@0: reporter.init(); michael@0: michael@0: reporter._waitForShutdown(); michael@0: do_check_eq(reporter.providerManagerShutdownCount, 0); michael@0: do_check_eq(reporter.storageCloseCount, 1); michael@0: }); michael@0: michael@0: // Ensure that a shutdown triggered while provider manager is initializing michael@0: // results in shutdown and storage closure. michael@0: add_task(function test_shutdown_provider_manager_in_progress() { michael@0: let reporter = yield getHealthReporter("shutdown_provider_manager_in_progress", michael@0: DUMMY_URI, true); michael@0: michael@0: reporter.onProviderManagerInitialized = function () { michael@0: print("Faking shutdown during provider manager initialization."); michael@0: reporter._initiateShutdown(); michael@0: }; michael@0: michael@0: reporter.init(); michael@0: michael@0: // This will hang if shutdown logic is busted. michael@0: reporter._waitForShutdown(); michael@0: do_check_eq(reporter.providerManagerShutdownCount, 1); michael@0: do_check_eq(reporter.storageCloseCount, 1); michael@0: }); michael@0: michael@0: // Simulates an error during provider manager initialization and verifies we shut down. michael@0: add_task(function test_shutdown_when_provider_manager_errors() { michael@0: let reporter = yield getHealthReporter("shutdown_when_provider_manager_errors", michael@0: DUMMY_URI, true); michael@0: michael@0: reporter.onInitializeProviderManagerFinished = function () { michael@0: print("Throwing fake error."); michael@0: throw new Error("Fake error during provider manager initialization."); michael@0: }; michael@0: michael@0: reporter.init(); michael@0: michael@0: // This will hang if shutdown logic is busted. michael@0: reporter._waitForShutdown(); michael@0: do_check_eq(reporter.providerManagerShutdownCount, 1); michael@0: do_check_eq(reporter.storageCloseCount, 1); michael@0: }); michael@0: michael@0: // Pull-only providers are only initialized at collect time. michael@0: add_task(function test_pull_only_providers() { michael@0: const category = "healthreporter-constant-only"; michael@0: michael@0: let cm = Cc["@mozilla.org/categorymanager;1"] michael@0: .getService(Ci.nsICategoryManager); michael@0: cm.addCategoryEntry(category, "DummyProvider", michael@0: "resource://testing-common/services/metrics/mocks.jsm", michael@0: false, true); michael@0: cm.addCategoryEntry(category, "DummyConstantProvider", michael@0: "resource://testing-common/services/metrics/mocks.jsm", michael@0: false, true); michael@0: michael@0: let reporter = yield getReporter("constant_only_providers"); michael@0: try { michael@0: let initCount = reporter._providerManager.providers.length; michael@0: yield reporter._providerManager.registerProvidersFromCategoryManager(category); michael@0: do_check_eq(reporter._providerManager._providers.size, initCount + 1); michael@0: do_check_true(reporter._storage.hasProvider("DummyProvider")); michael@0: do_check_false(reporter._storage.hasProvider("DummyConstantProvider")); michael@0: do_check_neq(reporter.getProvider("DummyProvider"), null); michael@0: do_check_null(reporter.getProvider("DummyConstantProvider")); michael@0: michael@0: yield reporter.collectMeasurements(); michael@0: michael@0: do_check_eq(reporter._providerManager._providers.size, initCount + 1); michael@0: do_check_true(reporter._storage.hasProvider("DummyConstantProvider")); michael@0: michael@0: let mID = reporter._storage.measurementID("DummyConstantProvider", "DummyMeasurement", 1); michael@0: let values = yield reporter._storage.getMeasurementValues(mID); michael@0: do_check_true(values.singular.size > 0); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_collect_daily() { michael@0: let reporter = yield getReporter("collect_daily"); michael@0: michael@0: try { michael@0: let now = new Date(); michael@0: let provider = new DummyProvider(); michael@0: yield reporter._providerManager.registerProvider(provider); michael@0: yield reporter.collectMeasurements(); michael@0: michael@0: do_check_eq(provider.collectConstantCount, 1); michael@0: do_check_eq(provider.collectDailyCount, 1); michael@0: michael@0: yield reporter.collectMeasurements(); michael@0: do_check_eq(provider.collectConstantCount, 1); michael@0: do_check_eq(provider.collectDailyCount, 1); michael@0: michael@0: yield reporter.collectMeasurements(); michael@0: do_check_eq(provider.collectDailyCount, 1); // Too soon. michael@0: michael@0: reporter._lastDailyDate = now.getTime() - MILLISECONDS_PER_DAY - 1; michael@0: yield reporter.collectMeasurements(); michael@0: do_check_eq(provider.collectDailyCount, 2); michael@0: michael@0: reporter._lastDailyDate = null; michael@0: yield reporter.collectMeasurements(); michael@0: do_check_eq(provider.collectDailyCount, 3); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_remove_old_lastpayload() { michael@0: let reporter = getHealthReporter("remove-old-lastpayload"); michael@0: let lastPayloadPath = reporter._state._lastPayloadPath; michael@0: let paths = [lastPayloadPath, lastPayloadPath + ".tmp"]; michael@0: let createFiles = function () { michael@0: return Task.spawn(function createFiles() { michael@0: for (let path of paths) { michael@0: yield OS.File.writeAtomic(path, "delete-me", {tmpPath: path + ".tmp"}); michael@0: do_check_true(yield OS.File.exists(path)); michael@0: } michael@0: }); michael@0: }; michael@0: try { michael@0: do_check_true(!reporter._state.removedOutdatedLastpayload); michael@0: yield createFiles(); michael@0: yield reporter.init(); michael@0: for (let path of paths) { michael@0: do_check_false(yield OS.File.exists(path)); michael@0: } michael@0: yield reporter._state.save(); michael@0: reporter._shutdown(); michael@0: michael@0: let o = yield CommonUtils.readJSON(reporter._state._filename); michael@0: do_check_true(o.removedOutdatedLastpayload); michael@0: michael@0: yield createFiles(); michael@0: reporter = getHealthReporter("remove-old-lastpayload"); michael@0: yield reporter.init(); michael@0: for (let path of paths) { michael@0: do_check_true(yield OS.File.exists(path)); michael@0: } michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_json_payload_simple() { michael@0: let reporter = yield getReporter("json_payload_simple"); michael@0: michael@0: let clientID = reporter._state.clientID; michael@0: do_check_neq(clientID, null); michael@0: michael@0: try { michael@0: let now = new Date(); michael@0: let payload = yield reporter.getJSONPayload(); michael@0: do_check_eq(typeof payload, "string"); michael@0: let original = JSON.parse(payload); michael@0: michael@0: do_check_eq(original.version, 2); michael@0: do_check_eq(original.thisPingDate, reporter._formatDate(now)); michael@0: do_check_eq(original.clientID, clientID); michael@0: do_check_eq(original.clientIDVersion, reporter._state.clientIDVersion); michael@0: do_check_eq(original.clientIDVersion, 1); michael@0: do_check_eq(Object.keys(original.data.last).length, 0); michael@0: do_check_eq(Object.keys(original.data.days).length, 0); michael@0: do_check_false("notInitialized" in original); michael@0: michael@0: yield reporter._state.setLastPingDate( michael@0: new Date(now.getTime() - 24 * 60 * 60 * 1000 - 10)); michael@0: michael@0: original = JSON.parse(yield reporter.getJSONPayload()); michael@0: do_check_eq(original.lastPingDate, reporter._formatDate(reporter.lastPingDate)); michael@0: do_check_eq(original.clientID, clientID); michael@0: michael@0: // This could fail if we cross UTC day boundaries at the exact instance the michael@0: // test is executed. Let's tempt fate. michael@0: do_check_eq(original.thisPingDate, reporter._formatDate(now)); michael@0: michael@0: payload = yield reporter.getJSONPayload(true); michael@0: do_check_eq(typeof payload, "object"); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_json_payload_dummy_provider() { michael@0: let reporter = yield getReporter("json_payload_dummy_provider"); michael@0: michael@0: try { michael@0: yield reporter._providerManager.registerProvider(new DummyProvider()); michael@0: yield reporter.collectMeasurements(); michael@0: let payload = yield reporter.getJSONPayload(); michael@0: print(payload); michael@0: let o = JSON.parse(payload); michael@0: michael@0: let name = "DummyProvider.DummyMeasurement"; michael@0: do_check_eq(Object.keys(o.data.last).length, 1); michael@0: do_check_true(name in o.data.last); michael@0: do_check_eq(o.data.last[name]._v, 1); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_collect_and_obtain_json_payload() { michael@0: let reporter = yield getReporter("collect_and_obtain_json_payload"); michael@0: michael@0: try { michael@0: yield reporter._providerManager.registerProvider(new DummyProvider()); michael@0: let payload = yield reporter.collectAndObtainJSONPayload(); michael@0: do_check_eq(typeof payload, "string"); michael@0: michael@0: let o = JSON.parse(payload); michael@0: do_check_true("DummyProvider.DummyMeasurement" in o.data.last); michael@0: michael@0: payload = yield reporter.collectAndObtainJSONPayload(true); michael@0: do_check_eq(typeof payload, "object"); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // Ensure constant-only providers make their way into the JSON payload. michael@0: add_task(function test_constant_only_providers_in_json_payload() { michael@0: const category = "healthreporter-constant-only-in-payload"; michael@0: michael@0: let cm = Cc["@mozilla.org/categorymanager;1"] michael@0: .getService(Ci.nsICategoryManager); michael@0: cm.addCategoryEntry(category, "DummyProvider", michael@0: "resource://testing-common/services/metrics/mocks.jsm", michael@0: false, true); michael@0: cm.addCategoryEntry(category, "DummyConstantProvider", michael@0: "resource://testing-common/services/metrics/mocks.jsm", michael@0: false, true); michael@0: michael@0: let reporter = yield getReporter("constant_only_providers_in_json_payload"); michael@0: try { michael@0: let initCount = reporter._providerManager.providers.length; michael@0: yield reporter._providerManager.registerProvidersFromCategoryManager(category); michael@0: michael@0: let payload = yield reporter.collectAndObtainJSONPayload(); michael@0: let o = JSON.parse(payload); michael@0: do_check_true("DummyProvider.DummyMeasurement" in o.data.last); michael@0: do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); michael@0: michael@0: let providers = reporter._providerManager.providers; michael@0: do_check_eq(providers.length, initCount + 1); michael@0: michael@0: // Do it again for good measure. michael@0: payload = yield reporter.collectAndObtainJSONPayload(); michael@0: o = JSON.parse(payload); michael@0: do_check_true("DummyProvider.DummyMeasurement" in o.data.last); michael@0: do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); michael@0: michael@0: providers = reporter._providerManager.providers; michael@0: do_check_eq(providers.length, initCount + 1); michael@0: michael@0: // Ensure throwing getJSONPayload is handled properly. michael@0: Object.defineProperty(reporter, "_getJSONPayload", { michael@0: value: function () { michael@0: throw new Error("Silly error."); michael@0: }, michael@0: }); michael@0: michael@0: let deferred = Promise.defer(); michael@0: michael@0: reporter.collectAndObtainJSONPayload().then(do_throw, function onError() { michael@0: providers = reporter._providerManager.providers; michael@0: do_check_eq(providers.length, initCount + 1); michael@0: deferred.resolve(); michael@0: }); michael@0: michael@0: yield deferred.promise; michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_json_payload_multiple_days() { michael@0: let reporter = yield getReporter("json_payload_multiple_days"); michael@0: michael@0: try { michael@0: let provider = new DummyProvider(); michael@0: yield reporter._providerManager.registerProvider(provider); michael@0: michael@0: let now = new Date(); michael@0: michael@0: let m = provider.getMeasurement("DummyMeasurement", 1); michael@0: for (let i = 0; i < 200; i++) { michael@0: let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); michael@0: yield m.incrementDailyCounter("daily-counter", date); michael@0: } michael@0: michael@0: // This test could fail if we cross a UTC day boundary when running. So, michael@0: // we ensure this doesn't occur. michael@0: Object.defineProperty(reporter, "_now", { michael@0: value: function () { michael@0: return now; michael@0: }, michael@0: }); michael@0: michael@0: let payload = yield reporter.getJSONPayload(); michael@0: print(payload); michael@0: let o = JSON.parse(payload); michael@0: michael@0: do_check_eq(Object.keys(o.data.days).length, 180); michael@0: let today = reporter._formatDate(now); michael@0: do_check_true(today in o.data.days); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_json_payload_newer_version_overwrites() { michael@0: let reporter = yield getReporter("json_payload_newer_version_overwrites"); michael@0: michael@0: try { michael@0: let now = new Date(); michael@0: // Instead of hacking up the internals to ensure consistent order in Map michael@0: // iteration (which would be difficult), we instead opt to generate a lot michael@0: // of measurements of different versions and verify their iterable order michael@0: // is not increasing. michael@0: let versions = [1, 6, 3, 9, 2, 3, 7, 4, 10, 8]; michael@0: let protos = []; michael@0: for (let version of versions) { michael@0: let m = function () { michael@0: Metrics.Measurement.call(this); michael@0: }; michael@0: m.prototype = { michael@0: __proto__: DummyMeasurement.prototype, michael@0: name: "DummyMeasurement", michael@0: version: version, michael@0: }; michael@0: michael@0: protos.push(m); michael@0: } michael@0: michael@0: let ctor = function () { michael@0: Metrics.Provider.call(this); michael@0: }; michael@0: ctor.prototype = { michael@0: __proto__: DummyProvider.prototype, michael@0: michael@0: name: "MultiMeasurementProvider", michael@0: measurementTypes: protos, michael@0: }; michael@0: michael@0: let provider = new ctor(); michael@0: michael@0: yield reporter._providerManager.registerProvider(provider); michael@0: michael@0: let haveUnordered = false; michael@0: let last = -1; michael@0: let highestVersion = -1; michael@0: for (let [key, measurement] of provider.measurements) { michael@0: yield measurement.setDailyLastNumeric("daily-last-numeric", michael@0: measurement.version, now); michael@0: yield measurement.setLastNumeric("last-numeric", michael@0: measurement.version, now); michael@0: michael@0: if (measurement.version > highestVersion) { michael@0: highestVersion = measurement.version; michael@0: } michael@0: michael@0: if (measurement.version < last) { michael@0: haveUnordered = true; michael@0: } michael@0: michael@0: last = measurement.version; michael@0: } michael@0: michael@0: // Ensure Map traversal isn't ordered. If this ever fails, then we'll need michael@0: // to monkeypatch. michael@0: do_check_true(haveUnordered); michael@0: michael@0: let payload = yield reporter.getJSONPayload(); michael@0: let o = JSON.parse(payload); michael@0: do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.last); michael@0: do_check_eq(o.data.last["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion); michael@0: michael@0: let day = reporter._formatDate(now); michael@0: do_check_true(day in o.data.days); michael@0: do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.days[day]); michael@0: do_check_eq(o.data.days[day]["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion); michael@0: michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_idle_daily() { michael@0: let reporter = yield getReporter("idle_daily"); michael@0: try { michael@0: let provider = new DummyProvider(); michael@0: yield reporter._providerManager.registerProvider(provider); michael@0: michael@0: let now = new Date(); michael@0: let m = provider.getMeasurement("DummyMeasurement", 1); michael@0: for (let i = 0; i < 200; i++) { michael@0: let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY); michael@0: yield m.incrementDailyCounter("daily-counter", date); michael@0: } michael@0: michael@0: let values = yield m.getValues(); michael@0: do_check_eq(values.days.size, 200); michael@0: michael@0: Services.obs.notifyObservers(null, "idle-daily", null); michael@0: michael@0: values = yield m.getValues(); michael@0: do_check_eq(values.days.size, 180); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_data_submission_transport_failure() { michael@0: let reporter = yield getReporter("data_submission_transport_failure"); michael@0: try { michael@0: reporter.serverURI = DUMMY_URI; michael@0: reporter.serverNamespace = "test00"; michael@0: michael@0: let deferred = Promise.defer(); michael@0: let request = new DataSubmissionRequest(deferred, new Date(Date.now + 30000)); michael@0: reporter.requestDataUpload(request); michael@0: michael@0: yield deferred.promise; michael@0: do_check_eq(request.state, request.SUBMISSION_FAILURE_SOFT); michael@0: michael@0: let data = yield getHealthReportProviderValues(reporter, new Date()); michael@0: do_check_eq(data.firstDocumentUploadAttempt, 1); michael@0: do_check_eq(data.uploadTransportFailure, 1); michael@0: do_check_eq(Object.keys(data).length, 3); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_data_submission_server_failure() { michael@0: let [reporter, server] = yield getReporterAndServer("data_submission_server_failure"); michael@0: try { michael@0: Object.defineProperty(server, "_handleNamespaceSubmitPost", { michael@0: value: function (ns, id, request, response) { michael@0: throw HTTP_500; michael@0: }, michael@0: writable: true, michael@0: }); michael@0: michael@0: let deferred = Promise.defer(); michael@0: let now = new Date(); michael@0: let request = new DataSubmissionRequest(deferred, now); michael@0: reporter.requestDataUpload(request); michael@0: yield deferred.promise; michael@0: do_check_eq(request.state, request.SUBMISSION_FAILURE_HARD); michael@0: michael@0: let data = yield getHealthReportProviderValues(reporter, now); michael@0: do_check_eq(data.firstDocumentUploadAttempt, 1); michael@0: do_check_eq(data.uploadServerFailure, 1); michael@0: do_check_eq(Object.keys(data).length, 3); michael@0: } finally { michael@0: yield shutdownServer(server); michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_data_submission_success() { michael@0: let [reporter, server] = yield getReporterAndServer("data_submission_success"); michael@0: try { michael@0: yield reporter._providerManager.registerProviderFromType(DummyProvider); michael@0: yield reporter._providerManager.registerProviderFromType(DummyConstantProvider); michael@0: michael@0: do_check_eq(reporter.lastPingDate.getTime(), 0); michael@0: do_check_false(reporter.haveRemoteData()); michael@0: michael@0: let deferred = Promise.defer(); michael@0: michael@0: let now = new Date(); michael@0: let request = new DataSubmissionRequest(deferred, now); michael@0: reporter._state.addRemoteID("foo"); michael@0: reporter.requestDataUpload(request); michael@0: yield deferred.promise; michael@0: do_check_eq(request.state, request.SUBMISSION_SUCCESS); michael@0: do_check_true(reporter.lastPingDate.getTime() > 0); michael@0: do_check_true(reporter.haveRemoteData()); michael@0: for (let remoteID of reporter._state.remoteIDs) { michael@0: do_check_neq(remoteID, "foo"); michael@0: } michael@0: michael@0: // Ensure data from providers made it to payload. michael@0: let o = yield reporter.getJSONPayload(true); michael@0: do_check_true("DummyProvider.DummyMeasurement" in o.data.last); michael@0: do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last); michael@0: michael@0: let data = yield getHealthReportProviderValues(reporter, now); michael@0: do_check_eq(data.continuationUploadAttempt, 1); michael@0: do_check_eq(data.uploadSuccess, 1); michael@0: do_check_eq(Object.keys(data).length, 3); michael@0: michael@0: let d = reporter.lastPingDate; michael@0: let id = reporter.lastSubmitID; michael@0: let clientID = reporter._state.clientID; michael@0: michael@0: reporter._shutdown(); michael@0: michael@0: // Ensure reloading state works. michael@0: reporter = yield getReporter("data_submission_success"); michael@0: do_check_eq(reporter.lastSubmitID, id); michael@0: do_check_eq(reporter.lastPingDate.getTime(), d.getTime()); michael@0: do_check_eq(reporter._state.clientID, clientID); michael@0: michael@0: reporter._shutdown(); michael@0: } finally { michael@0: yield shutdownServer(server); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_recurring_daily_pings() { michael@0: let [reporter, server] = yield getReporterAndServer("recurring_daily_pings"); michael@0: try { michael@0: reporter._providerManager.registerProvider(new DummyProvider()); michael@0: michael@0: let policy = reporter._policy; michael@0: michael@0: defineNow(policy, policy._futureDate(-24 * 60 * 68 * 1000)); michael@0: policy.recordUserAcceptance(); michael@0: defineNow(policy, policy.nextDataSubmissionDate); michael@0: let promise = policy.checkStateAndTrigger(); michael@0: do_check_neq(promise, null); michael@0: yield promise; michael@0: michael@0: let lastID = reporter.lastSubmitID; michael@0: do_check_neq(lastID, null); michael@0: do_check_true(server.hasDocument(reporter.serverNamespace, lastID)); michael@0: michael@0: // Skip forward to next scheduled submission time. michael@0: defineNow(policy, policy.nextDataSubmissionDate); michael@0: promise = policy.checkStateAndTrigger(); michael@0: do_check_neq(promise, null); michael@0: yield promise; michael@0: do_check_neq(reporter.lastSubmitID, lastID); michael@0: do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); michael@0: do_check_false(server.hasDocument(reporter.serverNamespace, lastID)); michael@0: michael@0: // now() on the health reporter instance wasn't munged. So, we should see michael@0: // both requests attributed to the same day. michael@0: let data = yield getHealthReportProviderValues(reporter, new Date()); michael@0: do_check_eq(data.firstDocumentUploadAttempt, 1); michael@0: do_check_eq(data.continuationUploadAttempt, 1); michael@0: do_check_eq(data.uploadSuccess, 2); michael@0: do_check_eq(Object.keys(data).length, 4); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: yield shutdownServer(server); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_request_remote_data_deletion() { michael@0: let [reporter, server] = yield getReporterAndServer("request_remote_data_deletion"); michael@0: michael@0: try { michael@0: let policy = reporter._policy; michael@0: defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); michael@0: policy.recordUserAcceptance(); michael@0: defineNow(policy, policy.nextDataSubmissionDate); michael@0: yield policy.checkStateAndTrigger(); michael@0: let id = reporter.lastSubmitID; michael@0: do_check_neq(id, null); michael@0: do_check_true(server.hasDocument(reporter.serverNamespace, id)); michael@0: michael@0: let clientID = reporter._state.clientID; michael@0: do_check_neq(clientID, null); michael@0: michael@0: defineNow(policy, policy._futureDate(10 * 1000)); michael@0: michael@0: let promise = reporter.requestDeleteRemoteData(); michael@0: do_check_neq(promise, null); michael@0: yield promise; michael@0: do_check_null(reporter.lastSubmitID); michael@0: do_check_false(reporter.haveRemoteData()); michael@0: do_check_false(server.hasDocument(reporter.serverNamespace, id)); michael@0: michael@0: // Client ID should be updated. michael@0: do_check_neq(reporter._state.clientID, null); michael@0: do_check_neq(reporter._state.clientID, clientID); michael@0: do_check_eq(reporter._state.clientIDVersion, 1); michael@0: michael@0: // And it should be persisted to disk. michael@0: let o = yield CommonUtils.readJSON(reporter._state._filename); michael@0: do_check_eq(o.clientID, reporter._state.clientID); michael@0: do_check_eq(o.clientIDVersion, 1); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: yield shutdownServer(server); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_multiple_simultaneous_uploads() { michael@0: let [reporter, server] = yield getReporterAndServer("multiple_simultaneous_uploads"); michael@0: michael@0: try { michael@0: let d1 = Promise.defer(); michael@0: let d2 = Promise.defer(); michael@0: let t1 = new Date(Date.now() - 1000); michael@0: let t2 = new Date(t1.getTime() + 500); michael@0: let r1 = new DataSubmissionRequest(d1, t1); michael@0: let r2 = new DataSubmissionRequest(d2, t2); michael@0: michael@0: let getPayloadDeferred = Promise.defer(); michael@0: michael@0: Object.defineProperty(reporter, "getJSONPayload", { michael@0: configurable: true, michael@0: value: () => { michael@0: getPayloadDeferred.resolve(); michael@0: delete reporter["getJSONPayload"]; michael@0: return reporter.getJSONPayload(); michael@0: }, michael@0: }); michael@0: michael@0: let p1 = reporter.requestDataUpload(r1); michael@0: yield getPayloadDeferred.promise; michael@0: do_check_true(reporter._uploadInProgress); michael@0: let p2 = reporter.requestDataUpload(r2); michael@0: michael@0: yield p1; michael@0: yield p2; michael@0: michael@0: do_check_eq(r1.state, r1.SUBMISSION_SUCCESS); michael@0: do_check_eq(r2.state, r2.UPLOAD_IN_PROGRESS); michael@0: michael@0: // They should both be resolved already. michael@0: yield d1; michael@0: yield d2; michael@0: michael@0: let data = yield getHealthReportProviderValues(reporter, t1); michael@0: do_check_eq(data.firstDocumentUploadAttempt, 1); michael@0: do_check_false("continuationUploadAttempt" in data); michael@0: do_check_eq(data.uploadSuccess, 1); michael@0: do_check_eq(data.uploadAlreadyInProgress, 1); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: yield shutdownServer(server); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_policy_accept_reject() { michael@0: let [reporter, server] = yield getReporterAndServer("policy_accept_reject"); michael@0: michael@0: try { michael@0: let policy = reporter._policy; michael@0: michael@0: do_check_false(policy.dataSubmissionPolicyAccepted); michael@0: do_check_false(reporter.willUploadData); michael@0: michael@0: policy.recordUserAcceptance(); michael@0: do_check_true(policy.dataSubmissionPolicyAccepted); michael@0: do_check_true(reporter.willUploadData); michael@0: michael@0: policy.recordUserRejection(); michael@0: do_check_false(policy.dataSubmissionPolicyAccepted); michael@0: do_check_false(reporter.willUploadData); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: yield shutdownServer(server); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_error_message_scrubbing() { michael@0: let reporter = yield getReporter("error_message_scrubbing"); michael@0: michael@0: try { michael@0: let profile = Services.dirsvc.get("ProfD", Ci.nsIFile).path; michael@0: reporter._recordError("Foo " + profile); michael@0: michael@0: do_check_eq(reporter._errors.length, 1); michael@0: do_check_eq(reporter._errors[0], "Foo "); michael@0: michael@0: reporter._errors = []; michael@0: michael@0: let appdata = Services.dirsvc.get("UAppData", Ci.nsIFile); michael@0: let uri = Services.io.newFileURI(appdata); michael@0: michael@0: reporter._recordError("Foo " + uri.spec); michael@0: do_check_eq(reporter._errors[0], "Foo "); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_basic_appinfo() { michael@0: function verify(d) { michael@0: do_check_eq(d["_v"], 1); michael@0: do_check_eq(d._v, 1); michael@0: do_check_eq(d.vendor, "Mozilla"); michael@0: do_check_eq(d.name, "xpcshell"); michael@0: do_check_eq(d.id, "xpcshell@tests.mozilla.org"); michael@0: do_check_eq(d.version, "1"); michael@0: do_check_eq(d.appBuildID, "20121107"); michael@0: do_check_eq(d.platformVersion, "p-ver"); michael@0: do_check_eq(d.platformBuildID, "20121106"); michael@0: do_check_eq(d.os, "XPCShell"); michael@0: do_check_eq(d.xpcomabi, "noarch-spidermonkey"); michael@0: do_check_true("updateChannel" in d); michael@0: } michael@0: let reporter = yield getReporter("basic_appinfo"); michael@0: try { michael@0: verify(reporter.obtainAppInfo()); michael@0: let payload = yield reporter.collectAndObtainJSONPayload(true); michael@0: do_check_eq(payload["version"], 2); michael@0: verify(payload["geckoAppInfo"]); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // Ensure collection occurs if upload is disabled. michael@0: add_task(function test_collect_when_upload_disabled() { michael@0: let reporter = getHealthReporter("collect_when_upload_disabled"); michael@0: reporter._policy.recordHealthReportUploadEnabled(false, "testing-collect"); michael@0: do_check_false(reporter._policy.healthReportUploadEnabled); michael@0: michael@0: let name = "healthreport-testing-collect_when_upload_disabled-healthreport-lastDailyCollection"; michael@0: let pref = "app.update.lastUpdateTime." + name; michael@0: do_check_false(Services.prefs.prefHasUserValue(pref)); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: do_check_true(Services.prefs.prefHasUserValue(pref)); michael@0: michael@0: // We would ideally ensure the timer fires and does the right thing. michael@0: // However, testing the update timer manager is quite involved. michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_failure_if_not_initialized() { michael@0: let reporter = yield getReporter("failure_if_not_initialized"); michael@0: reporter._shutdown(); michael@0: michael@0: let error = false; michael@0: try { michael@0: yield reporter.requestDataUpload(); michael@0: } catch (ex) { michael@0: error = true; michael@0: do_check_true(ex.message.contains("Not initialized.")); michael@0: } finally { michael@0: do_check_true(error); michael@0: error = false; michael@0: } michael@0: michael@0: try { michael@0: yield reporter.collectMeasurements(); michael@0: } catch (ex) { michael@0: error = true; michael@0: do_check_true(ex.message.contains("Not initialized.")); michael@0: } finally { michael@0: do_check_true(error); michael@0: error = false; michael@0: } michael@0: michael@0: // getJSONPayload always works (to facilitate error upload). michael@0: yield reporter.getJSONPayload(); michael@0: }); michael@0: michael@0: add_task(function test_upload_on_init_failure() { michael@0: let server = new BagheeraServer(); michael@0: server.start(); michael@0: let reporter = yield getHealthReporter("upload_on_init_failure", server.serverURI, true); michael@0: server.createNamespace(reporter.serverNamespace); michael@0: michael@0: reporter.onInitializeProviderManagerFinished = function () { michael@0: throw new Error("Fake error during provider manager initialization."); michael@0: }; michael@0: michael@0: let deferred = Promise.defer(); michael@0: michael@0: let oldOnResult = reporter._onBagheeraResult; michael@0: Object.defineProperty(reporter, "_onBagheeraResult", { michael@0: value: function (request, isDelete, date, result) { michael@0: do_check_false(isDelete); michael@0: do_check_true(result.transportSuccess); michael@0: do_check_true(result.serverSuccess); michael@0: michael@0: oldOnResult.call(reporter, request, isDelete, new Date(), result); michael@0: deferred.resolve(); michael@0: }, michael@0: }); michael@0: michael@0: reporter._policy.recordUserAcceptance(); michael@0: let error = false; michael@0: try { michael@0: yield reporter.init(); michael@0: } catch (ex) { michael@0: error = true; michael@0: } finally { michael@0: do_check_true(error); michael@0: } michael@0: michael@0: // At this point the emergency upload should have been initiated. We michael@0: // wait for our monkeypatched response handler to signal request michael@0: // completion. michael@0: yield deferred.promise; michael@0: michael@0: do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID)); michael@0: let doc = server.getDocument(reporter.serverNamespace, reporter.lastSubmitID); michael@0: do_check_true("notInitialized" in doc); michael@0: do_check_eq(doc.notInitialized, 1); michael@0: do_check_true("errors" in doc); michael@0: do_check_eq(doc.errors.length, 1); michael@0: do_check_true(doc.errors[0].contains("Fake error during provider manager initialization")); michael@0: michael@0: reporter._shutdown(); michael@0: yield shutdownServer(server); michael@0: }); michael@0: michael@0: add_task(function test_state_prefs_conversion_simple() { michael@0: let reporter = getHealthReporter("state_prefs_conversion"); michael@0: let prefs = reporter._prefs; michael@0: michael@0: let lastSubmit = new Date(); michael@0: prefs.set("lastSubmitID", "lastID"); michael@0: CommonUtils.setDatePref(prefs, "lastPingTime", lastSubmit); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_eq(reporter._state.lastSubmitID, "lastID"); michael@0: do_check_eq(reporter._state.remoteIDs.length, 1); michael@0: do_check_eq(reporter._state.lastPingDate.getTime(), lastSubmit.getTime()); michael@0: do_check_eq(reporter._state.lastPingDate.getTime(), reporter.lastPingDate.getTime()); michael@0: do_check_eq(reporter._state.lastSubmitID, reporter.lastSubmitID); michael@0: do_check_true(reporter.haveRemoteData()); michael@0: michael@0: // User set preferences should have been wiped out. michael@0: do_check_false(prefs.isSet("lastSubmitID")); michael@0: do_check_false(prefs.isSet("lastPingTime")); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // If the saved JSON file does not contain an object, we should reset michael@0: // automatically. michael@0: add_task(function test_state_no_json_object() { michael@0: let reporter = getHealthReporter("state_shared"); michael@0: yield CommonUtils.writeJSON("hello", reporter._state._filename); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_eq(reporter.lastPingDate.getTime(), 0); michael@0: do_check_null(reporter.lastSubmitID); michael@0: michael@0: let o = yield CommonUtils.readJSON(reporter._state._filename); michael@0: do_check_eq(typeof(o), "object"); michael@0: do_check_eq(o.v, 1); michael@0: do_check_eq(o.lastPingTime, 0); michael@0: do_check_eq(o.remoteIDs.length, 0); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // If we encounter a future version, we reset state to the current version. michael@0: add_task(function test_state_future_version() { michael@0: let reporter = getHealthReporter("state_shared"); michael@0: yield CommonUtils.writeJSON({v: 2, remoteIDs: ["foo"], lastPingTime: 2412}, michael@0: reporter._state._filename); michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_eq(reporter.lastPingDate.getTime(), 0); michael@0: do_check_null(reporter.lastSubmitID); michael@0: michael@0: // While the object is updated, we don't save the file. michael@0: let o = yield CommonUtils.readJSON(reporter._state._filename); michael@0: do_check_eq(o.v, 2); michael@0: do_check_eq(o.lastPingTime, 2412); michael@0: do_check_eq(o.remoteIDs.length, 1); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // Test recovery if the state file contains invalid JSON. michael@0: add_task(function test_state_invalid_json() { michael@0: let reporter = getHealthReporter("state_shared"); michael@0: michael@0: let encoder = new TextEncoder(); michael@0: let arr = encoder.encode("{foo: bad value, 'bad': as2,}"); michael@0: let path = reporter._state._filename; michael@0: yield OS.File.writeAtomic(path, arr, {tmpPath: path + ".tmp"}); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_eq(reporter.lastPingDate.getTime(), 0); michael@0: do_check_null(reporter.lastSubmitID); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function test_state_multiple_remote_ids() { michael@0: let [reporter, server] = yield getReporterAndServer("state_multiple_remote_ids"); michael@0: let documents = [ michael@0: [reporter.serverNamespace, "one", "{v:1}"], michael@0: [reporter.serverNamespace, "two", "{v:2}"], michael@0: ]; michael@0: let now = new Date(Date.now() - 5000); michael@0: michael@0: try { michael@0: for (let [ns, id, payload] of documents) { michael@0: server.setDocument(ns, id, payload); michael@0: do_check_true(server.hasDocument(ns, id)); michael@0: yield reporter._state.addRemoteID(id); michael@0: do_check_eq(reporter._state.remoteIDs.indexOf(id), reporter._state.remoteIDs.length - 1); michael@0: } michael@0: yield reporter._state.setLastPingDate(now); michael@0: do_check_eq(reporter._state.remoteIDs.length, 2); michael@0: do_check_eq(reporter.lastSubmitID, documents[0][1]); michael@0: michael@0: let deferred = Promise.defer(); michael@0: let request = new DataSubmissionRequest(deferred, now); michael@0: reporter.requestDataUpload(request); michael@0: yield deferred.promise; michael@0: michael@0: do_check_eq(reporter._state.remoteIDs.length, 1); michael@0: for (let [,id,] of documents) { michael@0: do_check_eq(reporter._state.remoteIDs.indexOf(id), -1); michael@0: do_check_false(server.hasDocument(reporter.serverNamespace, id)); michael@0: } michael@0: do_check_true(reporter.lastPingDate.getTime() > now.getTime()); michael@0: michael@0: let o = yield CommonUtils.readJSON(reporter._state._filename); michael@0: do_check_eq(o.remoteIDs.length, 1); michael@0: do_check_eq(o.remoteIDs[0], reporter._state.remoteIDs[0]); michael@0: do_check_eq(o.lastPingTime, reporter.lastPingDate.getTime()); michael@0: } finally { michael@0: yield shutdownServer(server); michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // If we have a state file then downgrade to prefs, the prefs should be michael@0: // reimported and should supplement existing state. michael@0: add_task(function test_state_downgrade_upgrade() { michael@0: let reporter = getHealthReporter("state_shared"); michael@0: michael@0: let now = new Date(); michael@0: michael@0: yield CommonUtils.writeJSON({v: 1, remoteIDs: ["id1", "id2"], lastPingTime: now.getTime()}, michael@0: reporter._state._filename); michael@0: michael@0: let prefs = reporter._prefs; michael@0: prefs.set("lastSubmitID", "prefID"); michael@0: prefs.set("lastPingTime", "" + (now.getTime() + 1000)); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_eq(reporter.lastSubmitID, "id1"); michael@0: do_check_eq(reporter._state.remoteIDs.length, 3); michael@0: do_check_eq(reporter._state.remoteIDs[2], "prefID"); michael@0: do_check_eq(reporter.lastPingDate.getTime(), now.getTime() + 1000); michael@0: do_check_false(prefs.isSet("lastSubmitID")); michael@0: do_check_false(prefs.isSet("lastPingTime")); michael@0: michael@0: let o = yield CommonUtils.readJSON(reporter._state._filename); michael@0: do_check_eq(o.remoteIDs.length, 3); michael@0: do_check_eq(o.lastPingTime, now.getTime() + 1000); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // Missing client ID in state should be created on state load. michael@0: add_task(function* test_state_create_client_id() { michael@0: let reporter = getHealthReporter("state_create_client_id"); michael@0: michael@0: yield CommonUtils.writeJSON({ michael@0: v: 1, michael@0: remoteIDs: ["id1", "id2"], michael@0: lastPingTime: Date.now(), michael@0: removeOutdatedLastPayload: true, michael@0: }, reporter._state._filename); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_eq(reporter.lastSubmitID, "id1"); michael@0: do_check_neq(reporter._state.clientID, null); michael@0: do_check_eq(reporter._state.clientID.length, 36); michael@0: do_check_eq(reporter._state.clientIDVersion, 1); michael@0: michael@0: let clientID = reporter._state.clientID; michael@0: michael@0: // The client ID should be persisted as soon as it is created. michael@0: reporter._shutdown(); michael@0: michael@0: reporter = getHealthReporter("state_create_client_id"); michael@0: yield reporter.init(); michael@0: do_check_eq(reporter._state.clientID, clientID); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: // Invalid stored client ID is reset automatically. michael@0: add_task(function* test_empty_client_id() { michael@0: let reporter = getHealthReporter("state_empty_client_id"); michael@0: michael@0: yield CommonUtils.writeJSON({ michael@0: v: 1, michael@0: clientID: "", michael@0: remoteIDs: ["id1", "id2"], michael@0: lastPingTime: Date.now(), michael@0: removeOutdatedLastPayload: true, michael@0: }, reporter._state._filename); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_neq(reporter._state.clientID, null); michael@0: do_check_eq(reporter._state.clientID.length, 36); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: }); michael@0: michael@0: add_task(function* test_nonstring_client_id() { michael@0: let reporter = getHealthReporter("state_nonstring_client_id"); michael@0: michael@0: yield CommonUtils.writeJSON({ michael@0: v: 1, michael@0: clientID: 42, michael@0: remoteIDs: ["id1", "id2"], michael@0: lastPingTime: Date.now(), michael@0: remoteOutdatedLastPayload: true, michael@0: }, reporter._state._filename); michael@0: michael@0: try { michael@0: yield reporter.init(); michael@0: michael@0: do_check_neq(reporter._state.clientID, null); michael@0: do_check_eq(reporter._state.clientID.length, 36); michael@0: } finally { michael@0: reporter._shutdown(); michael@0: } michael@0: });