bug 1440673 - Test Event Summarization in xpcshell r?Dexter draft
authorChris H-C <chutten@mozilla.com>
Tue, 03 Apr 2018 14:38:51 -0400
changeset 778106 061f38f026b13bf5462ddd32c637811a03caeaa2
parent 778105 9bf40ea05e16df91359d4127276c64966353fdce
child 778107 64399964eb28fd8358a00b8950a10dcd116b0308
push id105384
push userbmo:chutten@mozilla.com
push dateThu, 05 Apr 2018 20:33:48 +0000
reviewersDexter
bugs1440673
milestone61.0a1
bug 1440673 - Test Event Summarization in xpcshell r?Dexter To test the TelemetryEvents portion of the code we need TelemetryEvents tests. (The gtests only cover portions deeper than the TelemetryScalar API). MozReview-Commit-ID: ExaiW0OiwFI
toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
@@ -31,61 +31,86 @@ function checkEventFormat(events) {
       Assert.ok(Object.keys(extra).every(k => typeof(k) == "string"),
                 "All extra keys should be strings.");
       Assert.ok(Object.values(extra).every(v => typeof(v) == "string"),
                 "All extra values should be strings.");
     }
   }
 }
 
+/**
+ * @param summaries is of the form
+ *   [{process, [event category, event object, event method], count}]
+ * @param clearScalars - true if you want to clear the scalars
+ */
+function checkEventSummary(summaries, clearScalars) {
+  let scalars = Telemetry.snapshotKeyedScalars(OPTOUT, clearScalars);
+  dump(JSON.stringify(summaries));
+  for (let [process, [category, eObject, method], count] of summaries) {
+    let uniqueEventName = `${category}#${eObject}#${method}`;
+    let summaryCount;
+    if (process === "dynamic") {
+      summaryCount = scalars.dynamic["telemetry.dynamic_event_counts"][uniqueEventName];
+    } else {
+      summaryCount = scalars[process]["telemetry.event_counts"][uniqueEventName];
+    }
+    Assert.equal(summaryCount, count, `${uniqueEventName} had wrong summary count`);
+  }
+}
+
 add_task(async function test_recording_state() {
   const events = [
     ["telemetry.test", "test1", "object1"],
     ["telemetry.test.second", "test", "object1"],
   ];
 
   // Both test categories should be off by default.
   events.forEach(e => Telemetry.recordEvent(...e));
   let snapshot = Telemetry.snapshotEvents(OPTIN, true);
   Assert.equal(Object.keys(snapshot).length, 0, "Should not have recorded any events.");
+  checkEventSummary(events.map(e => (["parent", e, 1])), true);
 
   // Enable one test category and see that we record correctly.
   Telemetry.setEventRecordingEnabled("telemetry.test", true);
   events.forEach(e => Telemetry.recordEvent(...e));
   snapshot = Telemetry.snapshotEvents(OPTIN, true);
   Assert.ok(("parent" in snapshot), "Should have entry for main process.");
   Assert.equal(snapshot.parent.length, 1, "Should have recorded one event.");
   Assert.equal(snapshot.parent[0][1], "telemetry.test", "Should have recorded one event in telemetry.test");
+  checkEventSummary(events.map(e => (["parent", e, 1])), true);
 
   // Also enable the other test category and see that we record correctly.
   Telemetry.setEventRecordingEnabled("telemetry.test.second", true);
   events.forEach(e => Telemetry.recordEvent(...e));
   snapshot = Telemetry.snapshotEvents(OPTIN, true);
   Assert.ok(("parent" in snapshot), "Should have entry for main process.");
   Assert.equal(snapshot.parent.length, 2, "Should have recorded two events.");
   Assert.equal(snapshot.parent[0][1], "telemetry.test", "Should have recorded one event in telemetry.test");
   Assert.equal(snapshot.parent[1][1], "telemetry.test.second", "Should have recorded one event in telemetry.test.second");
+  checkEventSummary(events.map(e => (["parent", e, 1])), true);
 
   // Now turn of one category again and check that this works as expected.
   Telemetry.setEventRecordingEnabled("telemetry.test", false);
   events.forEach(e => Telemetry.recordEvent(...e));
   snapshot = Telemetry.snapshotEvents(OPTIN, true);
   Assert.ok(("parent" in snapshot), "Should have entry for main process.");
   Assert.equal(snapshot.parent.length, 1, "Should have recorded one event.");
   Assert.equal(snapshot.parent[0][1], "telemetry.test.second", "Should have recorded one event in telemetry.test.second");
+  checkEventSummary(events.map(e => (["parent", e, 1])), true);
 });
 
 add_task(async function recording_setup() {
   // Make sure both test categories are enabled for the remaining tests.
   // Otherwise their event recording won't work.
   Telemetry.setEventRecordingEnabled("telemetry.test", true);
   Telemetry.setEventRecordingEnabled("telemetry.test.second", true);
 });
 
 add_task(async function test_recording() {
+  Telemetry.clearScalars();
   Telemetry.clearEvents();
 
   // Record some events.
   let expected = [
     {optout: false, event: ["telemetry.test", "test1", "object1"]},
     {optout: false, event: ["telemetry.test", "test2", "object2"]},
 
     {optout: false, event: ["telemetry.test", "test1", "object1", "value"]},
@@ -111,16 +136,29 @@ add_task(async function test_recording()
   // Strip off trailing null values to match the serialized events.
   for (let entry of expected) {
     let e = entry.event;
     while ((e.length >= 3) && (e[e.length - 1] === null)) {
       e.pop();
     }
   }
 
+  // Check that the events were summarized properly.
+  let summaries = {};
+  expected.forEach(({optout, event}) => {
+    let [category, eObject, method] = event;
+    let uniqueEventName = `${category}#${eObject}#${method}`;
+    if (!(uniqueEventName in summaries)) {
+      summaries[uniqueEventName] = ["parent", event, 1];
+    } else {
+      summaries[uniqueEventName][2]++;
+    }
+  });
+  checkEventSummary(Object.values(summaries), true);
+
   // The following should not result in any recorded events.
   Assert.throws(() => Telemetry.recordEvent("unknown.category", "test1", "object1"),
                 /Error: Unknown event: \["unknown.category", "test1", "object1"\]/,
                 "Should throw on unknown category.");
   Assert.throws(() => Telemetry.recordEvent("telemetry.test", "unknown", "object1"),
                 /Error: Unknown event: \["telemetry.test", "unknown", "object1"\]/,
                 "Should throw on unknown method.");
   Assert.throws(() => Telemetry.recordEvent("telemetry.test", "test1", "unknown"),
@@ -294,16 +332,17 @@ add_task(async function test_unicodeValu
   let events = snapshot.parent;
   Assert.equal(events.length, 2, "Should have recorded 2 events.");
   Assert.equal(events[0][4], value, "Should have recorded the right value.");
   Assert.equal(events[1][5].key1, value, "Should have recorded the right extra value.");
 });
 
 add_task(async function test_dynamicEvents() {
   Telemetry.clearEvents();
+  Telemetry.clearScalars();
   Telemetry.canRecordExtended = true;
 
   // Register some test events.
   Telemetry.registerEvents("telemetry.test.dynamic", {
     // Event with only required fields.
     "test1": {
       methods: ["test1"],
       objects: ["object1"],
@@ -355,16 +394,19 @@ add_task(async function test_dynamicEven
   ];
   let events = snapshot.dynamic;
   Assert.equal(events.length, expected.length, "Should have recorded the right amount of events.");
   for (let i = 0; i < expected.length; ++i) {
     Assert.deepEqual(events[i].slice(1), expected[i],
                      "Should have recorded the expected event data.");
   }
 
+  // Check that we've summarized the recorded events
+  checkEventSummary(expected.map(ev => ["dynamic", ev, 1]), true);
+
   // Check that the opt-out snapshot contains only the one expected event.
   snapshot = Telemetry.snapshotEvents(OPTOUT, false);
   Assert.ok(("dynamic" in snapshot), "Should have dynamic events in the snapshot.");
   Assert.equal(snapshot.dynamic.length, 1, "Should have one opt-out event in the snapshot.");
   expected = ["telemetry.test.dynamic", "test4", "object1"];
   Assert.deepEqual(snapshot.dynamic[0].slice(1), expected);
 
   // Recording with unknown extra keys should be ignored and print an error.