Bug 1549970 - Add test for aboutdebugging connection_attempt telemetry events r=daisuke
authorJulian Descottes <jdescottes@mozilla.com>
Mon, 13 May 2019 12:02:28 +0000
changeset 532416 c2c7d0b11f1365b96292f925ba1a24270d0b819c
parent 532415 1abd79d8d7da4aa6d94fb1ac3fbbe137fbc18edd
child 532417 f9170a14e63672ffff429d81c76ffd5c49f9936f
push id11268
push usercsabou@mozilla.com
push dateTue, 14 May 2019 15:24:22 +0000
treeherdermozilla-beta@5fb7fcd568d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaisuke
bugs1549970
milestone68.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 1549970 - Add test for aboutdebugging connection_attempt telemetry events r=daisuke Depends on D30373 Differential Revision: https://phabricator.services.mozilla.com/D30374
devtools/client/aboutdebugging-new/test/browser/browser.ini
devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_telemetry_connection_attempt.js
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -105,16 +105,17 @@ skip-if = debug || asan # Frequent inter
 [browser_aboutdebugging_sidebar_usb_runtime_refresh.js]
 [browser_aboutdebugging_sidebar_usb_runtime_select.js]
 [browser_aboutdebugging_sidebar_usb_status.js]
 [browser_aboutdebugging_sidebar_usb_unavailable_runtime.js]
 [browser_aboutdebugging_sidebar_usb_unplugged_device.js]
 [browser_aboutdebugging_hidden_addons.js]
 [browser_aboutdebugging_tab_favicons.js]
 [browser_aboutdebugging_telemetry_basic.js]
+[browser_aboutdebugging_telemetry_connection_attempt.js]
 [browser_aboutdebugging_telemetry_inspect.js]
 [browser_aboutdebugging_telemetry_navigate.js]
 [browser_aboutdebugging_telemetry_runtime_actions.js]
 [browser_aboutdebugging_telemetry_runtime_connected_details.js]
 [browser_aboutdebugging_telemetry_runtime_updates.js]
 [browser_aboutdebugging_telemetry_runtime_updates_multi.js]
 [browser_aboutdebugging_telemetry_runtime_updates_network.js]
 [browser_aboutdebugging_thisfirefox.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_telemetry_connection_attempt.js
@@ -0,0 +1,183 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from helper-telemetry.js */
+Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-telemetry.js", this);
+
+const USB_RUNTIME = {
+  id: "runtime-id-1",
+  deviceName: "Device A",
+  name: "Runtime 1",
+  shortName: "R1",
+};
+
+/**
+ * Check that telemetry events for connection attempts are correctly recorded in various
+ * scenarios:
+ * - successful connection
+ * - successful connection after showing the timeout warning
+ * - failed connection
+ * - connection timeout
+ */
+add_task(async function testSuccessfulConnectionAttempt() {
+  const { doc, mocks, runtimeId, sessionId, tab } = await setupConnectionAttemptTest();
+
+  await connectToRuntime(USB_RUNTIME.deviceName, doc);
+
+  const connectionEvents = checkTelemetryEvents([
+    { method: "runtime_connected", extras: { runtime_id: runtimeId } },
+    { method: "connection_attempt", extras: getEventExtras("start", runtimeId) },
+    { method: "connection_attempt", extras: getEventExtras("success", runtimeId) },
+  ], sessionId).filter(({method}) => method === "connection_attempt");
+
+  checkConnectionId(connectionEvents);
+
+  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
+  await removeTab(tab);
+});
+
+add_task(async function testFailedConnectionAttempt() {
+  const { doc, mocks, runtimeId, sessionId, tab } = await setupConnectionAttemptTest();
+  mocks.runtimeClientFactoryMock.createClientForRuntime = async (runtime) => {
+    throw new Error("failed");
+  };
+
+  info("Try to connect to the runtime and wait for the connection error message");
+  const usbRuntimeSidebarItem = findSidebarItemByText(USB_RUNTIME.deviceName, doc);
+  const connectButton = usbRuntimeSidebarItem.querySelector(".qa-connect-button");
+  connectButton.click();
+  await waitUntil(() => usbRuntimeSidebarItem.querySelector(".qa-connection-error"));
+
+  const connectionEvents = checkTelemetryEvents([
+    { method: "connection_attempt", extras: getEventExtras("start", runtimeId) },
+    { method: "connection_attempt", extras: getEventExtras("failed", runtimeId) },
+  ], sessionId).filter(({method}) => method === "connection_attempt");
+
+  checkConnectionId(connectionEvents);
+
+  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
+  await removeTab(tab);
+});
+
+add_task(async function testPendingConnectionAttempt() {
+  info("Set timeout preferences to avoid cancelling the connection");
+  await pushPref("devtools.aboutdebugging.test-connection-timing-out-delay", 100);
+  await pushPref("devtools.aboutdebugging.test-connection-cancel-delay", 100000);
+
+  const { doc, mocks, runtimeId, sessionId, tab } = await setupConnectionAttemptTest();
+
+  info("Simulate a pending connection");
+  let resumeConnection;
+  const resumeConnectionPromise = new Promise(r => {
+    resumeConnection = r;
+  });
+  mocks.runtimeClientFactoryMock.createClientForRuntime = async (runtime) => {
+    await resumeConnectionPromise;
+    return mocks._clients[runtime.type][runtime.id];
+  };
+
+  info("Click on the connect button and wait for the warning message");
+  const usbRuntimeSidebarItem = findSidebarItemByText(USB_RUNTIME.deviceName, doc);
+  const connectButton = usbRuntimeSidebarItem.querySelector(".qa-connect-button");
+  connectButton.click();
+  await waitUntil(() => doc.querySelector(".qa-connection-not-responding"));
+
+  info("Resume the connection and wait for the connection to succeed");
+  resumeConnection();
+  await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".qa-connect-button"));
+
+  const connectionEvents = checkTelemetryEvents([
+    { method: "runtime_connected", extras: { runtime_id: runtimeId } },
+    { method: "connection_attempt", extras: getEventExtras("start", runtimeId) },
+    { method: "connection_attempt", extras: getEventExtras("not responding", runtimeId) },
+    { method: "connection_attempt", extras: getEventExtras("success", runtimeId) },
+  ], sessionId).filter(({method}) => method === "connection_attempt");
+  checkConnectionId(connectionEvents);
+
+  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
+  await removeTab(tab);
+});
+
+add_task(async function testCancelledConnectionAttempt() {
+  info("Set timeout preferences to quickly cancel the connection");
+  await pushPref("devtools.aboutdebugging.test-connection-timing-out-delay", 100);
+  await pushPref("devtools.aboutdebugging.test-connection-cancel-delay", 1000);
+
+  const { doc, mocks, runtimeId, sessionId, tab } = await setupConnectionAttemptTest();
+
+  info("Simulate a connection timeout");
+  mocks.runtimeClientFactoryMock.createClientForRuntime = async (runtime) => {
+    await new Promise(r => {});
+  };
+
+  info("Click on the connect button and wait for the error message");
+  const usbRuntimeSidebarItem = findSidebarItemByText(USB_RUNTIME.deviceName, doc);
+  const connectButton = usbRuntimeSidebarItem.querySelector(".qa-connect-button");
+  connectButton.click();
+  await waitUntil(() => usbRuntimeSidebarItem.querySelector(".qa-connection-timeout"));
+
+  const connectionEvents = checkTelemetryEvents([
+    { method: "connection_attempt", extras: getEventExtras("start", runtimeId) },
+    { method: "connection_attempt", extras: getEventExtras("not responding", runtimeId) },
+    { method: "connection_attempt", extras: getEventExtras("cancelled", runtimeId) },
+  ], sessionId).filter(({method}) => method === "connection_attempt");
+  checkConnectionId(connectionEvents);
+
+  await removeUsbRuntime(USB_RUNTIME, mocks, doc);
+  await removeTab(tab);
+});
+
+function checkConnectionId(connectionEvents) {
+  const connectionId = connectionEvents[0].extras.connection_id;
+  ok(!!connectionId, "Found a valid connection id in the first connection_attempt event");
+  for (const evt of connectionEvents) {
+    is(evt.extras.connection_id, connectionId,
+      "All connection_attempt events share the same connection id");
+  }
+}
+
+// Small helper to create the expected event extras object for connection_attempt events
+function getEventExtras(status, runtimeId) {
+  return {
+    connection_type: "usb",
+    runtime_id: runtimeId,
+    status,
+  };
+}
+
+// Open about:debugging, setup telemetry, mocks and create a mocked USB runtime.
+async function setupConnectionAttemptTest() {
+  const mocks = new Mocks();
+  setupTelemetryTest();
+
+  const { tab, document } = await openAboutDebugging();
+
+  const sessionId = getOpenEventSessionId();
+  ok(!isNaN(sessionId), "Open event has a valid session id");
+
+  mocks.createUSBRuntime(USB_RUNTIME.id, {
+    deviceName: USB_RUNTIME.deviceName,
+    name: USB_RUNTIME.name,
+    shortName: USB_RUNTIME.shortName,
+  });
+  mocks.emitUSBUpdate();
+
+  info("Wait for the runtime to appear in the sidebar");
+  await waitUntil(() => findSidebarItemByText(USB_RUNTIME.shortName, document));
+  const evts = checkTelemetryEvents([
+    { method: "device_added", extras: {} },
+    { method: "runtime_added", extras: {} },
+  ], sessionId);
+
+  const runtimeId = evts.filter(e => e.method === "runtime_added")[0].extras.runtime_id;
+  return { doc: document, mocks, runtimeId, sessionId, tab };
+}
+
+async function removeUsbRuntime(runtime, mocks, doc) {
+  mocks.removeRuntime(runtime.id);
+  mocks.emitUSBUpdate();
+  await waitUntil(() => !findSidebarItemByText(runtime.name, doc) &&
+                        !findSidebarItemByText(runtime.shortName, doc));
+}