Bug 1059186 - Client needs to report number of generated URLs via Telemetry. r=MattN
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Thu, 18 Sep 2014 13:05:39 +0100
changeset 225375 2d38bdaef1d42c733eae6afa3a55bfd49943eddf
parent 225374 576d969adba1e9c57f97ec196716556a574adf7b
child 225376 1277219be1ca7ed6245560a38008097d14f5dedd
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1059186
milestone34.0a2
Bug 1059186 - Client needs to report number of generated URLs via Telemetry. r=MattN
browser/components/loop/MozLoopAPI.jsm
browser/components/loop/content/js/client.js
browser/components/loop/test/desktop-local/client_test.js
browser/components/loop/test/mochitest/browser.ini
browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
toolkit/components/telemetry/Histograms.json
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -469,17 +469,31 @@ function injectLoopAPI(targetWindow) {
     composeEmail: {
       enumerable: true,
       writable: true,
       value: function(subject, body) {
         let mailtoURL = "mailto:?subject=" + encodeURIComponent(subject) + "&" +
                         "body=" + encodeURIComponent(body);
         extProtocolSvc.loadURI(CommonUtils.makeURI(mailtoURL));
       }
-    }
+    },
+
+    /**
+     * Adds a value to a telemetry histogram.
+     *
+     * @param  {string}  histogramId Name of the telemetry histogram to update.
+     * @param  {integer} value       Value to add to the histogram.
+     */
+    telemetryAdd: {
+      enumerable: true,
+      writable: true,
+      value: function(histogramId, value) {
+        Services.telemetry.getHistogramById(histogramId).add(value);
+      }
+    },
   };
 
   function onStatusChanged(aSubject, aTopic, aData) {
     let event = new targetWindow.CustomEvent("LoopStatusChanged");
     targetWindow.dispatchEvent(event)
   };
 
   function onDOMWindowDestroyed(aSubject, aTopic, aData) {
--- a/browser/components/loop/content/js/client.js
+++ b/browser/components/loop/content/js/client.js
@@ -106,25 +106,32 @@ loop.Client = (function($) {
      *
      * @param  {string} nickname the nickname of the future caller
      * @param  {Function} cb Callback(err, callUrlData)
      */
     _requestCallUrlInternal: function(nickname, cb) {
       this.mozLoop.hawkRequest("/call-url/", "POST", {callerId: nickname},
                                function (error, responseText) {
         if (error) {
+          this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", false);
           this._failureHandler(cb, error);
           return;
         }
 
         try {
           var urlData = JSON.parse(responseText);
 
-          cb(null, this._validate(urlData, expectedCallUrlProperties));
+          // This throws if the data is invalid, in which case only the failure
+          // telementry will be recorded.
+          var returnData = this._validate(urlData, expectedCallUrlProperties);
+
+          this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", true);
+          cb(null, returnData);
         } catch (err) {
+          this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", false);
           console.log("Error requesting call info", err);
           cb(err);
         }
       }.bind(this));
     },
 
     /**
      * Block call URL based on the token identifier
@@ -182,12 +189,26 @@ loop.Client = (function($) {
         if (err) {
           cb(err);
           return;
         }
 
         this._requestCallUrlInternal(nickname, cb);
       }.bind(this));
     },
+
+    /**
+     * Adds a value to a telemetry histogram, ignoring errors.
+     *
+     * @param  {string}  histogramId Name of the telemetry histogram to update.
+     * @param  {integer} value       Value to add to the histogram.
+     */
+    _telemetryAdd: function(histogramId, value) {
+      try {
+        this.mozLoop.telemetryAdd(histogramId, value);
+      } catch (err) {
+        console.error("Error recording telemetry", err);
+      }
+    },
   };
 
   return Client;
 })(jQuery);
--- a/browser/components/loop/test/desktop-local/client_test.js
+++ b/browser/components/loop/test/desktop-local/client_test.js
@@ -29,17 +29,18 @@ describe("loop.Client", function() {
     fakeToken = "fakeTokenText";
     mozLoop = {
       getLoopCharPref: sandbox.stub()
         .returns(null)
         .withArgs("hawk-session-token")
         .returns(fakeToken),
       ensureRegistered: sinon.stub().callsArgWith(0, null),
       noteCallUrlExpiry: sinon.spy(),
-      hawkRequest: sinon.stub()
+      hawkRequest: sinon.stub(),
+      telemetryAdd: sinon.spy(),
     };
     // Alias for clearer tests.
     hawkRequestStub = mozLoop.hawkRequest;
     client = new loop.Client({
       mozLoop: mozLoop
     });
   });
 
@@ -151,16 +152,40 @@ describe("loop.Client", function() {
           hawkRequestStub.callsArgWith(3, null,
             JSON.stringify(callUrlData));
 
           client.requestCallUrl("foo", callback);
 
           sinon.assert.notCalled(mozLoop.noteCallUrlExpiry);
         });
 
+      it("should call mozLoop.telemetryAdd when the request succeeds",
+        function(done) {
+          var callUrlData = {
+            "callUrl": "fakeCallUrl",
+            "expiresAt": 60
+          };
+
+          // Sets up the hawkRequest stub to trigger the callback with no error
+          // and the url.
+          hawkRequestStub.callsArgWith(3, null,
+            JSON.stringify(callUrlData));
+
+          client.requestCallUrl("foo", function(err) {
+            expect(err).to.be.null;
+
+            sinon.assert.calledOnce(mozLoop.telemetryAdd);
+            sinon.assert.calledWith(mozLoop.telemetryAdd,
+                                    "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS",
+                                    true);
+
+            done();
+          });
+        });
+
       it("should send an error when the request fails", function() {
         // Sets up the hawkRequest stub to trigger the callback with
         // an error
         hawkRequestStub.callsArgWith(3, fakeErrorRes);
 
         client.requestCallUrl("foo", callback);
 
         sinon.assert.calledOnce(callback);
@@ -176,11 +201,29 @@ describe("loop.Client", function() {
 
         client.requestCallUrl("foo", callback);
 
         sinon.assert.calledOnce(callback);
         sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
           return /Invalid data received/.test(err.message);
         }));
       });
+
+      it("should call mozLoop.telemetryAdd when the request fails",
+        function(done) {
+          // Sets up the hawkRequest stub to trigger the callback with
+          // an error
+          hawkRequestStub.callsArgWith(3, fakeErrorRes);
+
+          client.requestCallUrl("foo", function(err) {
+            expect(err).not.to.be.null;
+
+            sinon.assert.calledOnce(mozLoop.telemetryAdd);
+            sinon.assert.calledWith(mozLoop.telemetryAdd,
+                                    "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS",
+                                    false);
+
+            done();
+          });
+        });
     });
   });
 });
--- a/browser/components/loop/test/mochitest/browser.ini
+++ b/browser/components/loop/test/mochitest/browser.ini
@@ -11,8 +11,10 @@ support-files =
 [browser_LoopContacts.js]
 [browser_mozLoop_appVersionInfo.js]
 [browser_mozLoop_prefs.js]
 [browser_mozLoop_doNotDisturb.js]
 [browser_mozLoop_softStart.js]
 skip-if = buildapp == 'mulet'
 [browser_toolbarbutton.js]
 [browser_mozLoop_pluralStrings.js]
+[browser_mozLoop_telemetry.js]
+skip-if = e10s
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * This file contains tests for the mozLoop telemetry API.
+ */
+
+add_task(loadLoopPanel);
+
+/**
+ * Tests that boolean histograms exist and can be updated.
+ */
+add_task(function* test_mozLoop_telemetryAdd_boolean() {
+  for (let histogramId of [
+    "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS",
+  ]) {
+    let snapshot = Services.telemetry.getHistogramById(histogramId).snapshot();
+
+    let initialFalseCount = snapshot.counts[0];
+    let initialTrueCount = snapshot.counts[1];
+
+    for (let value of [false, false, true]) {
+      gMozLoopAPI.telemetryAdd(histogramId, value);
+    }
+
+    // The telemetry service updates histograms asynchronously, so we need to
+    // poll for the final values and time out otherwise.
+    info("Waiting for update of " + histogramId);
+    do {
+      yield new Promise(resolve => setTimeout(resolve, 50));
+      snapshot = Services.telemetry.getHistogramById(histogramId).snapshot();
+    } while (snapshot.counts[0] == initialFalseCount + 2 &&
+             snapshot.counts[1] == initialTrueCount + 1);
+    ok(true, "Correctly updated " + histogramId);
+  }
+});
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6519,9 +6519,14 @@
     "description": "Tracking protection shield (0 = not shown, 1 = blocked, 2 = loaded, 3 = due to mixed content"
   },
   "TRACKING_PROTECTION_EVENTS": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 3,
     "description": "Doorhanger shown = 0, Disable = 1, Enable = 2"
   }
+  "LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Stores 1 if generating a call URL succeeded, and 0 if it failed."
+  },
 }