services/healthreport/tests/xpcshell/test_healthreporter.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 * http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 "use strict";
michael@0 5
michael@0 6 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
michael@0 7
michael@0 8 Cu.import("resource://services-common/observers.js");
michael@0 9 Cu.import("resource://services-common/utils.js");
michael@0 10 Cu.import("resource://gre/modules/Promise.jsm");
michael@0 11 Cu.import("resource://gre/modules/Metrics.jsm");
michael@0 12 Cu.import("resource://gre/modules/osfile.jsm");
michael@0 13 Cu.import("resource://gre/modules/Preferences.jsm");
michael@0 14 let bsp = Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm");
michael@0 15 Cu.import("resource://gre/modules/services/healthreport/providers.jsm");
michael@0 16 Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
michael@0 17 Cu.import("resource://gre/modules/Services.jsm");
michael@0 18 Cu.import("resource://gre/modules/Task.jsm");
michael@0 19 Cu.import("resource://testing-common/httpd.js");
michael@0 20 Cu.import("resource://testing-common/services-common/bagheeraserver.js");
michael@0 21 Cu.import("resource://testing-common/services/metrics/mocks.jsm");
michael@0 22 Cu.import("resource://testing-common/services/healthreport/utils.jsm");
michael@0 23 Cu.import("resource://testing-common/AppData.jsm");
michael@0 24
michael@0 25
michael@0 26 const DUMMY_URI = "http://localhost:62013/";
michael@0 27 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
michael@0 28
michael@0 29 const HealthReporterState = bsp.HealthReporterState;
michael@0 30
michael@0 31
michael@0 32 function defineNow(policy, now) {
michael@0 33 print("Adjusting fake system clock to " + now);
michael@0 34 Object.defineProperty(policy, "now", {
michael@0 35 value: function customNow() {
michael@0 36 return now;
michael@0 37 },
michael@0 38 writable: true,
michael@0 39 });
michael@0 40 }
michael@0 41
michael@0 42 function getReporter(name, uri, inspected) {
michael@0 43 return Task.spawn(function init() {
michael@0 44 let reporter = getHealthReporter(name, uri, inspected);
michael@0 45 yield reporter.init();
michael@0 46
michael@0 47 yield reporter._providerManager.registerProviderFromType(
michael@0 48 HealthReportProvider);
michael@0 49
michael@0 50 throw new Task.Result(reporter);
michael@0 51 });
michael@0 52 }
michael@0 53
michael@0 54 function getReporterAndServer(name, namespace="test") {
michael@0 55 return Task.spawn(function get() {
michael@0 56 let server = new BagheeraServer();
michael@0 57 server.createNamespace(namespace);
michael@0 58 server.start();
michael@0 59
michael@0 60 let reporter = yield getReporter(name, server.serverURI);
michael@0 61 reporter.serverNamespace = namespace;
michael@0 62
michael@0 63 throw new Task.Result([reporter, server]);
michael@0 64 });
michael@0 65 }
michael@0 66
michael@0 67 function shutdownServer(server) {
michael@0 68 let deferred = Promise.defer();
michael@0 69 server.stop(deferred.resolve.bind(deferred));
michael@0 70
michael@0 71 return deferred.promise;
michael@0 72 }
michael@0 73
michael@0 74 function getHealthReportProviderValues(reporter, day=null) {
michael@0 75 return Task.spawn(function getValues() {
michael@0 76 let p = reporter.getProvider("org.mozilla.healthreport");
michael@0 77 do_check_neq(p, null);
michael@0 78 let m = p.getMeasurement("submissions", 2);
michael@0 79 do_check_neq(m, null);
michael@0 80
michael@0 81 let data = yield reporter._storage.getMeasurementValues(m.id);
michael@0 82 if (!day) {
michael@0 83 throw new Task.Result(data);
michael@0 84 }
michael@0 85
michael@0 86 do_check_true(data.days.hasDay(day));
michael@0 87 let serializer = m.serializer(m.SERIALIZE_JSON)
michael@0 88 let json = serializer.daily(data.days.getDay(day));
michael@0 89 do_check_eq(json._v, 2);
michael@0 90
michael@0 91 throw new Task.Result(json);
michael@0 92 });
michael@0 93 }
michael@0 94
michael@0 95 function run_test() {
michael@0 96 run_next_test();
michael@0 97 }
michael@0 98
michael@0 99 // run_test() needs to finish synchronously, so we do async init here.
michael@0 100 add_task(function test_init() {
michael@0 101 yield makeFakeAppDir();
michael@0 102 });
michael@0 103
michael@0 104 add_task(function test_constructor() {
michael@0 105 let reporter = yield getReporter("constructor");
michael@0 106
michael@0 107 try {
michael@0 108 do_check_eq(reporter.lastPingDate.getTime(), 0);
michael@0 109 do_check_null(reporter.lastSubmitID);
michael@0 110 do_check_eq(typeof(reporter._state), "object");
michael@0 111 do_check_eq(reporter._state.lastPingDate.getTime(), 0);
michael@0 112 do_check_eq(reporter._state.remoteIDs.length, 0);
michael@0 113 do_check_eq(reporter._state.clientIDVersion, 1);
michael@0 114 do_check_neq(reporter._state.clientID, null);
michael@0 115
michael@0 116 let failed = false;
michael@0 117 try {
michael@0 118 new HealthReporter("foo.bar");
michael@0 119 } catch (ex) {
michael@0 120 failed = true;
michael@0 121 do_check_true(ex.message.startsWith("Branch must end"));
michael@0 122 } finally {
michael@0 123 do_check_true(failed);
michael@0 124 failed = false;
michael@0 125 }
michael@0 126 } finally {
michael@0 127 reporter._shutdown();
michael@0 128 }
michael@0 129 });
michael@0 130
michael@0 131 add_task(function test_shutdown_normal() {
michael@0 132 let reporter = yield getReporter("shutdown_normal");
michael@0 133
michael@0 134 // We can't send "quit-application" notification because the xpcshell runner
michael@0 135 // will shut down!
michael@0 136 reporter._initiateShutdown();
michael@0 137 reporter._waitForShutdown();
michael@0 138 });
michael@0 139
michael@0 140 add_task(function test_shutdown_storage_in_progress() {
michael@0 141 let reporter = yield getHealthReporter("shutdown_storage_in_progress", DUMMY_URI, true);
michael@0 142
michael@0 143 reporter.onStorageCreated = function () {
michael@0 144 print("Faking shutdown during storage initialization.");
michael@0 145 reporter._initiateShutdown();
michael@0 146 };
michael@0 147
michael@0 148 reporter.init();
michael@0 149
michael@0 150 reporter._waitForShutdown();
michael@0 151 do_check_eq(reporter.providerManagerShutdownCount, 0);
michael@0 152 do_check_eq(reporter.storageCloseCount, 1);
michael@0 153 });
michael@0 154
michael@0 155 // Ensure that a shutdown triggered while provider manager is initializing
michael@0 156 // results in shutdown and storage closure.
michael@0 157 add_task(function test_shutdown_provider_manager_in_progress() {
michael@0 158 let reporter = yield getHealthReporter("shutdown_provider_manager_in_progress",
michael@0 159 DUMMY_URI, true);
michael@0 160
michael@0 161 reporter.onProviderManagerInitialized = function () {
michael@0 162 print("Faking shutdown during provider manager initialization.");
michael@0 163 reporter._initiateShutdown();
michael@0 164 };
michael@0 165
michael@0 166 reporter.init();
michael@0 167
michael@0 168 // This will hang if shutdown logic is busted.
michael@0 169 reporter._waitForShutdown();
michael@0 170 do_check_eq(reporter.providerManagerShutdownCount, 1);
michael@0 171 do_check_eq(reporter.storageCloseCount, 1);
michael@0 172 });
michael@0 173
michael@0 174 // Simulates an error during provider manager initialization and verifies we shut down.
michael@0 175 add_task(function test_shutdown_when_provider_manager_errors() {
michael@0 176 let reporter = yield getHealthReporter("shutdown_when_provider_manager_errors",
michael@0 177 DUMMY_URI, true);
michael@0 178
michael@0 179 reporter.onInitializeProviderManagerFinished = function () {
michael@0 180 print("Throwing fake error.");
michael@0 181 throw new Error("Fake error during provider manager initialization.");
michael@0 182 };
michael@0 183
michael@0 184 reporter.init();
michael@0 185
michael@0 186 // This will hang if shutdown logic is busted.
michael@0 187 reporter._waitForShutdown();
michael@0 188 do_check_eq(reporter.providerManagerShutdownCount, 1);
michael@0 189 do_check_eq(reporter.storageCloseCount, 1);
michael@0 190 });
michael@0 191
michael@0 192 // Pull-only providers are only initialized at collect time.
michael@0 193 add_task(function test_pull_only_providers() {
michael@0 194 const category = "healthreporter-constant-only";
michael@0 195
michael@0 196 let cm = Cc["@mozilla.org/categorymanager;1"]
michael@0 197 .getService(Ci.nsICategoryManager);
michael@0 198 cm.addCategoryEntry(category, "DummyProvider",
michael@0 199 "resource://testing-common/services/metrics/mocks.jsm",
michael@0 200 false, true);
michael@0 201 cm.addCategoryEntry(category, "DummyConstantProvider",
michael@0 202 "resource://testing-common/services/metrics/mocks.jsm",
michael@0 203 false, true);
michael@0 204
michael@0 205 let reporter = yield getReporter("constant_only_providers");
michael@0 206 try {
michael@0 207 let initCount = reporter._providerManager.providers.length;
michael@0 208 yield reporter._providerManager.registerProvidersFromCategoryManager(category);
michael@0 209 do_check_eq(reporter._providerManager._providers.size, initCount + 1);
michael@0 210 do_check_true(reporter._storage.hasProvider("DummyProvider"));
michael@0 211 do_check_false(reporter._storage.hasProvider("DummyConstantProvider"));
michael@0 212 do_check_neq(reporter.getProvider("DummyProvider"), null);
michael@0 213 do_check_null(reporter.getProvider("DummyConstantProvider"));
michael@0 214
michael@0 215 yield reporter.collectMeasurements();
michael@0 216
michael@0 217 do_check_eq(reporter._providerManager._providers.size, initCount + 1);
michael@0 218 do_check_true(reporter._storage.hasProvider("DummyConstantProvider"));
michael@0 219
michael@0 220 let mID = reporter._storage.measurementID("DummyConstantProvider", "DummyMeasurement", 1);
michael@0 221 let values = yield reporter._storage.getMeasurementValues(mID);
michael@0 222 do_check_true(values.singular.size > 0);
michael@0 223 } finally {
michael@0 224 reporter._shutdown();
michael@0 225 }
michael@0 226 });
michael@0 227
michael@0 228 add_task(function test_collect_daily() {
michael@0 229 let reporter = yield getReporter("collect_daily");
michael@0 230
michael@0 231 try {
michael@0 232 let now = new Date();
michael@0 233 let provider = new DummyProvider();
michael@0 234 yield reporter._providerManager.registerProvider(provider);
michael@0 235 yield reporter.collectMeasurements();
michael@0 236
michael@0 237 do_check_eq(provider.collectConstantCount, 1);
michael@0 238 do_check_eq(provider.collectDailyCount, 1);
michael@0 239
michael@0 240 yield reporter.collectMeasurements();
michael@0 241 do_check_eq(provider.collectConstantCount, 1);
michael@0 242 do_check_eq(provider.collectDailyCount, 1);
michael@0 243
michael@0 244 yield reporter.collectMeasurements();
michael@0 245 do_check_eq(provider.collectDailyCount, 1); // Too soon.
michael@0 246
michael@0 247 reporter._lastDailyDate = now.getTime() - MILLISECONDS_PER_DAY - 1;
michael@0 248 yield reporter.collectMeasurements();
michael@0 249 do_check_eq(provider.collectDailyCount, 2);
michael@0 250
michael@0 251 reporter._lastDailyDate = null;
michael@0 252 yield reporter.collectMeasurements();
michael@0 253 do_check_eq(provider.collectDailyCount, 3);
michael@0 254 } finally {
michael@0 255 reporter._shutdown();
michael@0 256 }
michael@0 257 });
michael@0 258
michael@0 259 add_task(function test_remove_old_lastpayload() {
michael@0 260 let reporter = getHealthReporter("remove-old-lastpayload");
michael@0 261 let lastPayloadPath = reporter._state._lastPayloadPath;
michael@0 262 let paths = [lastPayloadPath, lastPayloadPath + ".tmp"];
michael@0 263 let createFiles = function () {
michael@0 264 return Task.spawn(function createFiles() {
michael@0 265 for (let path of paths) {
michael@0 266 yield OS.File.writeAtomic(path, "delete-me", {tmpPath: path + ".tmp"});
michael@0 267 do_check_true(yield OS.File.exists(path));
michael@0 268 }
michael@0 269 });
michael@0 270 };
michael@0 271 try {
michael@0 272 do_check_true(!reporter._state.removedOutdatedLastpayload);
michael@0 273 yield createFiles();
michael@0 274 yield reporter.init();
michael@0 275 for (let path of paths) {
michael@0 276 do_check_false(yield OS.File.exists(path));
michael@0 277 }
michael@0 278 yield reporter._state.save();
michael@0 279 reporter._shutdown();
michael@0 280
michael@0 281 let o = yield CommonUtils.readJSON(reporter._state._filename);
michael@0 282 do_check_true(o.removedOutdatedLastpayload);
michael@0 283
michael@0 284 yield createFiles();
michael@0 285 reporter = getHealthReporter("remove-old-lastpayload");
michael@0 286 yield reporter.init();
michael@0 287 for (let path of paths) {
michael@0 288 do_check_true(yield OS.File.exists(path));
michael@0 289 }
michael@0 290 } finally {
michael@0 291 reporter._shutdown();
michael@0 292 }
michael@0 293 });
michael@0 294
michael@0 295 add_task(function test_json_payload_simple() {
michael@0 296 let reporter = yield getReporter("json_payload_simple");
michael@0 297
michael@0 298 let clientID = reporter._state.clientID;
michael@0 299 do_check_neq(clientID, null);
michael@0 300
michael@0 301 try {
michael@0 302 let now = new Date();
michael@0 303 let payload = yield reporter.getJSONPayload();
michael@0 304 do_check_eq(typeof payload, "string");
michael@0 305 let original = JSON.parse(payload);
michael@0 306
michael@0 307 do_check_eq(original.version, 2);
michael@0 308 do_check_eq(original.thisPingDate, reporter._formatDate(now));
michael@0 309 do_check_eq(original.clientID, clientID);
michael@0 310 do_check_eq(original.clientIDVersion, reporter._state.clientIDVersion);
michael@0 311 do_check_eq(original.clientIDVersion, 1);
michael@0 312 do_check_eq(Object.keys(original.data.last).length, 0);
michael@0 313 do_check_eq(Object.keys(original.data.days).length, 0);
michael@0 314 do_check_false("notInitialized" in original);
michael@0 315
michael@0 316 yield reporter._state.setLastPingDate(
michael@0 317 new Date(now.getTime() - 24 * 60 * 60 * 1000 - 10));
michael@0 318
michael@0 319 original = JSON.parse(yield reporter.getJSONPayload());
michael@0 320 do_check_eq(original.lastPingDate, reporter._formatDate(reporter.lastPingDate));
michael@0 321 do_check_eq(original.clientID, clientID);
michael@0 322
michael@0 323 // This could fail if we cross UTC day boundaries at the exact instance the
michael@0 324 // test is executed. Let's tempt fate.
michael@0 325 do_check_eq(original.thisPingDate, reporter._formatDate(now));
michael@0 326
michael@0 327 payload = yield reporter.getJSONPayload(true);
michael@0 328 do_check_eq(typeof payload, "object");
michael@0 329 } finally {
michael@0 330 reporter._shutdown();
michael@0 331 }
michael@0 332 });
michael@0 333
michael@0 334 add_task(function test_json_payload_dummy_provider() {
michael@0 335 let reporter = yield getReporter("json_payload_dummy_provider");
michael@0 336
michael@0 337 try {
michael@0 338 yield reporter._providerManager.registerProvider(new DummyProvider());
michael@0 339 yield reporter.collectMeasurements();
michael@0 340 let payload = yield reporter.getJSONPayload();
michael@0 341 print(payload);
michael@0 342 let o = JSON.parse(payload);
michael@0 343
michael@0 344 let name = "DummyProvider.DummyMeasurement";
michael@0 345 do_check_eq(Object.keys(o.data.last).length, 1);
michael@0 346 do_check_true(name in o.data.last);
michael@0 347 do_check_eq(o.data.last[name]._v, 1);
michael@0 348 } finally {
michael@0 349 reporter._shutdown();
michael@0 350 }
michael@0 351 });
michael@0 352
michael@0 353 add_task(function test_collect_and_obtain_json_payload() {
michael@0 354 let reporter = yield getReporter("collect_and_obtain_json_payload");
michael@0 355
michael@0 356 try {
michael@0 357 yield reporter._providerManager.registerProvider(new DummyProvider());
michael@0 358 let payload = yield reporter.collectAndObtainJSONPayload();
michael@0 359 do_check_eq(typeof payload, "string");
michael@0 360
michael@0 361 let o = JSON.parse(payload);
michael@0 362 do_check_true("DummyProvider.DummyMeasurement" in o.data.last);
michael@0 363
michael@0 364 payload = yield reporter.collectAndObtainJSONPayload(true);
michael@0 365 do_check_eq(typeof payload, "object");
michael@0 366 } finally {
michael@0 367 reporter._shutdown();
michael@0 368 }
michael@0 369 });
michael@0 370
michael@0 371 // Ensure constant-only providers make their way into the JSON payload.
michael@0 372 add_task(function test_constant_only_providers_in_json_payload() {
michael@0 373 const category = "healthreporter-constant-only-in-payload";
michael@0 374
michael@0 375 let cm = Cc["@mozilla.org/categorymanager;1"]
michael@0 376 .getService(Ci.nsICategoryManager);
michael@0 377 cm.addCategoryEntry(category, "DummyProvider",
michael@0 378 "resource://testing-common/services/metrics/mocks.jsm",
michael@0 379 false, true);
michael@0 380 cm.addCategoryEntry(category, "DummyConstantProvider",
michael@0 381 "resource://testing-common/services/metrics/mocks.jsm",
michael@0 382 false, true);
michael@0 383
michael@0 384 let reporter = yield getReporter("constant_only_providers_in_json_payload");
michael@0 385 try {
michael@0 386 let initCount = reporter._providerManager.providers.length;
michael@0 387 yield reporter._providerManager.registerProvidersFromCategoryManager(category);
michael@0 388
michael@0 389 let payload = yield reporter.collectAndObtainJSONPayload();
michael@0 390 let o = JSON.parse(payload);
michael@0 391 do_check_true("DummyProvider.DummyMeasurement" in o.data.last);
michael@0 392 do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last);
michael@0 393
michael@0 394 let providers = reporter._providerManager.providers;
michael@0 395 do_check_eq(providers.length, initCount + 1);
michael@0 396
michael@0 397 // Do it again for good measure.
michael@0 398 payload = yield reporter.collectAndObtainJSONPayload();
michael@0 399 o = JSON.parse(payload);
michael@0 400 do_check_true("DummyProvider.DummyMeasurement" in o.data.last);
michael@0 401 do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last);
michael@0 402
michael@0 403 providers = reporter._providerManager.providers;
michael@0 404 do_check_eq(providers.length, initCount + 1);
michael@0 405
michael@0 406 // Ensure throwing getJSONPayload is handled properly.
michael@0 407 Object.defineProperty(reporter, "_getJSONPayload", {
michael@0 408 value: function () {
michael@0 409 throw new Error("Silly error.");
michael@0 410 },
michael@0 411 });
michael@0 412
michael@0 413 let deferred = Promise.defer();
michael@0 414
michael@0 415 reporter.collectAndObtainJSONPayload().then(do_throw, function onError() {
michael@0 416 providers = reporter._providerManager.providers;
michael@0 417 do_check_eq(providers.length, initCount + 1);
michael@0 418 deferred.resolve();
michael@0 419 });
michael@0 420
michael@0 421 yield deferred.promise;
michael@0 422 } finally {
michael@0 423 reporter._shutdown();
michael@0 424 }
michael@0 425 });
michael@0 426
michael@0 427 add_task(function test_json_payload_multiple_days() {
michael@0 428 let reporter = yield getReporter("json_payload_multiple_days");
michael@0 429
michael@0 430 try {
michael@0 431 let provider = new DummyProvider();
michael@0 432 yield reporter._providerManager.registerProvider(provider);
michael@0 433
michael@0 434 let now = new Date();
michael@0 435
michael@0 436 let m = provider.getMeasurement("DummyMeasurement", 1);
michael@0 437 for (let i = 0; i < 200; i++) {
michael@0 438 let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY);
michael@0 439 yield m.incrementDailyCounter("daily-counter", date);
michael@0 440 }
michael@0 441
michael@0 442 // This test could fail if we cross a UTC day boundary when running. So,
michael@0 443 // we ensure this doesn't occur.
michael@0 444 Object.defineProperty(reporter, "_now", {
michael@0 445 value: function () {
michael@0 446 return now;
michael@0 447 },
michael@0 448 });
michael@0 449
michael@0 450 let payload = yield reporter.getJSONPayload();
michael@0 451 print(payload);
michael@0 452 let o = JSON.parse(payload);
michael@0 453
michael@0 454 do_check_eq(Object.keys(o.data.days).length, 180);
michael@0 455 let today = reporter._formatDate(now);
michael@0 456 do_check_true(today in o.data.days);
michael@0 457 } finally {
michael@0 458 reporter._shutdown();
michael@0 459 }
michael@0 460 });
michael@0 461
michael@0 462 add_task(function test_json_payload_newer_version_overwrites() {
michael@0 463 let reporter = yield getReporter("json_payload_newer_version_overwrites");
michael@0 464
michael@0 465 try {
michael@0 466 let now = new Date();
michael@0 467 // Instead of hacking up the internals to ensure consistent order in Map
michael@0 468 // iteration (which would be difficult), we instead opt to generate a lot
michael@0 469 // of measurements of different versions and verify their iterable order
michael@0 470 // is not increasing.
michael@0 471 let versions = [1, 6, 3, 9, 2, 3, 7, 4, 10, 8];
michael@0 472 let protos = [];
michael@0 473 for (let version of versions) {
michael@0 474 let m = function () {
michael@0 475 Metrics.Measurement.call(this);
michael@0 476 };
michael@0 477 m.prototype = {
michael@0 478 __proto__: DummyMeasurement.prototype,
michael@0 479 name: "DummyMeasurement",
michael@0 480 version: version,
michael@0 481 };
michael@0 482
michael@0 483 protos.push(m);
michael@0 484 }
michael@0 485
michael@0 486 let ctor = function () {
michael@0 487 Metrics.Provider.call(this);
michael@0 488 };
michael@0 489 ctor.prototype = {
michael@0 490 __proto__: DummyProvider.prototype,
michael@0 491
michael@0 492 name: "MultiMeasurementProvider",
michael@0 493 measurementTypes: protos,
michael@0 494 };
michael@0 495
michael@0 496 let provider = new ctor();
michael@0 497
michael@0 498 yield reporter._providerManager.registerProvider(provider);
michael@0 499
michael@0 500 let haveUnordered = false;
michael@0 501 let last = -1;
michael@0 502 let highestVersion = -1;
michael@0 503 for (let [key, measurement] of provider.measurements) {
michael@0 504 yield measurement.setDailyLastNumeric("daily-last-numeric",
michael@0 505 measurement.version, now);
michael@0 506 yield measurement.setLastNumeric("last-numeric",
michael@0 507 measurement.version, now);
michael@0 508
michael@0 509 if (measurement.version > highestVersion) {
michael@0 510 highestVersion = measurement.version;
michael@0 511 }
michael@0 512
michael@0 513 if (measurement.version < last) {
michael@0 514 haveUnordered = true;
michael@0 515 }
michael@0 516
michael@0 517 last = measurement.version;
michael@0 518 }
michael@0 519
michael@0 520 // Ensure Map traversal isn't ordered. If this ever fails, then we'll need
michael@0 521 // to monkeypatch.
michael@0 522 do_check_true(haveUnordered);
michael@0 523
michael@0 524 let payload = yield reporter.getJSONPayload();
michael@0 525 let o = JSON.parse(payload);
michael@0 526 do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.last);
michael@0 527 do_check_eq(o.data.last["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion);
michael@0 528
michael@0 529 let day = reporter._formatDate(now);
michael@0 530 do_check_true(day in o.data.days);
michael@0 531 do_check_true("MultiMeasurementProvider.DummyMeasurement" in o.data.days[day]);
michael@0 532 do_check_eq(o.data.days[day]["MultiMeasurementProvider.DummyMeasurement"]._v, highestVersion);
michael@0 533
michael@0 534 } finally {
michael@0 535 reporter._shutdown();
michael@0 536 }
michael@0 537 });
michael@0 538
michael@0 539 add_task(function test_idle_daily() {
michael@0 540 let reporter = yield getReporter("idle_daily");
michael@0 541 try {
michael@0 542 let provider = new DummyProvider();
michael@0 543 yield reporter._providerManager.registerProvider(provider);
michael@0 544
michael@0 545 let now = new Date();
michael@0 546 let m = provider.getMeasurement("DummyMeasurement", 1);
michael@0 547 for (let i = 0; i < 200; i++) {
michael@0 548 let date = new Date(now.getTime() - i * MILLISECONDS_PER_DAY);
michael@0 549 yield m.incrementDailyCounter("daily-counter", date);
michael@0 550 }
michael@0 551
michael@0 552 let values = yield m.getValues();
michael@0 553 do_check_eq(values.days.size, 200);
michael@0 554
michael@0 555 Services.obs.notifyObservers(null, "idle-daily", null);
michael@0 556
michael@0 557 values = yield m.getValues();
michael@0 558 do_check_eq(values.days.size, 180);
michael@0 559 } finally {
michael@0 560 reporter._shutdown();
michael@0 561 }
michael@0 562 });
michael@0 563
michael@0 564 add_task(function test_data_submission_transport_failure() {
michael@0 565 let reporter = yield getReporter("data_submission_transport_failure");
michael@0 566 try {
michael@0 567 reporter.serverURI = DUMMY_URI;
michael@0 568 reporter.serverNamespace = "test00";
michael@0 569
michael@0 570 let deferred = Promise.defer();
michael@0 571 let request = new DataSubmissionRequest(deferred, new Date(Date.now + 30000));
michael@0 572 reporter.requestDataUpload(request);
michael@0 573
michael@0 574 yield deferred.promise;
michael@0 575 do_check_eq(request.state, request.SUBMISSION_FAILURE_SOFT);
michael@0 576
michael@0 577 let data = yield getHealthReportProviderValues(reporter, new Date());
michael@0 578 do_check_eq(data.firstDocumentUploadAttempt, 1);
michael@0 579 do_check_eq(data.uploadTransportFailure, 1);
michael@0 580 do_check_eq(Object.keys(data).length, 3);
michael@0 581 } finally {
michael@0 582 reporter._shutdown();
michael@0 583 }
michael@0 584 });
michael@0 585
michael@0 586 add_task(function test_data_submission_server_failure() {
michael@0 587 let [reporter, server] = yield getReporterAndServer("data_submission_server_failure");
michael@0 588 try {
michael@0 589 Object.defineProperty(server, "_handleNamespaceSubmitPost", {
michael@0 590 value: function (ns, id, request, response) {
michael@0 591 throw HTTP_500;
michael@0 592 },
michael@0 593 writable: true,
michael@0 594 });
michael@0 595
michael@0 596 let deferred = Promise.defer();
michael@0 597 let now = new Date();
michael@0 598 let request = new DataSubmissionRequest(deferred, now);
michael@0 599 reporter.requestDataUpload(request);
michael@0 600 yield deferred.promise;
michael@0 601 do_check_eq(request.state, request.SUBMISSION_FAILURE_HARD);
michael@0 602
michael@0 603 let data = yield getHealthReportProviderValues(reporter, now);
michael@0 604 do_check_eq(data.firstDocumentUploadAttempt, 1);
michael@0 605 do_check_eq(data.uploadServerFailure, 1);
michael@0 606 do_check_eq(Object.keys(data).length, 3);
michael@0 607 } finally {
michael@0 608 yield shutdownServer(server);
michael@0 609 reporter._shutdown();
michael@0 610 }
michael@0 611 });
michael@0 612
michael@0 613 add_task(function test_data_submission_success() {
michael@0 614 let [reporter, server] = yield getReporterAndServer("data_submission_success");
michael@0 615 try {
michael@0 616 yield reporter._providerManager.registerProviderFromType(DummyProvider);
michael@0 617 yield reporter._providerManager.registerProviderFromType(DummyConstantProvider);
michael@0 618
michael@0 619 do_check_eq(reporter.lastPingDate.getTime(), 0);
michael@0 620 do_check_false(reporter.haveRemoteData());
michael@0 621
michael@0 622 let deferred = Promise.defer();
michael@0 623
michael@0 624 let now = new Date();
michael@0 625 let request = new DataSubmissionRequest(deferred, now);
michael@0 626 reporter._state.addRemoteID("foo");
michael@0 627 reporter.requestDataUpload(request);
michael@0 628 yield deferred.promise;
michael@0 629 do_check_eq(request.state, request.SUBMISSION_SUCCESS);
michael@0 630 do_check_true(reporter.lastPingDate.getTime() > 0);
michael@0 631 do_check_true(reporter.haveRemoteData());
michael@0 632 for (let remoteID of reporter._state.remoteIDs) {
michael@0 633 do_check_neq(remoteID, "foo");
michael@0 634 }
michael@0 635
michael@0 636 // Ensure data from providers made it to payload.
michael@0 637 let o = yield reporter.getJSONPayload(true);
michael@0 638 do_check_true("DummyProvider.DummyMeasurement" in o.data.last);
michael@0 639 do_check_true("DummyConstantProvider.DummyMeasurement" in o.data.last);
michael@0 640
michael@0 641 let data = yield getHealthReportProviderValues(reporter, now);
michael@0 642 do_check_eq(data.continuationUploadAttempt, 1);
michael@0 643 do_check_eq(data.uploadSuccess, 1);
michael@0 644 do_check_eq(Object.keys(data).length, 3);
michael@0 645
michael@0 646 let d = reporter.lastPingDate;
michael@0 647 let id = reporter.lastSubmitID;
michael@0 648 let clientID = reporter._state.clientID;
michael@0 649
michael@0 650 reporter._shutdown();
michael@0 651
michael@0 652 // Ensure reloading state works.
michael@0 653 reporter = yield getReporter("data_submission_success");
michael@0 654 do_check_eq(reporter.lastSubmitID, id);
michael@0 655 do_check_eq(reporter.lastPingDate.getTime(), d.getTime());
michael@0 656 do_check_eq(reporter._state.clientID, clientID);
michael@0 657
michael@0 658 reporter._shutdown();
michael@0 659 } finally {
michael@0 660 yield shutdownServer(server);
michael@0 661 }
michael@0 662 });
michael@0 663
michael@0 664 add_task(function test_recurring_daily_pings() {
michael@0 665 let [reporter, server] = yield getReporterAndServer("recurring_daily_pings");
michael@0 666 try {
michael@0 667 reporter._providerManager.registerProvider(new DummyProvider());
michael@0 668
michael@0 669 let policy = reporter._policy;
michael@0 670
michael@0 671 defineNow(policy, policy._futureDate(-24 * 60 * 68 * 1000));
michael@0 672 policy.recordUserAcceptance();
michael@0 673 defineNow(policy, policy.nextDataSubmissionDate);
michael@0 674 let promise = policy.checkStateAndTrigger();
michael@0 675 do_check_neq(promise, null);
michael@0 676 yield promise;
michael@0 677
michael@0 678 let lastID = reporter.lastSubmitID;
michael@0 679 do_check_neq(lastID, null);
michael@0 680 do_check_true(server.hasDocument(reporter.serverNamespace, lastID));
michael@0 681
michael@0 682 // Skip forward to next scheduled submission time.
michael@0 683 defineNow(policy, policy.nextDataSubmissionDate);
michael@0 684 promise = policy.checkStateAndTrigger();
michael@0 685 do_check_neq(promise, null);
michael@0 686 yield promise;
michael@0 687 do_check_neq(reporter.lastSubmitID, lastID);
michael@0 688 do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID));
michael@0 689 do_check_false(server.hasDocument(reporter.serverNamespace, lastID));
michael@0 690
michael@0 691 // now() on the health reporter instance wasn't munged. So, we should see
michael@0 692 // both requests attributed to the same day.
michael@0 693 let data = yield getHealthReportProviderValues(reporter, new Date());
michael@0 694 do_check_eq(data.firstDocumentUploadAttempt, 1);
michael@0 695 do_check_eq(data.continuationUploadAttempt, 1);
michael@0 696 do_check_eq(data.uploadSuccess, 2);
michael@0 697 do_check_eq(Object.keys(data).length, 4);
michael@0 698 } finally {
michael@0 699 reporter._shutdown();
michael@0 700 yield shutdownServer(server);
michael@0 701 }
michael@0 702 });
michael@0 703
michael@0 704 add_task(function test_request_remote_data_deletion() {
michael@0 705 let [reporter, server] = yield getReporterAndServer("request_remote_data_deletion");
michael@0 706
michael@0 707 try {
michael@0 708 let policy = reporter._policy;
michael@0 709 defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
michael@0 710 policy.recordUserAcceptance();
michael@0 711 defineNow(policy, policy.nextDataSubmissionDate);
michael@0 712 yield policy.checkStateAndTrigger();
michael@0 713 let id = reporter.lastSubmitID;
michael@0 714 do_check_neq(id, null);
michael@0 715 do_check_true(server.hasDocument(reporter.serverNamespace, id));
michael@0 716
michael@0 717 let clientID = reporter._state.clientID;
michael@0 718 do_check_neq(clientID, null);
michael@0 719
michael@0 720 defineNow(policy, policy._futureDate(10 * 1000));
michael@0 721
michael@0 722 let promise = reporter.requestDeleteRemoteData();
michael@0 723 do_check_neq(promise, null);
michael@0 724 yield promise;
michael@0 725 do_check_null(reporter.lastSubmitID);
michael@0 726 do_check_false(reporter.haveRemoteData());
michael@0 727 do_check_false(server.hasDocument(reporter.serverNamespace, id));
michael@0 728
michael@0 729 // Client ID should be updated.
michael@0 730 do_check_neq(reporter._state.clientID, null);
michael@0 731 do_check_neq(reporter._state.clientID, clientID);
michael@0 732 do_check_eq(reporter._state.clientIDVersion, 1);
michael@0 733
michael@0 734 // And it should be persisted to disk.
michael@0 735 let o = yield CommonUtils.readJSON(reporter._state._filename);
michael@0 736 do_check_eq(o.clientID, reporter._state.clientID);
michael@0 737 do_check_eq(o.clientIDVersion, 1);
michael@0 738 } finally {
michael@0 739 reporter._shutdown();
michael@0 740 yield shutdownServer(server);
michael@0 741 }
michael@0 742 });
michael@0 743
michael@0 744 add_task(function test_multiple_simultaneous_uploads() {
michael@0 745 let [reporter, server] = yield getReporterAndServer("multiple_simultaneous_uploads");
michael@0 746
michael@0 747 try {
michael@0 748 let d1 = Promise.defer();
michael@0 749 let d2 = Promise.defer();
michael@0 750 let t1 = new Date(Date.now() - 1000);
michael@0 751 let t2 = new Date(t1.getTime() + 500);
michael@0 752 let r1 = new DataSubmissionRequest(d1, t1);
michael@0 753 let r2 = new DataSubmissionRequest(d2, t2);
michael@0 754
michael@0 755 let getPayloadDeferred = Promise.defer();
michael@0 756
michael@0 757 Object.defineProperty(reporter, "getJSONPayload", {
michael@0 758 configurable: true,
michael@0 759 value: () => {
michael@0 760 getPayloadDeferred.resolve();
michael@0 761 delete reporter["getJSONPayload"];
michael@0 762 return reporter.getJSONPayload();
michael@0 763 },
michael@0 764 });
michael@0 765
michael@0 766 let p1 = reporter.requestDataUpload(r1);
michael@0 767 yield getPayloadDeferred.promise;
michael@0 768 do_check_true(reporter._uploadInProgress);
michael@0 769 let p2 = reporter.requestDataUpload(r2);
michael@0 770
michael@0 771 yield p1;
michael@0 772 yield p2;
michael@0 773
michael@0 774 do_check_eq(r1.state, r1.SUBMISSION_SUCCESS);
michael@0 775 do_check_eq(r2.state, r2.UPLOAD_IN_PROGRESS);
michael@0 776
michael@0 777 // They should both be resolved already.
michael@0 778 yield d1;
michael@0 779 yield d2;
michael@0 780
michael@0 781 let data = yield getHealthReportProviderValues(reporter, t1);
michael@0 782 do_check_eq(data.firstDocumentUploadAttempt, 1);
michael@0 783 do_check_false("continuationUploadAttempt" in data);
michael@0 784 do_check_eq(data.uploadSuccess, 1);
michael@0 785 do_check_eq(data.uploadAlreadyInProgress, 1);
michael@0 786 } finally {
michael@0 787 reporter._shutdown();
michael@0 788 yield shutdownServer(server);
michael@0 789 }
michael@0 790 });
michael@0 791
michael@0 792 add_task(function test_policy_accept_reject() {
michael@0 793 let [reporter, server] = yield getReporterAndServer("policy_accept_reject");
michael@0 794
michael@0 795 try {
michael@0 796 let policy = reporter._policy;
michael@0 797
michael@0 798 do_check_false(policy.dataSubmissionPolicyAccepted);
michael@0 799 do_check_false(reporter.willUploadData);
michael@0 800
michael@0 801 policy.recordUserAcceptance();
michael@0 802 do_check_true(policy.dataSubmissionPolicyAccepted);
michael@0 803 do_check_true(reporter.willUploadData);
michael@0 804
michael@0 805 policy.recordUserRejection();
michael@0 806 do_check_false(policy.dataSubmissionPolicyAccepted);
michael@0 807 do_check_false(reporter.willUploadData);
michael@0 808 } finally {
michael@0 809 reporter._shutdown();
michael@0 810 yield shutdownServer(server);
michael@0 811 }
michael@0 812 });
michael@0 813
michael@0 814 add_task(function test_error_message_scrubbing() {
michael@0 815 let reporter = yield getReporter("error_message_scrubbing");
michael@0 816
michael@0 817 try {
michael@0 818 let profile = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
michael@0 819 reporter._recordError("Foo " + profile);
michael@0 820
michael@0 821 do_check_eq(reporter._errors.length, 1);
michael@0 822 do_check_eq(reporter._errors[0], "Foo <ProfilePath>");
michael@0 823
michael@0 824 reporter._errors = [];
michael@0 825
michael@0 826 let appdata = Services.dirsvc.get("UAppData", Ci.nsIFile);
michael@0 827 let uri = Services.io.newFileURI(appdata);
michael@0 828
michael@0 829 reporter._recordError("Foo " + uri.spec);
michael@0 830 do_check_eq(reporter._errors[0], "Foo <AppDataURI>");
michael@0 831 } finally {
michael@0 832 reporter._shutdown();
michael@0 833 }
michael@0 834 });
michael@0 835
michael@0 836 add_task(function test_basic_appinfo() {
michael@0 837 function verify(d) {
michael@0 838 do_check_eq(d["_v"], 1);
michael@0 839 do_check_eq(d._v, 1);
michael@0 840 do_check_eq(d.vendor, "Mozilla");
michael@0 841 do_check_eq(d.name, "xpcshell");
michael@0 842 do_check_eq(d.id, "xpcshell@tests.mozilla.org");
michael@0 843 do_check_eq(d.version, "1");
michael@0 844 do_check_eq(d.appBuildID, "20121107");
michael@0 845 do_check_eq(d.platformVersion, "p-ver");
michael@0 846 do_check_eq(d.platformBuildID, "20121106");
michael@0 847 do_check_eq(d.os, "XPCShell");
michael@0 848 do_check_eq(d.xpcomabi, "noarch-spidermonkey");
michael@0 849 do_check_true("updateChannel" in d);
michael@0 850 }
michael@0 851 let reporter = yield getReporter("basic_appinfo");
michael@0 852 try {
michael@0 853 verify(reporter.obtainAppInfo());
michael@0 854 let payload = yield reporter.collectAndObtainJSONPayload(true);
michael@0 855 do_check_eq(payload["version"], 2);
michael@0 856 verify(payload["geckoAppInfo"]);
michael@0 857 } finally {
michael@0 858 reporter._shutdown();
michael@0 859 }
michael@0 860 });
michael@0 861
michael@0 862 // Ensure collection occurs if upload is disabled.
michael@0 863 add_task(function test_collect_when_upload_disabled() {
michael@0 864 let reporter = getHealthReporter("collect_when_upload_disabled");
michael@0 865 reporter._policy.recordHealthReportUploadEnabled(false, "testing-collect");
michael@0 866 do_check_false(reporter._policy.healthReportUploadEnabled);
michael@0 867
michael@0 868 let name = "healthreport-testing-collect_when_upload_disabled-healthreport-lastDailyCollection";
michael@0 869 let pref = "app.update.lastUpdateTime." + name;
michael@0 870 do_check_false(Services.prefs.prefHasUserValue(pref));
michael@0 871
michael@0 872 try {
michael@0 873 yield reporter.init();
michael@0 874 do_check_true(Services.prefs.prefHasUserValue(pref));
michael@0 875
michael@0 876 // We would ideally ensure the timer fires and does the right thing.
michael@0 877 // However, testing the update timer manager is quite involved.
michael@0 878 } finally {
michael@0 879 reporter._shutdown();
michael@0 880 }
michael@0 881 });
michael@0 882
michael@0 883 add_task(function test_failure_if_not_initialized() {
michael@0 884 let reporter = yield getReporter("failure_if_not_initialized");
michael@0 885 reporter._shutdown();
michael@0 886
michael@0 887 let error = false;
michael@0 888 try {
michael@0 889 yield reporter.requestDataUpload();
michael@0 890 } catch (ex) {
michael@0 891 error = true;
michael@0 892 do_check_true(ex.message.contains("Not initialized."));
michael@0 893 } finally {
michael@0 894 do_check_true(error);
michael@0 895 error = false;
michael@0 896 }
michael@0 897
michael@0 898 try {
michael@0 899 yield reporter.collectMeasurements();
michael@0 900 } catch (ex) {
michael@0 901 error = true;
michael@0 902 do_check_true(ex.message.contains("Not initialized."));
michael@0 903 } finally {
michael@0 904 do_check_true(error);
michael@0 905 error = false;
michael@0 906 }
michael@0 907
michael@0 908 // getJSONPayload always works (to facilitate error upload).
michael@0 909 yield reporter.getJSONPayload();
michael@0 910 });
michael@0 911
michael@0 912 add_task(function test_upload_on_init_failure() {
michael@0 913 let server = new BagheeraServer();
michael@0 914 server.start();
michael@0 915 let reporter = yield getHealthReporter("upload_on_init_failure", server.serverURI, true);
michael@0 916 server.createNamespace(reporter.serverNamespace);
michael@0 917
michael@0 918 reporter.onInitializeProviderManagerFinished = function () {
michael@0 919 throw new Error("Fake error during provider manager initialization.");
michael@0 920 };
michael@0 921
michael@0 922 let deferred = Promise.defer();
michael@0 923
michael@0 924 let oldOnResult = reporter._onBagheeraResult;
michael@0 925 Object.defineProperty(reporter, "_onBagheeraResult", {
michael@0 926 value: function (request, isDelete, date, result) {
michael@0 927 do_check_false(isDelete);
michael@0 928 do_check_true(result.transportSuccess);
michael@0 929 do_check_true(result.serverSuccess);
michael@0 930
michael@0 931 oldOnResult.call(reporter, request, isDelete, new Date(), result);
michael@0 932 deferred.resolve();
michael@0 933 },
michael@0 934 });
michael@0 935
michael@0 936 reporter._policy.recordUserAcceptance();
michael@0 937 let error = false;
michael@0 938 try {
michael@0 939 yield reporter.init();
michael@0 940 } catch (ex) {
michael@0 941 error = true;
michael@0 942 } finally {
michael@0 943 do_check_true(error);
michael@0 944 }
michael@0 945
michael@0 946 // At this point the emergency upload should have been initiated. We
michael@0 947 // wait for our monkeypatched response handler to signal request
michael@0 948 // completion.
michael@0 949 yield deferred.promise;
michael@0 950
michael@0 951 do_check_true(server.hasDocument(reporter.serverNamespace, reporter.lastSubmitID));
michael@0 952 let doc = server.getDocument(reporter.serverNamespace, reporter.lastSubmitID);
michael@0 953 do_check_true("notInitialized" in doc);
michael@0 954 do_check_eq(doc.notInitialized, 1);
michael@0 955 do_check_true("errors" in doc);
michael@0 956 do_check_eq(doc.errors.length, 1);
michael@0 957 do_check_true(doc.errors[0].contains("Fake error during provider manager initialization"));
michael@0 958
michael@0 959 reporter._shutdown();
michael@0 960 yield shutdownServer(server);
michael@0 961 });
michael@0 962
michael@0 963 add_task(function test_state_prefs_conversion_simple() {
michael@0 964 let reporter = getHealthReporter("state_prefs_conversion");
michael@0 965 let prefs = reporter._prefs;
michael@0 966
michael@0 967 let lastSubmit = new Date();
michael@0 968 prefs.set("lastSubmitID", "lastID");
michael@0 969 CommonUtils.setDatePref(prefs, "lastPingTime", lastSubmit);
michael@0 970
michael@0 971 try {
michael@0 972 yield reporter.init();
michael@0 973
michael@0 974 do_check_eq(reporter._state.lastSubmitID, "lastID");
michael@0 975 do_check_eq(reporter._state.remoteIDs.length, 1);
michael@0 976 do_check_eq(reporter._state.lastPingDate.getTime(), lastSubmit.getTime());
michael@0 977 do_check_eq(reporter._state.lastPingDate.getTime(), reporter.lastPingDate.getTime());
michael@0 978 do_check_eq(reporter._state.lastSubmitID, reporter.lastSubmitID);
michael@0 979 do_check_true(reporter.haveRemoteData());
michael@0 980
michael@0 981 // User set preferences should have been wiped out.
michael@0 982 do_check_false(prefs.isSet("lastSubmitID"));
michael@0 983 do_check_false(prefs.isSet("lastPingTime"));
michael@0 984 } finally {
michael@0 985 reporter._shutdown();
michael@0 986 }
michael@0 987 });
michael@0 988
michael@0 989 // If the saved JSON file does not contain an object, we should reset
michael@0 990 // automatically.
michael@0 991 add_task(function test_state_no_json_object() {
michael@0 992 let reporter = getHealthReporter("state_shared");
michael@0 993 yield CommonUtils.writeJSON("hello", reporter._state._filename);
michael@0 994
michael@0 995 try {
michael@0 996 yield reporter.init();
michael@0 997
michael@0 998 do_check_eq(reporter.lastPingDate.getTime(), 0);
michael@0 999 do_check_null(reporter.lastSubmitID);
michael@0 1000
michael@0 1001 let o = yield CommonUtils.readJSON(reporter._state._filename);
michael@0 1002 do_check_eq(typeof(o), "object");
michael@0 1003 do_check_eq(o.v, 1);
michael@0 1004 do_check_eq(o.lastPingTime, 0);
michael@0 1005 do_check_eq(o.remoteIDs.length, 0);
michael@0 1006 } finally {
michael@0 1007 reporter._shutdown();
michael@0 1008 }
michael@0 1009 });
michael@0 1010
michael@0 1011 // If we encounter a future version, we reset state to the current version.
michael@0 1012 add_task(function test_state_future_version() {
michael@0 1013 let reporter = getHealthReporter("state_shared");
michael@0 1014 yield CommonUtils.writeJSON({v: 2, remoteIDs: ["foo"], lastPingTime: 2412},
michael@0 1015 reporter._state._filename);
michael@0 1016 try {
michael@0 1017 yield reporter.init();
michael@0 1018
michael@0 1019 do_check_eq(reporter.lastPingDate.getTime(), 0);
michael@0 1020 do_check_null(reporter.lastSubmitID);
michael@0 1021
michael@0 1022 // While the object is updated, we don't save the file.
michael@0 1023 let o = yield CommonUtils.readJSON(reporter._state._filename);
michael@0 1024 do_check_eq(o.v, 2);
michael@0 1025 do_check_eq(o.lastPingTime, 2412);
michael@0 1026 do_check_eq(o.remoteIDs.length, 1);
michael@0 1027 } finally {
michael@0 1028 reporter._shutdown();
michael@0 1029 }
michael@0 1030 });
michael@0 1031
michael@0 1032 // Test recovery if the state file contains invalid JSON.
michael@0 1033 add_task(function test_state_invalid_json() {
michael@0 1034 let reporter = getHealthReporter("state_shared");
michael@0 1035
michael@0 1036 let encoder = new TextEncoder();
michael@0 1037 let arr = encoder.encode("{foo: bad value, 'bad': as2,}");
michael@0 1038 let path = reporter._state._filename;
michael@0 1039 yield OS.File.writeAtomic(path, arr, {tmpPath: path + ".tmp"});
michael@0 1040
michael@0 1041 try {
michael@0 1042 yield reporter.init();
michael@0 1043
michael@0 1044 do_check_eq(reporter.lastPingDate.getTime(), 0);
michael@0 1045 do_check_null(reporter.lastSubmitID);
michael@0 1046 } finally {
michael@0 1047 reporter._shutdown();
michael@0 1048 }
michael@0 1049 });
michael@0 1050
michael@0 1051 add_task(function test_state_multiple_remote_ids() {
michael@0 1052 let [reporter, server] = yield getReporterAndServer("state_multiple_remote_ids");
michael@0 1053 let documents = [
michael@0 1054 [reporter.serverNamespace, "one", "{v:1}"],
michael@0 1055 [reporter.serverNamespace, "two", "{v:2}"],
michael@0 1056 ];
michael@0 1057 let now = new Date(Date.now() - 5000);
michael@0 1058
michael@0 1059 try {
michael@0 1060 for (let [ns, id, payload] of documents) {
michael@0 1061 server.setDocument(ns, id, payload);
michael@0 1062 do_check_true(server.hasDocument(ns, id));
michael@0 1063 yield reporter._state.addRemoteID(id);
michael@0 1064 do_check_eq(reporter._state.remoteIDs.indexOf(id), reporter._state.remoteIDs.length - 1);
michael@0 1065 }
michael@0 1066 yield reporter._state.setLastPingDate(now);
michael@0 1067 do_check_eq(reporter._state.remoteIDs.length, 2);
michael@0 1068 do_check_eq(reporter.lastSubmitID, documents[0][1]);
michael@0 1069
michael@0 1070 let deferred = Promise.defer();
michael@0 1071 let request = new DataSubmissionRequest(deferred, now);
michael@0 1072 reporter.requestDataUpload(request);
michael@0 1073 yield deferred.promise;
michael@0 1074
michael@0 1075 do_check_eq(reporter._state.remoteIDs.length, 1);
michael@0 1076 for (let [,id,] of documents) {
michael@0 1077 do_check_eq(reporter._state.remoteIDs.indexOf(id), -1);
michael@0 1078 do_check_false(server.hasDocument(reporter.serverNamespace, id));
michael@0 1079 }
michael@0 1080 do_check_true(reporter.lastPingDate.getTime() > now.getTime());
michael@0 1081
michael@0 1082 let o = yield CommonUtils.readJSON(reporter._state._filename);
michael@0 1083 do_check_eq(o.remoteIDs.length, 1);
michael@0 1084 do_check_eq(o.remoteIDs[0], reporter._state.remoteIDs[0]);
michael@0 1085 do_check_eq(o.lastPingTime, reporter.lastPingDate.getTime());
michael@0 1086 } finally {
michael@0 1087 yield shutdownServer(server);
michael@0 1088 reporter._shutdown();
michael@0 1089 }
michael@0 1090 });
michael@0 1091
michael@0 1092 // If we have a state file then downgrade to prefs, the prefs should be
michael@0 1093 // reimported and should supplement existing state.
michael@0 1094 add_task(function test_state_downgrade_upgrade() {
michael@0 1095 let reporter = getHealthReporter("state_shared");
michael@0 1096
michael@0 1097 let now = new Date();
michael@0 1098
michael@0 1099 yield CommonUtils.writeJSON({v: 1, remoteIDs: ["id1", "id2"], lastPingTime: now.getTime()},
michael@0 1100 reporter._state._filename);
michael@0 1101
michael@0 1102 let prefs = reporter._prefs;
michael@0 1103 prefs.set("lastSubmitID", "prefID");
michael@0 1104 prefs.set("lastPingTime", "" + (now.getTime() + 1000));
michael@0 1105
michael@0 1106 try {
michael@0 1107 yield reporter.init();
michael@0 1108
michael@0 1109 do_check_eq(reporter.lastSubmitID, "id1");
michael@0 1110 do_check_eq(reporter._state.remoteIDs.length, 3);
michael@0 1111 do_check_eq(reporter._state.remoteIDs[2], "prefID");
michael@0 1112 do_check_eq(reporter.lastPingDate.getTime(), now.getTime() + 1000);
michael@0 1113 do_check_false(prefs.isSet("lastSubmitID"));
michael@0 1114 do_check_false(prefs.isSet("lastPingTime"));
michael@0 1115
michael@0 1116 let o = yield CommonUtils.readJSON(reporter._state._filename);
michael@0 1117 do_check_eq(o.remoteIDs.length, 3);
michael@0 1118 do_check_eq(o.lastPingTime, now.getTime() + 1000);
michael@0 1119 } finally {
michael@0 1120 reporter._shutdown();
michael@0 1121 }
michael@0 1122 });
michael@0 1123
michael@0 1124 // Missing client ID in state should be created on state load.
michael@0 1125 add_task(function* test_state_create_client_id() {
michael@0 1126 let reporter = getHealthReporter("state_create_client_id");
michael@0 1127
michael@0 1128 yield CommonUtils.writeJSON({
michael@0 1129 v: 1,
michael@0 1130 remoteIDs: ["id1", "id2"],
michael@0 1131 lastPingTime: Date.now(),
michael@0 1132 removeOutdatedLastPayload: true,
michael@0 1133 }, reporter._state._filename);
michael@0 1134
michael@0 1135 try {
michael@0 1136 yield reporter.init();
michael@0 1137
michael@0 1138 do_check_eq(reporter.lastSubmitID, "id1");
michael@0 1139 do_check_neq(reporter._state.clientID, null);
michael@0 1140 do_check_eq(reporter._state.clientID.length, 36);
michael@0 1141 do_check_eq(reporter._state.clientIDVersion, 1);
michael@0 1142
michael@0 1143 let clientID = reporter._state.clientID;
michael@0 1144
michael@0 1145 // The client ID should be persisted as soon as it is created.
michael@0 1146 reporter._shutdown();
michael@0 1147
michael@0 1148 reporter = getHealthReporter("state_create_client_id");
michael@0 1149 yield reporter.init();
michael@0 1150 do_check_eq(reporter._state.clientID, clientID);
michael@0 1151 } finally {
michael@0 1152 reporter._shutdown();
michael@0 1153 }
michael@0 1154 });
michael@0 1155
michael@0 1156 // Invalid stored client ID is reset automatically.
michael@0 1157 add_task(function* test_empty_client_id() {
michael@0 1158 let reporter = getHealthReporter("state_empty_client_id");
michael@0 1159
michael@0 1160 yield CommonUtils.writeJSON({
michael@0 1161 v: 1,
michael@0 1162 clientID: "",
michael@0 1163 remoteIDs: ["id1", "id2"],
michael@0 1164 lastPingTime: Date.now(),
michael@0 1165 removeOutdatedLastPayload: true,
michael@0 1166 }, reporter._state._filename);
michael@0 1167
michael@0 1168 try {
michael@0 1169 yield reporter.init();
michael@0 1170
michael@0 1171 do_check_neq(reporter._state.clientID, null);
michael@0 1172 do_check_eq(reporter._state.clientID.length, 36);
michael@0 1173 } finally {
michael@0 1174 reporter._shutdown();
michael@0 1175 }
michael@0 1176 });
michael@0 1177
michael@0 1178 add_task(function* test_nonstring_client_id() {
michael@0 1179 let reporter = getHealthReporter("state_nonstring_client_id");
michael@0 1180
michael@0 1181 yield CommonUtils.writeJSON({
michael@0 1182 v: 1,
michael@0 1183 clientID: 42,
michael@0 1184 remoteIDs: ["id1", "id2"],
michael@0 1185 lastPingTime: Date.now(),
michael@0 1186 remoteOutdatedLastPayload: true,
michael@0 1187 }, reporter._state._filename);
michael@0 1188
michael@0 1189 try {
michael@0 1190 yield reporter.init();
michael@0 1191
michael@0 1192 do_check_neq(reporter._state.clientID, null);
michael@0 1193 do_check_eq(reporter._state.clientID.length, 36);
michael@0 1194 } finally {
michael@0 1195 reporter._shutdown();
michael@0 1196 }
michael@0 1197 });

mercurial