services/healthreport/tests/xpcshell/test_healthreporter.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

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

mercurial