Bug 1120379 - Add tests for the deletion ping. r=gfritzsche
authorAlessio Placitelli <alessio.placitelli@gmail.com>
Thu, 28 May 2015 08:48:00 +0200
changeset 267044 113c09b2fd55d1330266543530bd917b0cb52e70
parent 267043 fbf0fdd5facef147113166594959274f82f4a6c8
child 267045 6142099b326079d6ef2f53c539336eb253be3979
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs1120379
milestone41.0a1
Bug 1120379 - Add tests for the deletion ping. r=gfritzsche
browser/base/content/test/general/healthreport_testRemoteCommands.html
toolkit/components/telemetry/tests/unit/test_TelemetryController.js
--- a/browser/base/content/test/general/healthreport_testRemoteCommands.html
+++ b/browser/base/content/test/general/healthreport_testRemoteCommands.html
@@ -1,267 +1,272 @@
-<html>
-  <head>
-    <meta charset="utf-8">
-<script type="application/javascript;version=1.7"
-            src="healthreport_pingData.js">
-</script>
-<script type="application/javascript;version=1.7">
-
-function init() {
-  window.addEventListener("message", function process(e) {
-    // The init function of abouthealth.js schedules an initial payload event,
-    // which will be sent after the payload data has been collected. This extra
-    // event can cause unexpected successes/failures in this test, so we wait
-    // for the extra event to arrive here before progressing with the actual
-    // test.
-    if (e.data.type == "payload") {
-      window.removeEventListener("message", process, false);
-
-      window.addEventListener("message", doTest, false);
-      doTest();
-    }
-  }, false);
-}
-
-function checkSubmissionValue(payload, expectedValue) {
-  return payload.enabled == expectedValue;
-}
-
-function validatePayload(payload) {
-  payload = JSON.parse(payload);
-
-  // xxxmpc - this is some pretty low-bar validation, but we have plenty of tests of that API elsewhere
-  if (!payload.thisPingDate)
-    return false;
-
-  return true;
-}
-
-function isArray(arg) {
-  return Object.prototype.toString.call(arg) === '[object Array]';
-}
-
-function writeDiagnostic(text) {
-  let node = document.createTextNode(text);
-  let br = document.createElement("br");
-  document.body.appendChild(node);
-  document.body.appendChild(br);
-}
-
-function validateCurrentTelemetryEnvironment(data) {
-  // Simple check for now: check that the received object has the expected
-  // top-level properties.
-  const expectedKeys = ["profile", "settings", "system", "build", "partner", "addons"];
-  return expectedKeys.every(key => (key in data));
-}
-
-function validateCurrentTelemetryPingData(ping) {
-  // Simple check for now: check that the received object has the expected
-  // top-level properties and that the type and reason match.
-  const expectedKeys = ["environment", "clientId", "payload", "application",
-                        "version", "type", "id"];
-  return expectedKeys.every(key => (key in ping)) &&
-         (ping.type == "main") &&
-         ("info" in ping.payload) &&
-         ("reason" in ping.payload.info) &&
-         (ping.payload.info.reason == "gather-subsession-payload");
-}
-
-function validateTelemetryPingList(list) {
-  if (!isArray(list)) {
-    console.log("Telemetry ping list is not an array.");
-    return false;
-  }
-
-  if (list.length != TEST_PINGS.length) {
-    console.log("Telemetry ping length is not correct.");
-    return false;
-  }
-
-  let valid = true;
-  for (let i=0; i<list.length; ++i) {
-    let received = list[i];
-    let expected = TEST_PINGS[i];
-    if (received.type != expected.type ||
-        received.timestampCreated != expected.date.getTime()) {
-      writeDiagnostic("Telemetry ping " + i + " does not match.");
-      writeDiagnostic("Expected: " + JSON.stringify(expected));
-      writeDiagnostic("Received: " + JSON.stringify(received));
-      valid = false;
-    } else {
-      writeDiagnostic("Telemetry ping " + i + " matches.");
-    }
-  }
-
-  return true;
-}
-
-function validateTelemetryPingData(expected, received) {
-  const receivedDate = new Date(received.creationDate);
-  if (received.id != expected.id ||
-      received.type != expected.type ||
-      receivedDate.getTime() != expected.date.getTime()) {
-    writeDiagnostic("Telemetry ping data for " + expected.id + " doesn't match.");
-    writeDiagnostic("Expected: " + JSON.stringify(expected));
-    writeDiagnostic("Received: " + JSON.stringify(received));
-    return false;
-  }
-
-  writeDiagnostic("Telemetry ping data for " + expected.id + " matched.");
-  return true;
-}
-
-var tests = [
-{
-  info: "Checking initial value is enabled",
-  event: "RequestCurrentPrefs",
-  payloadType: "prefs",
-  validateResponse: function(payload) {
-    return checkSubmissionValue(payload, true);
-  },
-},
-{
-  info: "Verifying disabling works",
-  event: "DisableDataSubmission",
-  payloadType: "prefs",
-  validateResponse: function(payload) {
-    return checkSubmissionValue(payload, false);
-  },
-},
-{
-  info: "Verifying we're still disabled",
-  event: "RequestCurrentPrefs",
-  payloadType: "prefs",
-  validateResponse: function(payload) {
-    return checkSubmissionValue(payload, false);
-  },
-},
-{
-  info: "Verifying we can get a payload while submission is disabled",
-  event: "RequestCurrentPayload",
-  payloadType: "payload",
-  validateResponse: function(payload) {
-    return validatePayload(payload);
-  },
-},
-{
-  info: "Verifying enabling works",
-  event: "EnableDataSubmission",
-  payloadType: "prefs",
-  validateResponse: function(payload) {
-    return checkSubmissionValue(payload, true);
-  },
-},
-{
-  info: "Verifying we're still re-enabled",
-  event: "RequestCurrentPrefs",
-  payloadType: "prefs",
-  validateResponse: function(payload) {
-    return checkSubmissionValue(payload, true);
-  },
-},
-{
-  info: "Verifying we can get a payload after re-enabling",
-  event: "RequestCurrentPayload",
-  payloadType: "payload",
-  validateResponse: function(payload) {
-    return validatePayload(payload);
-  },
-},
-{
-  info: "Verifying that we can get the current Telemetry environment data",
-  event: "RequestCurrentEnvironment",
-  payloadType: "telemetry-current-environment-data",
-  validateResponse: function(payload) {
-    return validateCurrentTelemetryEnvironment(payload);
-  },
-},
-{
-  info: "Verifying that we can get the current Telemetry ping data",
-  event: "RequestCurrentPingData",
-  payloadType: "telemetry-current-ping-data",
-  validateResponse: function(payload) {
-    return validateCurrentTelemetryPingData(payload);
-  },
-},
-{
-  info: "Verifying that we get the proper Telemetry ping list",
-  event: "RequestTelemetryPingList",
-  payloadType: "telemetry-ping-list",
-  validateResponse: function(payload) {
-    // Validate the ping list
-    if (!validateTelemetryPingList(payload)) {
-      return false;
-    }
-
-    // Now that we received the ping ids, set up additional test tasks
-    // that check loading the individual pings.
-    for (let i=0; i<TEST_PINGS.length; ++i) {
-      TEST_PINGS[i].id = payload[i].id;
-      tests.push({
-        info: "Verifying that we can get the proper Telemetry ping data #" + (i + 1),
-        event: "RequestTelemetryPingData",
-        eventData: { id: TEST_PINGS[i].id },
-        payloadType: "telemetry-ping-data",
-        validateResponse: function(payload) {
-          return validateTelemetryPingData(TEST_PINGS[i], payload.pingData);
-        },
-      });
-    }
-
-    return true;
-  },
-},
-];
-
-var currentTest = -1;
-function doTest(evt) {
-  if (evt) {
-    if (currentTest < 0 || !evt.data.content)
-      return; // not yet testing
-
-    var test = tests[currentTest];
-    if (evt.data.type != test.payloadType)
-      return; // skip unrequested events
-
-    var error = JSON.stringify(evt.data.content);
-    var pass = false;
-    try {
-      pass = test.validateResponse(evt.data.content)
-    } catch (e) {}
-    reportResult(test.info, pass, error);
-  }
-  // start the next test if there are any left
-  if (tests[++currentTest])
-    sendToBrowser(tests[currentTest].event, tests[currentTest].eventData);
-  else
-    reportFinished();
-}
-
-function reportResult(info, pass, error) {
-  var data = {type: "testResult", info: info, pass: pass, error: error};
-  var event = new CustomEvent("FirefoxHealthReportTestResponse", {detail: {data: data}, bubbles: true});
-  document.dispatchEvent(event);
-}
-
-function reportFinished(cmd) {
-  var data = {type: "testsComplete", count: tests.length};
-  var event = new CustomEvent("FirefoxHealthReportTestResponse", {detail: {data: data}, bubbles: true});
-  document.dispatchEvent(event);
-}
-
-function sendToBrowser(type, eventData) {
-  eventData = eventData || {};
-  let detail = {command: type};
-  for (let key of Object.keys(eventData)) {
-    detail[key] = eventData[key];
-  }
-
-  var event = new CustomEvent("RemoteHealthReportCommand", {detail: detail, bubbles: true});
-  document.dispatchEvent(event);
-}
-
-</script>
-  </head>
-  <body onload="init()">
-  </body>
-</html>
+<html>
+  <head>
+    <meta charset="utf-8">
+<script type="application/javascript;version=1.7"
+            src="healthreport_pingData.js">
+</script>
+<script type="application/javascript;version=1.7">
+
+function init() {
+  window.addEventListener("message", function process(e) {
+    // The init function of abouthealth.js schedules an initial payload event,
+    // which will be sent after the payload data has been collected. This extra
+    // event can cause unexpected successes/failures in this test, so we wait
+    // for the extra event to arrive here before progressing with the actual
+    // test.
+    if (e.data.type == "payload") {
+      window.removeEventListener("message", process, false);
+
+      window.addEventListener("message", doTest, false);
+      doTest();
+    }
+  }, false);
+}
+
+function checkSubmissionValue(payload, expectedValue) {
+  return payload.enabled == expectedValue;
+}
+
+function validatePayload(payload) {
+  payload = JSON.parse(payload);
+
+  // xxxmpc - this is some pretty low-bar validation, but we have plenty of tests of that API elsewhere
+  if (!payload.thisPingDate)
+    return false;
+
+  return true;
+}
+
+function isArray(arg) {
+  return Object.prototype.toString.call(arg) === '[object Array]';
+}
+
+function writeDiagnostic(text) {
+  let node = document.createTextNode(text);
+  let br = document.createElement("br");
+  document.body.appendChild(node);
+  document.body.appendChild(br);
+}
+
+function validateCurrentTelemetryEnvironment(data) {
+  // Simple check for now: check that the received object has the expected
+  // top-level properties.
+  const expectedKeys = ["profile", "settings", "system", "build", "partner", "addons"];
+  return expectedKeys.every(key => (key in data));
+}
+
+function validateCurrentTelemetryPingData(ping) {
+  // Simple check for now: check that the received object has the expected
+  // top-level properties and that the type and reason match.
+  const expectedKeys = ["environment", "clientId", "payload", "application",
+                        "version", "type", "id"];
+  return expectedKeys.every(key => (key in ping)) &&
+         (ping.type == "main") &&
+         ("info" in ping.payload) &&
+         ("reason" in ping.payload.info) &&
+         (ping.payload.info.reason == "gather-subsession-payload");
+}
+
+function validateTelemetryPingList(list) {
+  if (!isArray(list)) {
+    console.log("Telemetry ping list is not an array.");
+    return false;
+  }
+
+  // Telemetry may generate other pings (e.g. "deletion" pings), so filter those
+  // out.
+  const TEST_TYPES_REGEX = /^test-telemetryArchive/;
+  list = list.filter(p => TEST_TYPES_REGEX.test(p.type));
+
+  if (list.length != TEST_PINGS.length) {
+    console.log("Telemetry ping length is not correct.");
+    return false;
+  }
+
+  let valid = true;
+  for (let i=0; i<list.length; ++i) {
+    let received = list[i];
+    let expected = TEST_PINGS[i];
+    if (received.type != expected.type ||
+        received.timestampCreated != expected.date.getTime()) {
+      writeDiagnostic("Telemetry ping " + i + " does not match.");
+      writeDiagnostic("Expected: " + JSON.stringify(expected));
+      writeDiagnostic("Received: " + JSON.stringify(received));
+      valid = false;
+    } else {
+      writeDiagnostic("Telemetry ping " + i + " matches.");
+    }
+  }
+
+  return true;
+}
+
+function validateTelemetryPingData(expected, received) {
+  const receivedDate = new Date(received.creationDate);
+  if (received.id != expected.id ||
+      received.type != expected.type ||
+      receivedDate.getTime() != expected.date.getTime()) {
+    writeDiagnostic("Telemetry ping data for " + expected.id + " doesn't match.");
+    writeDiagnostic("Expected: " + JSON.stringify(expected));
+    writeDiagnostic("Received: " + JSON.stringify(received));
+    return false;
+  }
+
+  writeDiagnostic("Telemetry ping data for " + expected.id + " matched.");
+  return true;
+}
+
+var tests = [
+{
+  info: "Checking initial value is enabled",
+  event: "RequestCurrentPrefs",
+  payloadType: "prefs",
+  validateResponse: function(payload) {
+    return checkSubmissionValue(payload, true);
+  },
+},
+{
+  info: "Verifying disabling works",
+  event: "DisableDataSubmission",
+  payloadType: "prefs",
+  validateResponse: function(payload) {
+    return checkSubmissionValue(payload, false);
+  },
+},
+{
+  info: "Verifying we're still disabled",
+  event: "RequestCurrentPrefs",
+  payloadType: "prefs",
+  validateResponse: function(payload) {
+    return checkSubmissionValue(payload, false);
+  },
+},
+{
+  info: "Verifying we can get a payload while submission is disabled",
+  event: "RequestCurrentPayload",
+  payloadType: "payload",
+  validateResponse: function(payload) {
+    return validatePayload(payload);
+  },
+},
+{
+  info: "Verifying enabling works",
+  event: "EnableDataSubmission",
+  payloadType: "prefs",
+  validateResponse: function(payload) {
+    return checkSubmissionValue(payload, true);
+  },
+},
+{
+  info: "Verifying we're still re-enabled",
+  event: "RequestCurrentPrefs",
+  payloadType: "prefs",
+  validateResponse: function(payload) {
+    return checkSubmissionValue(payload, true);
+  },
+},
+{
+  info: "Verifying we can get a payload after re-enabling",
+  event: "RequestCurrentPayload",
+  payloadType: "payload",
+  validateResponse: function(payload) {
+    return validatePayload(payload);
+  },
+},
+{
+  info: "Verifying that we can get the current Telemetry environment data",
+  event: "RequestCurrentEnvironment",
+  payloadType: "telemetry-current-environment-data",
+  validateResponse: function(payload) {
+    return validateCurrentTelemetryEnvironment(payload);
+  },
+},
+{
+  info: "Verifying that we can get the current Telemetry ping data",
+  event: "RequestCurrentPingData",
+  payloadType: "telemetry-current-ping-data",
+  validateResponse: function(payload) {
+    return validateCurrentTelemetryPingData(payload);
+  },
+},
+{
+  info: "Verifying that we get the proper Telemetry ping list",
+  event: "RequestTelemetryPingList",
+  payloadType: "telemetry-ping-list",
+  validateResponse: function(payload) {
+    // Validate the ping list
+    if (!validateTelemetryPingList(payload)) {
+      return false;
+    }
+
+    // Now that we received the ping ids, set up additional test tasks
+    // that check loading the individual pings.
+    for (let i=0; i<TEST_PINGS.length; ++i) {
+      TEST_PINGS[i].id = payload[i].id;
+      tests.push({
+        info: "Verifying that we can get the proper Telemetry ping data #" + (i + 1),
+        event: "RequestTelemetryPingData",
+        eventData: { id: TEST_PINGS[i].id },
+        payloadType: "telemetry-ping-data",
+        validateResponse: function(payload) {
+          return validateTelemetryPingData(TEST_PINGS[i], payload.pingData);
+        },
+      });
+    }
+
+    return true;
+  },
+},
+];
+
+var currentTest = -1;
+function doTest(evt) {
+  if (evt) {
+    if (currentTest < 0 || !evt.data.content)
+      return; // not yet testing
+
+    var test = tests[currentTest];
+    if (evt.data.type != test.payloadType)
+      return; // skip unrequested events
+
+    var error = JSON.stringify(evt.data.content);
+    var pass = false;
+    try {
+      pass = test.validateResponse(evt.data.content)
+    } catch (e) {}
+    reportResult(test.info, pass, error);
+  }
+  // start the next test if there are any left
+  if (tests[++currentTest])
+    sendToBrowser(tests[currentTest].event, tests[currentTest].eventData);
+  else
+    reportFinished();
+}
+
+function reportResult(info, pass, error) {
+  var data = {type: "testResult", info: info, pass: pass, error: error};
+  var event = new CustomEvent("FirefoxHealthReportTestResponse", {detail: {data: data}, bubbles: true});
+  document.dispatchEvent(event);
+}
+
+function reportFinished(cmd) {
+  var data = {type: "testsComplete", count: tests.length};
+  var event = new CustomEvent("FirefoxHealthReportTestResponse", {detail: {data: data}, bubbles: true});
+  document.dispatchEvent(event);
+}
+
+function sendToBrowser(type, eventData) {
+  eventData = eventData || {};
+  let detail = {command: type};
+  for (let key of Object.keys(eventData)) {
+    detail[key] = eventData[key];
+  }
+
+  var event = new CustomEvent("RemoteHealthReportCommand", {detail: detail, bubbles: true});
+  document.dispatchEvent(event);
+}
+
+</script>
+  </head>
+  <body onload="init()">
+  </body>
+</html>
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryController.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryController.js
@@ -16,16 +16,17 @@ Cu.import("resource://gre/modules/Teleme
 Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
 Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
 Cu.import("resource://gre/modules/TelemetryArchive.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Preferences.jsm");
 
 const PING_FORMAT_VERSION = 4;
+const DELETION_PING_TYPE = "deletion";
 const TEST_PING_TYPE = "test-ping-type";
 
 const PLATFORM_VERSION = "1.9.2";
 const APP_VERSION = "1";
 const APP_NAME = "XPCShell";
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_ENABLED = PREF_BRANCH + "enabled";
@@ -169,16 +170,35 @@ add_task(function* test_simplePing() {
   // Make sure the version in the query string matches the new ping format version.
   let params = request.queryString.split("&");
   Assert.ok(params.find(p => p == ("v=" + PING_FORMAT_VERSION)));
 
   let ping = decodeRequestPayload(request);
   checkPingFormat(ping, TEST_PING_TYPE, false, false);
 });
 
+add_task(function* test_deletionPing() {
+  const isUnified = Preferences.get(PREF_UNIFIED, false);
+  if (!isUnified) {
+    // Skipping the test if unified telemetry is off, as no deletion ping will
+    // be generated.
+    return;
+  }
+
+  // Disable FHR upload: this should trigger a deletion ping.
+  Preferences.set(PREF_FHR_UPLOAD_ENABLED, false);
+
+  let request = yield gRequestIterator.next();
+  let ping = decodeRequestPayload(request);
+  checkPingFormat(ping, DELETION_PING_TYPE, true, false);
+
+  // Restore FHR Upload.
+  Preferences.set(PREF_FHR_UPLOAD_ENABLED, true);
+});
+
 add_task(function* test_pingHasClientId() {
   // Send a ping with a clientId.
   yield sendPing(true, false);
 
   let request = yield gRequestIterator.next();
   let ping = decodeRequestPayload(request);
   checkPingFormat(ping, TEST_PING_TYPE, true, false);
 
@@ -226,16 +246,24 @@ add_task(function* test_archivePings() {
 
   // Disable ping upload so that pings don't get sent.
   // With unified telemetry the FHR upload pref controls this,
   // with non-unified telemetry the Telemetry enabled pref.
   const isUnified = Preferences.get(PREF_UNIFIED, false);
   const uploadPref = isUnified ? PREF_FHR_UPLOAD_ENABLED : PREF_ENABLED;
   Preferences.set(uploadPref, false);
 
+  // If we're using unified telemetry, disabling ping upload will generate a "deletion"
+  // ping. Catch it.
+  if (isUnified) {
+    let request = yield gRequestIterator.next();
+    let ping = decodeRequestPayload(request);
+    checkPingFormat(ping, DELETION_PING_TYPE, true, false);
+  }
+
   // Register a new Ping Handler that asserts if a ping is received, then send a ping.
   registerPingHandler(() => Assert.ok(false, "Telemetry must not send pings if not allowed to."));
   let pingId = yield sendPing(true, true);
 
   // Check that the ping was archived, even with upload disabled.
   let ping = yield TelemetryArchive.promiseArchivedPingById(pingId);
   Assert.equal(ping.id, pingId, "TelemetryController should still archive pings.");