diff options
Diffstat (limited to 'devtools/client/shared/test/telemetry-test-helpers.js')
-rw-r--r-- | devtools/client/shared/test/telemetry-test-helpers.js | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/devtools/client/shared/test/telemetry-test-helpers.js b/devtools/client/shared/test/telemetry-test-helpers.js new file mode 100644 index 0000000000..2a5032f466 --- /dev/null +++ b/devtools/client/shared/test/telemetry-test-helpers.js @@ -0,0 +1,273 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* global is ok registerCleanupFunction Services */ + +"use strict"; + +// We try to avoid polluting the global scope as far as possible by defining +// constants in the methods that use them because this script is not sandboxed +// meaning that it is loaded via Services.scriptloader.loadSubScript() + +class TelemetryHelpers { + constructor() { + this.oldCanRecord = Services.telemetry.canRecordExtended; + this.generateTelemetryTests = this.generateTelemetryTests.bind(this); + registerCleanupFunction(this.stopTelemetry.bind(this)); + } + + /** + * Allow collection of extended telemetry data. + */ + startTelemetry() { + Services.telemetry.canRecordExtended = true; + } + + /** + * Clear all telemetry types. + */ + stopTelemetry() { + // Clear histograms, scalars and Telemetry Events. + this.clearHistograms(Services.telemetry.getSnapshotForHistograms); + this.clearHistograms(Services.telemetry.getSnapshotForKeyedHistograms); + Services.telemetry.clearScalars(); + Services.telemetry.clearEvents(); + + Services.telemetry.canRecordExtended = this.oldCanRecord; + } + + /** + * Clears Telemetry Histograms. + * + * @param {Function} snapshotFunc + * The function used to take the snapshot. This can be one of the + * following: + * - Services.telemetry.getSnapshotForHistograms + * - Services.telemetry.getSnapshotForKeyedHistograms + */ + clearHistograms(snapshotFunc) { + snapshotFunc("main", true); + } + + /** + * Check the value of a given telemetry histogram. + * + * @param {String} histId + * Histogram id + * @param {String} key + * Keyed histogram key + * @param {Array|Number} expected + * Expected value + * @param {String} checkType + * "array" (default) - Check that an array matches the histogram data. + * "hasentries" - For non-enumerated linear and exponential + * histograms. This checks for at least one entry. + * "scalar" - Telemetry type is a scalar. + * "keyedscalar" - Telemetry type is a keyed scalar. + */ + checkTelemetry(histId, key, expected, checkType) { + let actual; + let msg; + + if (checkType === "array" || checkType === "hasentries") { + if (key) { + const keyedHistogram = Services.telemetry + .getKeyedHistogramById(histId) + .snapshot(); + const result = keyedHistogram[key]; + + if (result) { + actual = result.values; + } else { + ok(false, `${histId}[${key}] exists`); + return; + } + } else { + actual = Services.telemetry.getHistogramById(histId).snapshot().values; + } + } + + switch (checkType) { + case "array": + msg = key ? `${histId}["${key}"] correct.` : `${histId} correct.`; + is(JSON.stringify(actual), JSON.stringify(expected), msg); + break; + case "hasentries": + const hasEntry = Object.values(actual).some(num => num > 0); + if (key) { + ok(hasEntry, `${histId}["${key}"] has at least one entry.`); + } else { + ok(hasEntry, `${histId} has at least one entry.`); + } + break; + case "scalar": + const scalars = Services.telemetry.getSnapshotForScalars( + "main", + false + ).parent; + + is(scalars[histId], expected, `${histId} correct`); + break; + case "keyedscalar": + const keyedScalars = Services.telemetry.getSnapshotForKeyedScalars( + "main", + false + ).parent; + const value = keyedScalars[histId][key]; + + msg = key ? `${histId}["${key}"] correct.` : `${histId} correct.`; + is(value, expected, msg); + break; + } + } + + /** + * Generate telemetry tests. You should call generateTelemetryTests("DEVTOOLS_") + * from your result checking code in telemetry tests. It logs checkTelemetry + * calls for all changed telemetry values. + * + * @param {String} prefix + * Optionally limits results to histogram ids starting with prefix. + */ + generateTelemetryTests(prefix = "") { + // Get all histograms and scalars + const histograms = Services.telemetry.getSnapshotForHistograms( + "main", + true + ).parent; + const keyedHistograms = Services.telemetry.getSnapshotForKeyedHistograms( + "main", + true + ).parent; + const scalars = Services.telemetry.getSnapshotForScalars( + "main", + false + ).parent; + const keyedScalars = Services.telemetry.getSnapshotForKeyedScalars( + "main", + false + ).parent; + const allHistograms = Object.assign( + {}, + histograms, + keyedHistograms, + scalars, + keyedScalars + ); + // Get all keys + const histIds = Object.keys(allHistograms).filter(histId => + histId.startsWith(prefix) + ); + + dump("=".repeat(80) + "\n"); + for (const histId of histIds) { + const snapshot = allHistograms[histId]; + + if (histId === histId.toLowerCase()) { + if (typeof snapshot === "object") { + // Keyed Scalar + const keys = Object.keys(snapshot); + + for (const key of keys) { + const value = snapshot[key]; + + dump( + `checkTelemetry("${histId}", "${key}", ${value}, "keyedscalar");\n` + ); + } + } else { + // Scalar + dump(`checkTelemetry("${histId}", "", ${snapshot}, "scalar");\n`); + } + } else if ( + typeof snapshot.histogram_type !== "undefined" && + typeof snapshot.values !== "undefined" + ) { + // Histogram + const actual = snapshot.values; + + this.displayDataFromHistogramSnapshot(snapshot, "", histId, actual); + } else { + // Keyed Histogram + const keys = Object.keys(snapshot); + + for (const key of keys) { + const value = snapshot[key]; + const actual = value.counts; + + this.displayDataFromHistogramSnapshot(value, key, histId, actual); + } + } + } + dump("=".repeat(80) + "\n"); + } + + /** + * Generates the inner contents of a test's checkTelemetry() method. + * + * @param {HistogramSnapshot} snapshot + * A snapshot of a telemetry chart obtained via getSnapshotForHistograms or + * similar. + * @param {String} key + * Only used for keyed histograms. This is the key we are interested in + * checking. + * @param {String} histId + * The histogram ID. + * @param {Array|String|Boolean} actual + * The value of the histogram data. + */ + displayDataFromHistogramSnapshot(snapshot, key, histId, actual) { + key = key ? `"${key}"` : `""`; + + switch (snapshot.histogram_type) { + case Services.telemetry.HISTOGRAM_EXPONENTIAL: + case Services.telemetry.HISTOGRAM_LINEAR: + let total = 0; + for (const val of Object.values(actual)) { + total += val; + } + + if (histId.endsWith("_ENUMERATED")) { + if (total > 0) { + actual = actual.toSource(); + dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`); + } + return; + } + + dump(`checkTelemetry("${histId}", ${key}, null, "hasentries");\n`); + break; + case Services.telemetry.HISTOGRAM_BOOLEAN: + actual = actual.toSource(); + + if (actual !== "({})") { + dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`); + } + break; + case Services.telemetry.HISTOGRAM_FLAG: + actual = actual.toSource(); + + if (actual !== "({0:1, 1:0})") { + dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`); + } + break; + case Services.telemetry.HISTOGRAM_COUNT: + actual = actual.toSource(); + + dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`); + break; + } + } +} + +// "exports"... because this is a helper and not imported via require we need to +// expose the three main methods that should be used by tests. The reason this +// is not imported via require is because it needs access to test methods +// (is, ok etc). + +/* eslint-disable no-unused-vars */ +const telemetryHelpers = new TelemetryHelpers(); +const generateTelemetryTests = telemetryHelpers.generateTelemetryTests; +const checkTelemetry = telemetryHelpers.checkTelemetry; +const startTelemetry = telemetryHelpers.startTelemetry; +/* eslint-enable no-unused-vars */ |