Loading toolkit/components/telemetry/TelemetrySession.jsm +19 −0 Original line number Diff line number Diff line Loading @@ -1008,6 +1008,24 @@ var Impl = { return ret; }, getEvents: function(isSubsession, clearSubsession) { if (!isSubsession) { // We only support scalars for subsessions. this._log.trace("getEvents - We only support events in subsessions."); return []; } let events = Telemetry.snapshotBuiltinEvents(this.getDatasetType(), clearSubsession); // Don't return the test events outside of test environments. if (!this._testing) { events = events.filter(e => e[1].startsWith("telemetry.test")); } return events; }, getThreadHangStats: function getThreadHangStats(stats) { this._log.trace("getThreadHangStats"); Loading Loading @@ -1280,6 +1298,7 @@ var Impl = { parent: { scalars: protect(() => this.getScalars(isSubsession, clearSubsession)), keyedScalars: protect(() => this.getScalars(isSubsession, clearSubsession, true)), events: protect(() => this.getEvents(isSubsession, clearSubsession)), }, content: { histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT], Loading toolkit/components/telemetry/tests/unit/test_TelemetrySession.js +93 −0 Original line number Diff line number Diff line Loading @@ -291,6 +291,53 @@ function checkScalars(processes) { } } function checkEvents(processes) { // Check that the events section is available in the ping payload. const parent = processes.parent; Assert.ok("events" in parent, "The events section must be available in the parent process."); // Check that the events section has the right format. Assert.ok(Array.isArray(parent.events), "The events entry must be an array."); for (let [ts, category, method, object, value, extra] of parent.events) { Assert.equal(typeof(ts), "number", "Timestamp field should be a number."); Assert.greaterOrEqual(ts, 0, "Timestamp should be >= 0."); Assert.equal(typeof(category), "string", "Category should have the right type."); Assert.lessOrEqual(category.length, 100, "Category should have the right string length."); Assert.equal(typeof(method), "string", "Method should have the right type."); Assert.lessOrEqual(method.length, 40, "Method should have the right string length."); Assert.equal(typeof(object), "string", "Object should have the right type."); Assert.lessOrEqual(object.length, 40, "Object should have the right string length."); Assert.ok(value === null || typeof(value) === "string", "Value should be null or a string."); if (value) { Assert.lessOrEqual(value.length, 100, "Value should have the right string length."); } Assert.ok(extra === null || typeof(extra) === "object", "Extra should be null or an object."); if (extra) { let keys = Object.keys(extra); let keyTypes = keys.map(k => typeof(k)); Assert.lessOrEqual(keys.length, 20, "Should not have too many extra keys."); Assert.ok(keyTypes.every(t => t === "string"), "All extra keys should be strings."); Assert.ok(keys.every(k => k.length <= 20), "All extra keys should have the right string length."); let values = Object.values(extra); let valueTypes = values.map(v => typeof(v)); Assert.ok(valueTypes.every(t => t === "string"), "All extra values should be strings."); Assert.ok(values.every(v => v.length <= 100), "All extra values should have the right string length."); } } } function checkPayload(payload, reason, successfulPings, savedPings) { Assert.ok("info" in payload, "Payload must contain an info section."); checkPayloadInfo(payload.info); Loading Loading @@ -422,6 +469,7 @@ function checkPayload(payload, reason, successfulPings, savedPings) { Assert.ok("processes" in payload, "The payload must have a processes section."); Assert.ok("parent" in payload.processes, "There must be at least a parent process."); checkScalars(payload.processes); checkEvents(payload.processes); } function writeStringToFile(file, contents) { Loading Loading @@ -658,6 +706,51 @@ add_task(function* test_checkSubsessionScalars() { STRING_SCALAR + " must contain the expected value."); }); add_task(function* test_checkSubsessionEvents() { if (gIsAndroid) { // We don't support subsessions yet on Android. return; } // Clear the events. Telemetry.clearEvents(); yield TelemetryController.testReset(); // Record some events. let expected = [ ["telemetry.test", "test1", "object1", "a", null], ["telemetry.test", "test1", "object1", null, {key1: "value"}], ]; for (let event of expected) { Telemetry.recordEvent(...event); } // Check that events are not available in classic pings but are in subsession // pings. Also clear the subsession. let classic = TelemetrySession.getPayload(); let subsession = TelemetrySession.getPayload("environment-change", true); Assert.ok("events" in classic.processes.parent, "Should have an events field in classic payload."); Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload."); // They should be empty in the classic payload. Assert.deepEqual(classic.processes.parent.events, [], "Events in classic payload should be empty."); // In the subsession payload, they should contain the recorded test events. let events = subsession.processes.parent.events.filter(e => e[1] === "telemetry.test"); Assert.equal(events.length, expected.length, "Should have the right amount of events in the payload."); for (let i = 0; i < expected.length; ++i) { Assert.deepEqual(events[i].slice(1), expected[i], "Should have the right event data in the ping."); } // As we cleared the subsession above, the events entry should now be empty. subsession = TelemetrySession.getPayload("environment-change", false); Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload."); events = subsession.processes.parent.events.filter(e => e[1] === "telemetry.test"); Assert.equal(events.length, 0, "Should have no test events in the subsession payload now."); }); add_task(function* test_checkSubsessionHistograms() { if (gIsAndroid) { // We don't support subsessions yet on Android. Loading Loading
toolkit/components/telemetry/TelemetrySession.jsm +19 −0 Original line number Diff line number Diff line Loading @@ -1008,6 +1008,24 @@ var Impl = { return ret; }, getEvents: function(isSubsession, clearSubsession) { if (!isSubsession) { // We only support scalars for subsessions. this._log.trace("getEvents - We only support events in subsessions."); return []; } let events = Telemetry.snapshotBuiltinEvents(this.getDatasetType(), clearSubsession); // Don't return the test events outside of test environments. if (!this._testing) { events = events.filter(e => e[1].startsWith("telemetry.test")); } return events; }, getThreadHangStats: function getThreadHangStats(stats) { this._log.trace("getThreadHangStats"); Loading Loading @@ -1280,6 +1298,7 @@ var Impl = { parent: { scalars: protect(() => this.getScalars(isSubsession, clearSubsession)), keyedScalars: protect(() => this.getScalars(isSubsession, clearSubsession, true)), events: protect(() => this.getEvents(isSubsession, clearSubsession)), }, content: { histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT], Loading
toolkit/components/telemetry/tests/unit/test_TelemetrySession.js +93 −0 Original line number Diff line number Diff line Loading @@ -291,6 +291,53 @@ function checkScalars(processes) { } } function checkEvents(processes) { // Check that the events section is available in the ping payload. const parent = processes.parent; Assert.ok("events" in parent, "The events section must be available in the parent process."); // Check that the events section has the right format. Assert.ok(Array.isArray(parent.events), "The events entry must be an array."); for (let [ts, category, method, object, value, extra] of parent.events) { Assert.equal(typeof(ts), "number", "Timestamp field should be a number."); Assert.greaterOrEqual(ts, 0, "Timestamp should be >= 0."); Assert.equal(typeof(category), "string", "Category should have the right type."); Assert.lessOrEqual(category.length, 100, "Category should have the right string length."); Assert.equal(typeof(method), "string", "Method should have the right type."); Assert.lessOrEqual(method.length, 40, "Method should have the right string length."); Assert.equal(typeof(object), "string", "Object should have the right type."); Assert.lessOrEqual(object.length, 40, "Object should have the right string length."); Assert.ok(value === null || typeof(value) === "string", "Value should be null or a string."); if (value) { Assert.lessOrEqual(value.length, 100, "Value should have the right string length."); } Assert.ok(extra === null || typeof(extra) === "object", "Extra should be null or an object."); if (extra) { let keys = Object.keys(extra); let keyTypes = keys.map(k => typeof(k)); Assert.lessOrEqual(keys.length, 20, "Should not have too many extra keys."); Assert.ok(keyTypes.every(t => t === "string"), "All extra keys should be strings."); Assert.ok(keys.every(k => k.length <= 20), "All extra keys should have the right string length."); let values = Object.values(extra); let valueTypes = values.map(v => typeof(v)); Assert.ok(valueTypes.every(t => t === "string"), "All extra values should be strings."); Assert.ok(values.every(v => v.length <= 100), "All extra values should have the right string length."); } } } function checkPayload(payload, reason, successfulPings, savedPings) { Assert.ok("info" in payload, "Payload must contain an info section."); checkPayloadInfo(payload.info); Loading Loading @@ -422,6 +469,7 @@ function checkPayload(payload, reason, successfulPings, savedPings) { Assert.ok("processes" in payload, "The payload must have a processes section."); Assert.ok("parent" in payload.processes, "There must be at least a parent process."); checkScalars(payload.processes); checkEvents(payload.processes); } function writeStringToFile(file, contents) { Loading Loading @@ -658,6 +706,51 @@ add_task(function* test_checkSubsessionScalars() { STRING_SCALAR + " must contain the expected value."); }); add_task(function* test_checkSubsessionEvents() { if (gIsAndroid) { // We don't support subsessions yet on Android. return; } // Clear the events. Telemetry.clearEvents(); yield TelemetryController.testReset(); // Record some events. let expected = [ ["telemetry.test", "test1", "object1", "a", null], ["telemetry.test", "test1", "object1", null, {key1: "value"}], ]; for (let event of expected) { Telemetry.recordEvent(...event); } // Check that events are not available in classic pings but are in subsession // pings. Also clear the subsession. let classic = TelemetrySession.getPayload(); let subsession = TelemetrySession.getPayload("environment-change", true); Assert.ok("events" in classic.processes.parent, "Should have an events field in classic payload."); Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload."); // They should be empty in the classic payload. Assert.deepEqual(classic.processes.parent.events, [], "Events in classic payload should be empty."); // In the subsession payload, they should contain the recorded test events. let events = subsession.processes.parent.events.filter(e => e[1] === "telemetry.test"); Assert.equal(events.length, expected.length, "Should have the right amount of events in the payload."); for (let i = 0; i < expected.length; ++i) { Assert.deepEqual(events[i].slice(1), expected[i], "Should have the right event data in the ping."); } // As we cleared the subsession above, the events entry should now be empty. subsession = TelemetrySession.getPayload("environment-change", false); Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload."); events = subsession.processes.parent.events.filter(e => e[1] === "telemetry.test"); Assert.equal(events.length, 0, "Should have no test events in the subsession payload now."); }); add_task(function* test_checkSubsessionHistograms() { if (gIsAndroid) { // We don't support subsessions yet on Android. Loading