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 279722 113c09b2fd55d1330266543530bd917b0cb52e70
parent 279721 fbf0fdd5facef147113166594959274f82f4a6c8
child 279723 6142099b326079d6ef2f53c539336eb253be3979
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfritzsche
bugs1120379
milestone41.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 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.");