Bug 1347517 - Add telemetry for GCLI commands used r?jwalker
MozReview-Commit-ID: FBBsZeZkPEt
--- a/devtools/client/commandline/test/browser.ini
+++ b/devtools/client/commandline/test/browser.ini
@@ -113,13 +113,14 @@ skip-if = true # Bug 1093205 - Test does
[browser_gcli_keyboard4.js]
[browser_gcli_keyboard5.js]
[browser_gcli_menu.js]
[browser_gcli_node.js]
[browser_gcli_resource.js]
[browser_gcli_short.js]
[browser_gcli_spell.js]
[browser_gcli_split.js]
+[browser_gcli_telemetry.js]
[browser_gcli_tokenize.js]
[browser_gcli_tooltip.js]
skip-if = true # Bug 1093205 - Test does not run in Firefox due to missing terminal
[browser_gcli_types.js]
[browser_gcli_union.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/commandline/test/browser_gcli_telemetry.js
@@ -0,0 +1,160 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global helpers, btoa, whenDelayedStartupFinished, OpenBrowserWindow */
+
+// Test that GCLI telemetry works properly
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,browser_gcli_telemetry.js";
+const COMMAND_HISTOGRAM_ID = "DEVTOOLS_GCLI_COMMANDS_KEYED";
+
+function test() {
+ return Task.spawn(spawnTest).then(finish, helpers.handleError);
+}
+
+function* spawnTest() {
+ let options = yield helpers.openTab(TEST_URI);
+ let Telemetry = loadTelemetryAndRecordLogs();
+
+ yield helpers.openToolbar(options);
+
+ yield helpers.audit(options, [
+ {
+ setup: "addon list<RETURN>"
+ },
+ {
+ setup: "appcache clear<RETURN>"
+ },
+ {
+ setup: "clear<RETURN>"
+ },
+ {
+ setup: "console clear<RETURN>"
+ },
+ {
+ setup: "cookie list<RETURN>"
+ },
+ {
+ setup: "help<RETURN>"
+ },
+ {
+ setup: "help addon<RETURN>"
+ },
+ {
+ setup: "screenshot<RETURN>"
+ },
+ {
+ setup: "listen 6000<RETURN>"
+ },
+ {
+ setup: "unlisten<RETURN>"
+ },
+ {
+ setup: "context addon<RETURN>"
+ },
+ ]);
+
+ let results = Telemetry.prototype.telemetryInfo;
+
+ checkTelemetryResults(results);
+ stopRecordingTelemetryLogs(Telemetry);
+
+ info("Closing Developer Toolbar");
+ yield helpers.closeToolbar(options);
+
+ info("Closing tab");
+ yield helpers.closeTab(options);
+}
+
+/**
+ * Load the Telemetry utils, then stub Telemetry.prototype.log and
+ * Telemetry.prototype.logKeyed in order to record everything that's logged in
+ * it.
+ * Store all recordings in Telemetry.telemetryInfo.
+ * @return {Telemetry}
+ */
+function loadTelemetryAndRecordLogs() {
+ info("Mock the Telemetry log function to record logged information");
+
+ let Telemetry = require("devtools/client/shared/telemetry");
+
+ Telemetry.prototype.telemetryInfo = {};
+ Telemetry.prototype._oldlog = Telemetry.prototype.log;
+ Telemetry.prototype.log = function (histogramId, value) {
+ if (!this.telemetryInfo) {
+ // Telemetry instance still in use after stopRecordingTelemetryLogs
+ return;
+ }
+ if (histogramId) {
+ if (!this.telemetryInfo[histogramId]) {
+ this.telemetryInfo[histogramId] = [];
+ }
+ this.telemetryInfo[histogramId].push(value);
+ }
+ };
+ Telemetry.prototype._oldlogKeyed = Telemetry.prototype.logKeyed;
+ Telemetry.prototype.logKeyed = function (histogramId, key, value) {
+ this.log(`${histogramId}|${key}`, value);
+ };
+
+ return Telemetry;
+}
+
+/**
+ * Stop recording the Telemetry logs and put back the utils as it was before.
+ * @param {Telemetry} Required Telemetry
+ * Telemetry object that needs to be stopped.
+ */
+function stopRecordingTelemetryLogs(Telemetry) {
+ info("Stopping Telemetry");
+ Telemetry.prototype.log = Telemetry.prototype._oldlog;
+ Telemetry.prototype.logKeyed = Telemetry.prototype._oldlogKeyed;
+ delete Telemetry.prototype._oldlog;
+ delete Telemetry.prototype._oldlogKeyed;
+ delete Telemetry.prototype.telemetryInfo;
+}
+
+function checkTelemetryResults(results) {
+ let prefix = COMMAND_HISTOGRAM_ID + "|";
+ let keys = Object.keys(results).filter(result => {
+ return result.startsWith(prefix);
+ });
+
+ let commands = [
+ "addon list",
+ "appcache clear",
+ "clear",
+ "console clear",
+ "cookie list",
+ "screenshot",
+ "listen",
+ "unlisten",
+ "context",
+ "help"
+ ];
+
+ for (let command of commands) {
+ let key = prefix + command;
+
+ switch (key) {
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|addon list":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|appcache clear":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|clear":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|console clear":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|cookie list":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|screenshot":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|listen":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|unlisten":
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|context":
+ is(results[key].length, 1, `${key} is correct`);
+ break;
+ case "DEVTOOLS_GCLI_COMMANDS_KEYED|help":
+ is(results[key].length, 2, `${key} is correct`);
+ break;
+ default:
+ ok(false, `No telemetry pings were sent for command "${command}"`);
+ }
+ }
+}
--- a/devtools/client/commandline/test/head.js
+++ b/devtools/client/commandline/test/head.js
@@ -1,11 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ /* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+ /* import-globals-from helpers.js */
+ /* import-globals-from mockCommands.js */
+
+"use strict";
const TEST_BASE_HTTP = "http://example.com/browser/devtools/client/commandline/test/";
const TEST_BASE_HTTPS = "https://example.com/browser/devtools/client/commandline/test/";
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var { console } = require("resource://gre/modules/Console.jsm");
var flags = require("devtools/shared/flags");
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -273,24 +273,29 @@ Telemetry.prototype = {
/**
* Log a value to a keyed histogram.
*
* @param {String} histogramId
* Histogram in which the data is to be stored.
* @param {String} key
* The key within the single histogram.
- * @param value
- * Value to store.
+ * @param [value]
+ * Optional value to store.
*/
logKeyed: function (histogramId, key, value) {
if (histogramId) {
try {
let histogram = Services.telemetry.getKeyedHistogramById(histogramId);
- histogram.add(key, value);
+
+ if (typeof value === "undefined") {
+ histogram.add(key);
+ } else {
+ histogram.add(key, value);
+ }
} catch (e) {
dump("Warning: An attempt was made to write to the " + histogramId +
" histogram, which is not defined in Histograms.json\n");
}
}
},
/**
@@ -317,9 +322,8 @@ Telemetry.prototype = {
},
destroy: function () {
for (let histogramId of this._timers.keys()) {
this.stopTimer(histogramId);
}
}
};
-
--- a/devtools/shared/gcli/source/lib/gcli/mozui/inputter.js
+++ b/devtools/shared/gcli/source/lib/gcli/mozui/inputter.js
@@ -17,16 +17,18 @@
'use strict';
var util = require('../util/util');
var KeyEvent = require('../util/util').KeyEvent;
var Status = require('../types/types').Status;
var History = require('../ui/history').History;
+var Telemetry = require("devtools/client/shared/telemetry");
+
var RESOLVED = Promise.resolve(true);
/**
* A wrapper to take care of the functions concerning an input element
* @param components Object that links to other UI components. GCLI provided:
* - requisition
* - focusManager
* - element
@@ -42,16 +44,19 @@ function Inputter(components) {
this.document = this.element.ownerDocument;
// Used to distinguish focus from TAB in CLI. See onKeyUp()
this.lastTabDownAt = 0;
// Used to effect caret changes. See _processCaretChange()
this._caretChange = null;
+ // Use telemetry
+ this._telemetry = new Telemetry();
+
// Ensure that TAB/UP/DOWN isn't handled by the browser
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.element.addEventListener('keydown', this.onKeyDown);
this.element.addEventListener('keyup', this.onKeyUp);
// Setup History
this.history = new History();
@@ -117,16 +122,17 @@ Inputter.prototype.destroy = function()
this.outputted = undefined;
this.onMouseUp = undefined;
this.onKeyDown = undefined;
this.onKeyUp = undefined;
this.onWindowResize = undefined;
this.tooltip = undefined;
this.document = undefined;
this.element = undefined;
+ this._telemetry = undefined;
};
/**
* Make ourselves visually similar to the input element, and make the input
* element transparent so our background shines through
*/
Inputter.prototype.onWindowResize = function() {
// Mochitest sometimes causes resize after shutdown. See Bug 743190
@@ -552,16 +558,19 @@ Inputter.prototype._handleDownArrow = fu
* RETURN checks status and might exec
*/
Inputter.prototype._handleReturn = function() {
// Deny RETURN unless the command might work
if (this.requisition.status === Status.VALID) {
this._scrollingThroughHistory = false;
this.history.add(this.element.value);
+ let name = this.requisition.commandAssignment.value.name;
+ this._telemetry.logKeyed("DEVTOOLS_GCLI_COMMANDS_KEYED", name);
+
return this.requisition.exec().then(function() {
this.textChanged();
}.bind(this));
}
// If we can't execute the command, but there is a menu choice to use
// then use it.
if (!this.tooltip.selectChoice()) {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8640,16 +8640,24 @@
"alert_emails": ["dev-developer-tools@lists.mozilla.org"],
"expires_in_version": "58",
"kind": "enumerated",
"bug_numbers": [1205845],
"n_values": 9,
"releaseChannelCollection": "opt-out",
"description": "Records DevTools toolbox host each time the toolbox is opened and when the host is changed (0:Bottom, 1:Side, 2:Window, 3:Custom, 9:Unknown)."
},
+ "DEVTOOLS_GCLI_COMMANDS_KEYED": {
+ "bug_numbers": [1347517],
+ "alert_emails": ["dev-developer-tools@lists.mozilla.org"],
+ "expires_in_version": "never",
+ "keyed": true,
+ "kind": "count",
+ "description": "Reports the command name used in GCLI e.g. 'screenshot'"
+ },
"VIEW_SOURCE_IN_BROWSER_OPENED_BOOLEAN": {
"alert_emails": ["mozilla-dev-developer-tools@lists.mozilla.org", "jryans@mozilla.com"],
"expires_in_version": "53",
"kind": "boolean",
"description": "How many times has view source in browser / tab been opened?"
},
"VIEW_SOURCE_IN_WINDOW_OPENED_BOOLEAN": {
"alert_emails": ["mozilla-dev-developer-tools@lists.mozilla.org", "jryans@mozilla.com"],