Bug 849353 - Add basic app info section to top level of FHR payload. r=gps
authorRichard Newman <rnewman@mozilla.com>
Thu, 14 Mar 2013 11:24:12 -0700
changeset 124792 9db3cfddc9be4fd5821644b8c0e05baa366c067f
parent 124791 7036707114197511489a9e080bcc41f3ed10c07e
child 124793 e542dc7d650114e18c49b5cbc0364274e718f6b2
push id24435
push userrnewman@mozilla.com
push dateThu, 14 Mar 2013 22:31:19 +0000
treeherdermozilla-central@0f7261e288f2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs849353
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 849353 - Add basic app info section to top level of FHR payload. r=gps
services/healthreport/healthreporter.jsm
services/healthreport/tests/xpcshell/test_healthreporter.js
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -23,16 +23,18 @@ Cu.import("resource://services-common/pr
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 // Oldest year to allow in date preferences. This module was implemented in
 // 2012 and no dates older than that should be encountered.
 const OLDEST_ALLOWED_YEAR = 2012;
 
 const DAYS_IN_PAYLOAD = 180;
 
 const DEFAULT_DATABASE_NAME = "healthreport.sqlite";
@@ -576,18 +578,19 @@ AbstractHealthReporter.prototype = Objec
     return deferred.promise;
   },
 
   _getJSONPayload: function (now, asObject=false) {
     let pingDateString = this._formatDate(now);
     this._log.info("Producing JSON payload for " + pingDateString);
 
     let o = {
-      version: 1,
+      version: 2,
       thisPingDate: pingDateString,
+      geckoAppInfo: this.obtainAppInfo(this._log),
       data: {last: {}, days: {}},
     };
 
     let outputDataDays = o.data.days;
 
     // Guard here in case we don't track this (e.g., on Android).
     let lastPingDate = this.lastPingDate;
     if (lastPingDate && lastPingDate.getTime() > 0) {
@@ -749,16 +752,62 @@ AbstractHealthReporter.prototype = Objec
         return Promise.reject(error);
       }
     );
   },
 
   _now: function _now() {
     return new Date();
   },
+
+  // These are stolen from AppInfoProvider.
+  appInfoVersion: 1,
+  appInfoFields: {
+    // From nsIXULAppInfo.
+    vendor: "vendor",
+    name: "name",
+    id: "ID",
+    version: "version",
+    appBuildID: "appBuildID",
+    platformVersion: "platformVersion",
+    platformBuildID: "platformBuildID",
+
+    // From nsIXULRuntime.
+    os: "OS",
+    xpcomabi: "XPCOMABI",
+  },
+
+  /**
+   * Statically return a bundle of app info data, a subset of that produced by
+   * AppInfoProvider._populateConstants. This allows us to more usefully handle
+   * payloads that, due to error, contain no data.
+   *
+   * Returns a very sparse object if Services.appinfo is unavailable.
+   */
+  obtainAppInfo: function () {
+    let out = {"_v": this.appInfoVersion};
+    try {
+      let ai = Services.appinfo;
+      for (let [k, v] in Iterator(this.appInfoFields)) {
+        out[k] = ai[v];
+      }
+    } catch (ex) {
+      this._log.warn("Could not obtain Services.appinfo: " +
+                     CommonUtils.exceptionStr(ex));
+    }
+
+    try {
+      out["updateChannel"] = UpdateChannel.get();
+    } catch (ex) {
+      this._log.warn("Could not obtain update channel: " +
+                     CommonUtils.exceptionStr(ex));
+    }
+
+    return out;
+  },
 });
 
 /**
  * HealthReporter and its abstract superclass coordinate collection and
  * submission of health report metrics.
  *
  * This is the main type for Firefox Health Report on desktop. It glues all the
  * lower-level components (such as collection and submission) together.
--- a/services/healthreport/tests/xpcshell/test_healthreporter.js
+++ b/services/healthreport/tests/xpcshell/test_healthreporter.js
@@ -246,17 +246,17 @@ add_task(function test_json_payload_simp
   let reporter = yield getReporter("json_payload_simple");
 
   try {
     let now = new Date();
     let payload = yield reporter.getJSONPayload();
     do_check_eq(typeof payload, "string");
     let original = JSON.parse(payload);
 
-    do_check_eq(original.version, 1);
+    do_check_eq(original.version, 2);
     do_check_eq(original.thisPingDate, reporter._formatDate(now));
     do_check_eq(Object.keys(original.data.last).length, 0);
     do_check_eq(Object.keys(original.data.days).length, 0);
 
     reporter.lastPingDate = new Date(now.getTime() - 24 * 60 * 60 * 1000 - 10);
 
     original = JSON.parse(yield reporter.getJSONPayload());
     do_check_eq(original.lastPingDate, reporter._formatDate(reporter.lastPingDate));
@@ -580,8 +580,33 @@ add_task(function test_error_message_scr
 
     reporter._recordError("Foo " + uri.spec);
     do_check_eq(reporter._errors[0], "Foo <AppDataURI>");
   } finally {
     reporter._shutdown();
   }
 });
 
+add_task(function test_basic_appinfo() {
+  function verify(d) {
+    do_check_eq(d["_v"], 1);
+    do_check_eq(d._v, 1);
+    do_check_eq(d.vendor, "Mozilla");
+    do_check_eq(d.name, "xpcshell");
+    do_check_eq(d.id, "xpcshell@tests.mozilla.org");
+    do_check_eq(d.version, "1");
+    do_check_eq(d.appBuildID, "20121107");
+    do_check_eq(d.platformVersion, "p-ver");
+    do_check_eq(d.platformBuildID, "20121106");
+    do_check_eq(d.os, "XPCShell");
+    do_check_eq(d.xpcomabi, "noarch-spidermonkey");
+    do_check_true("updateChannel" in d);
+  }
+  let reporter = yield getReporter("basic_appinfo");
+  try {
+    verify(reporter.obtainAppInfo());
+    let payload = yield reporter.collectAndObtainJSONPayload(true);
+    do_check_eq(payload["version"], 2);
+    verify(payload["geckoAppInfo"]);
+  } finally {
+    reporter._shutdown();
+  }
+});