summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/tests/browser
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/telemetry/tests/browser')
-rw-r--r--toolkit/components/telemetry/tests/browser/browser.ini13
-rw-r--r--toolkit/components/telemetry/tests/browser/browser_DynamicScalars.js243
-rw-r--r--toolkit/components/telemetry/tests/browser/browser_UpdatePingSuccess.js165
-rw-r--r--toolkit/components/telemetry/tests/browser/browser_media_element_in_page_scalar.js128
-rw-r--r--toolkit/components/telemetry/tests/browser/file_iframe.html9
-rw-r--r--toolkit/components/telemetry/tests/browser/file_media.html9
-rw-r--r--toolkit/components/telemetry/tests/browser/gizmo.mp4bin0 -> 455255 bytes
7 files changed, 567 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/tests/browser/browser.ini b/toolkit/components/telemetry/tests/browser/browser.ini
new file mode 100644
index 0000000000..4efecc0352
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/browser.ini
@@ -0,0 +1,13 @@
+# 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/.
+[DEFAULT]
+support-files =
+ file_iframe.html
+ file_media.html
+ gizmo.mp4
+
+[browser_UpdatePingSuccess.js]
+[browser_DynamicScalars.js]
+skip-if = verify
+[browser_media_element_in_page_scalar.js]
diff --git a/toolkit/components/telemetry/tests/browser/browser_DynamicScalars.js b/toolkit/components/telemetry/tests/browser/browser_DynamicScalars.js
new file mode 100644
index 0000000000..74a7e48695
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/browser_DynamicScalars.js
@@ -0,0 +1,243 @@
+"use strict";
+
+const { TelemetryController } = ChromeUtils.importESModule(
+ "resource://gre/modules/TelemetryController.sys.mjs"
+);
+const { TelemetryUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/TelemetryUtils.sys.mjs"
+);
+
+const CONTENT_CREATED = "ipc:content-created";
+
+async function waitForProcessesScalars(
+ aProcesses,
+ aKeyed,
+ aAdditionalCondition = data => true
+) {
+ await TestUtils.waitForCondition(() => {
+ const scalars = aKeyed
+ ? Services.telemetry.getSnapshotForKeyedScalars("main", false)
+ : Services.telemetry.getSnapshotForScalars("main", false);
+ return (
+ aProcesses.every(p => Object.keys(scalars).includes(p)) &&
+ aAdditionalCondition(scalars)
+ );
+ });
+}
+
+add_task(async function test_setup() {
+ // Make sure the newly spawned content processes will have extended Telemetry enabled.
+ // Since Telemetry reads the prefs only at process startup, flush all cached
+ // and preallocated processes so they pick up the setting.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [TelemetryUtils.Preferences.OverridePreRelease, true],
+ ["dom.ipc.processPrelaunch.enabled", false],
+ ],
+ });
+ Services.ppmm.releaseCachedProcesses();
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.ipc.processPrelaunch.enabled", true]],
+ });
+
+ // And take care of the already initialized one as well.
+ let canRecordExtended = Services.telemetry.canRecordExtended;
+ Services.telemetry.canRecordExtended = true;
+ registerCleanupFunction(
+ () => (Services.telemetry.canRecordExtended = canRecordExtended)
+ );
+});
+
+add_task(async function test_recording() {
+ let currentPid = gBrowser.selectedBrowser.frameLoader.remoteTab.osPid;
+
+ // Register test scalars before spawning the content process: the scalar
+ // definitions will propagate to it.
+ Services.telemetry.registerScalars("telemetry.test.dynamic", {
+ pre_content_spawn: {
+ kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
+ keyed: false,
+ record_on_release: true,
+ },
+ pre_content_spawn_expiration: {
+ kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
+ keyed: false,
+ record_on_release: true,
+ },
+ });
+
+ Services.telemetry.scalarSet(
+ "telemetry.test.dynamic.pre_content_spawn_expiration",
+ 3
+ );
+
+ let processCreated = TestUtils.topicObserved(CONTENT_CREATED);
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank", forceNewProcess: true },
+ async function (browser) {
+ // Make sure our new browser is in its own process. The processCreated
+ // promise should have already resolved by this point.
+ await processCreated;
+ let newPid = browser.frameLoader.remoteTab.osPid;
+ ok(currentPid != newPid, "The new tab must spawn its own process");
+
+ // Register test scalars after spawning the content process: the scalar
+ // definitions will propagate to it.
+ // Also attempt to register again "pre_content_spawn_expiration" and set
+ // it to expired.
+ Services.telemetry.registerScalars("telemetry.test.dynamic", {
+ post_content_spawn: {
+ kind: Ci.nsITelemetry.SCALAR_TYPE_BOOLEAN,
+ keyed: false,
+ record_on_release: false,
+ },
+ post_content_spawn_keyed: {
+ kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
+ keyed: true,
+ record_on_release: true,
+ },
+ pre_content_spawn_expiration: {
+ kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
+ keyed: false,
+ record_on_release: true,
+ expired: true,
+ },
+ });
+
+ // Accumulate from the content process into both dynamic scalars.
+ await SpecialPowers.spawn(browser, [], async function () {
+ Services.telemetry.scalarAdd(
+ "telemetry.test.dynamic.pre_content_spawn_expiration",
+ 1
+ );
+ Services.telemetry.scalarSet(
+ "telemetry.test.dynamic.pre_content_spawn",
+ 3
+ );
+ Services.telemetry.scalarSet(
+ "telemetry.test.dynamic.post_content_spawn",
+ true
+ );
+ Services.telemetry.keyedScalarSet(
+ "telemetry.test.dynamic.post_content_spawn_keyed",
+ "testKey",
+ 3
+ );
+ });
+ }
+ );
+
+ // Wait for the dynamic scalars to appear non-keyed snapshots.
+ await waitForProcessesScalars(["dynamic"], true, scalars => {
+ // Wait for the scalars set in the content process to be available.
+ return "telemetry.test.dynamic.post_content_spawn_keyed" in scalars.dynamic;
+ });
+
+ // Verify the content of the snapshots.
+ const scalars = Services.telemetry.getSnapshotForScalars("main", false);
+ ok(
+ "dynamic" in scalars,
+ "The scalars must contain the 'dynamic' process section"
+ );
+ ok(
+ "telemetry.test.dynamic.pre_content_spawn" in scalars.dynamic,
+ "Dynamic scalars registered before a process spawns must be present."
+ );
+ is(
+ scalars.dynamic["telemetry.test.dynamic.pre_content_spawn"],
+ 3,
+ "The dynamic scalar must contain the expected value."
+ );
+ is(
+ scalars.dynamic["telemetry.test.dynamic.pre_content_spawn_expiration"],
+ 3,
+ "The dynamic scalar must not be updated after being expired."
+ );
+ ok(
+ "telemetry.test.dynamic.post_content_spawn" in scalars.dynamic,
+ "Dynamic scalars registered after a process spawns must be present."
+ );
+ is(
+ scalars.dynamic["telemetry.test.dynamic.post_content_spawn"],
+ true,
+ "The dynamic scalar must contain the expected value."
+ );
+
+ // Wait for the dynamic scalars to appear in the keyed snapshots.
+ await waitForProcessesScalars(["dynamic"], true);
+
+ const keyedScalars = Services.telemetry.getSnapshotForKeyedScalars(
+ "main",
+ false
+ );
+ ok(
+ "dynamic" in keyedScalars,
+ "The keyed scalars must contain the 'dynamic' process section"
+ );
+ ok(
+ "telemetry.test.dynamic.post_content_spawn_keyed" in keyedScalars.dynamic,
+ "Dynamic keyed scalars registered after a process spawns must be present."
+ );
+ is(
+ keyedScalars.dynamic["telemetry.test.dynamic.post_content_spawn_keyed"]
+ .testKey,
+ 3,
+ "The dynamic keyed scalar must contain the expected value."
+ );
+});
+
+add_task(async function test_aggregation() {
+ Services.telemetry.clearScalars();
+
+ // Register test scalars before spawning the content process: the scalar
+ // definitions will propagate to it. Also cheat TelemetrySession to put
+ // the test scalar in the payload by using "cheattest" instead of "test" in
+ // the scalar category name.
+ Services.telemetry.registerScalars("telemetry.cheattest.dynamic", {
+ test_aggregation: {
+ kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
+ keyed: false,
+ record_on_release: true,
+ },
+ });
+
+ const SCALAR_FULL_NAME = "telemetry.cheattest.dynamic.test_aggregation";
+ Services.telemetry.scalarAdd(SCALAR_FULL_NAME, 1);
+
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank", forceNewProcess: true },
+ async function (browser) {
+ // Accumulate from the content process into both dynamic scalars.
+ await SpecialPowers.spawn(
+ browser,
+ [SCALAR_FULL_NAME],
+ async function (aName) {
+ Services.telemetry.scalarAdd(aName, 3);
+ }
+ );
+ }
+ );
+
+ // Wait for the dynamic scalars to appear. Since we're testing that children
+ // and parent data get aggregated, we might need to wait a bit more:
+ // TelemetryIPCAccumulator.cpp sends batches to the parent process every 2 seconds.
+ await waitForProcessesScalars(["dynamic"], false, scalarData => {
+ return (
+ "dynamic" in scalarData &&
+ SCALAR_FULL_NAME in scalarData.dynamic &&
+ scalarData.dynamic[SCALAR_FULL_NAME] == 4
+ );
+ });
+
+ // Check that the definitions made it to the ping payload.
+ const pingData = TelemetryController.getCurrentPingData(true);
+ ok(
+ "dynamic" in pingData.payload.processes,
+ "The ping payload must contain the 'dynamic' process section"
+ );
+ is(
+ pingData.payload.processes.dynamic.scalars[SCALAR_FULL_NAME],
+ 4,
+ "The dynamic scalar must contain the aggregated parent and children data."
+ );
+});
diff --git a/toolkit/components/telemetry/tests/browser/browser_UpdatePingSuccess.js b/toolkit/components/telemetry/tests/browser/browser_UpdatePingSuccess.js
new file mode 100644
index 0000000000..09e5e3e62e
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/browser_UpdatePingSuccess.js
@@ -0,0 +1,165 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+"use strict";
+
+const { TelemetryUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/TelemetryUtils.sys.mjs"
+);
+const { TelemetryArchiveTesting } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryArchiveTesting.sys.mjs"
+);
+
+add_task(async function test_updatePing() {
+ const TEST_VERSION = "37.85";
+ const TEST_BUILDID = "20150711123724";
+ const XML_UPDATE = `<?xml version="1.0"?>
+ <updates xmlns="http://www.mozilla.org/2005/app-update">
+ <update appVersion="${Services.appinfo.version}" buildID="20080811053724"
+ channel="nightly" displayVersion="Version 1.0"
+ installDate="1238441400314" isCompleteUpdate="true" type="minor"
+ name="Update Test 1.0" detailsURL="http://example.com/"
+ previousAppVersion="${TEST_VERSION}"
+ serviceURL="https://example.com/" foregroundDownload="true"
+ statusText="The Update was successfully installed">
+ <patch type="complete" URL="http://example.com/" size="775"
+ selected="true" state="succeeded"/>
+ </update>
+ </updates>`;
+
+ // Set the preferences needed for the test: they will be cleared up
+ // after it runs.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [TelemetryUtils.Preferences.UpdatePing, true],
+ ["browser.startup.homepage_override.mstone", TEST_VERSION],
+ ["browser.startup.homepage_override.buildID", TEST_BUILDID],
+ ["toolkit.telemetry.log.level", "Trace"],
+ ],
+ });
+
+ registerCleanupFunction(async () => {
+ let activeUpdateFile = getActiveUpdateFile();
+ activeUpdateFile.remove(false);
+ reloadUpdateManagerData(true);
+ });
+ writeUpdatesToXMLFile(XML_UPDATE);
+ reloadUpdateManagerData(false);
+
+ // Start monitoring the ping archive.
+ let archiveChecker = new TelemetryArchiveTesting.Checker();
+ await archiveChecker.promiseInit();
+
+ // Manually call the BrowserContentHandler: this automatically gets called when
+ // the browser is started and an update was applied successfully in order to
+ // display the "update" info page.
+ Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
+
+ // We cannot control when the ping will be generated/archived after we trigger
+ // an update, so let's make sure to have one before moving on with validation.
+ let updatePing;
+ await BrowserTestUtils.waitForCondition(
+ async function () {
+ // Check that the ping made it into the Telemetry archive.
+ // The test data is defined in ../data/sharedUpdateXML.js
+ updatePing = await archiveChecker.promiseFindPing("update", [
+ [["payload", "reason"], "success"],
+ [["payload", "previousBuildId"], TEST_BUILDID],
+ [["payload", "previousVersion"], TEST_VERSION],
+ ]);
+ return !!updatePing;
+ },
+ "Make sure the ping is generated before trying to validate it.",
+ 500,
+ 100
+ );
+
+ ok(updatePing, "The 'update' ping must be correctly sent.");
+
+ // We have no easy way to simulate a previously applied update from toolkit/telemetry.
+ // Instead of moving this test to mozapps/update as well, just test that the
+ // "previousChannel" field is present and either a string or null.
+ ok(
+ "previousChannel" in updatePing.payload,
+ "The payload must contain the 'previousChannel' field"
+ );
+ const channelField = updatePing.payload.previousChannel;
+ if (channelField != null) {
+ ok(
+ typeof channelField == "string",
+ "'previousChannel' must be a string, if available."
+ );
+ }
+
+ // Also make sure that the ping contains both a client id and an
+ // environment section.
+ ok("clientId" in updatePing, "The update ping must report a client id.");
+ ok(
+ "environment" in updatePing,
+ "The update ping must report the environment."
+ );
+});
+
+/**
+ * Removes the updates.xml file and returns the nsIFile for the
+ * active-update.xml file.
+ *
+ * @return The nsIFile for the active-update.xml file.
+ */
+function getActiveUpdateFile() {
+ let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
+ let updatesFile = updateRootDir.clone();
+ updatesFile.append("updates.xml");
+ if (updatesFile.exists()) {
+ // The following is non-fatal.
+ try {
+ updatesFile.remove(false);
+ } catch (e) {}
+ }
+ let activeUpdateFile = updateRootDir.clone();
+ activeUpdateFile.append("active-update.xml");
+ return activeUpdateFile;
+}
+
+/**
+ * Reloads the update xml files.
+ *
+ * @param skipFiles (optional)
+ * If true, the update xml files will not be read and the metadata will
+ * be reset. If false (the default), the update xml files will be read
+ * to populate the update metadata.
+ */
+function reloadUpdateManagerData(skipFiles = false) {
+ Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager)
+ .QueryInterface(Ci.nsIObserver)
+ .observe(null, "um-reload-update-data", skipFiles ? "skip-files" : "");
+}
+
+/**
+ * Writes the updates specified to the active-update.xml file.
+ *
+ * @param aText
+ * The updates represented as a string to write to the active-update.xml
+ * file.
+ */
+function writeUpdatesToXMLFile(aText) {
+ const PERMS_FILE = 0o644;
+
+ const MODE_WRONLY = 0x02;
+ const MODE_CREATE = 0x08;
+ const MODE_TRUNCATE = 0x20;
+
+ let activeUpdateFile = getActiveUpdateFile();
+ if (!activeUpdateFile.exists()) {
+ activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+ let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+ Ci.nsIFileOutputStream
+ );
+ let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+ fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
+ fos.write(aText, aText.length);
+ fos.close();
+}
diff --git a/toolkit/components/telemetry/tests/browser/browser_media_element_in_page_scalar.js b/toolkit/components/telemetry/tests/browser/browser_media_element_in_page_scalar.js
new file mode 100644
index 0000000000..3c9dd8f8ee
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/browser_media_element_in_page_scalar.js
@@ -0,0 +1,128 @@
+"use strict";
+
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+const LOCATION =
+ "https://example.com/browser/toolkit/components/telemetry/tests/browser/";
+const CORS_LOCATION =
+ "https://example.org/browser/toolkit/components/telemetry/tests/browser/";
+const MEDIA_SCALAR_NAME = "media.element_in_page_count";
+
+/**
+ * 'media.element_in_page_count' is a permanant scalar, this test is used to
+ * check if that scalar can be accumulated correctly under different situations.
+ */
+add_task(async function start_tests() {
+ // Clean all scalars first to prevent being interfered by former test.
+ TelemetryTestUtils.getProcessScalars("parent", false, true /* clear */);
+
+ await testMediaInPageScalar({
+ description: "load a page with one media element",
+ url: "file_media.html",
+ expectedScalarCount: 1,
+ });
+ await testMediaInPageScalar({
+ description: "load a page with multiple media elements",
+ url: "file_media.html",
+ options: {
+ createSecondMedia: true,
+ },
+ expectedScalarCount: 1,
+ });
+ await testMediaInPageScalar({
+ description: "load a page with media element created from iframe",
+ url: "file_iframe.html",
+ options: {
+ iframeUrl: "file_media.html",
+ },
+ expectedScalarCount: 1,
+ });
+ await testMediaInPageScalar({
+ description: "load a page with media element created from CORS iframe",
+ url: "file_iframe.html",
+ options: {
+ iframeUrl: "file_media.html",
+ CORSIframe: true,
+ },
+ expectedScalarCount: 1,
+ });
+ await testMediaInPageScalar({
+ description: "run multiple tabs, all loading media page",
+ url: "file_media.html",
+ options: {
+ tabNums: 2,
+ },
+ expectedScalarCount: 2,
+ });
+});
+
+async function testMediaInPageScalar({
+ description,
+ url,
+ options,
+ expectedScalarCount,
+} = {}) {
+ info(`media scalar should be undefined in the start`);
+ let scalars = TelemetryTestUtils.getProcessScalars("parent");
+ is(scalars[MEDIA_SCALAR_NAME], undefined, "has not created media scalar yet");
+
+ info(`run test '${description}'`);
+ url = LOCATION + url;
+ await runMediaPage(url, options);
+
+ info(`media scalar should be increased to ${expectedScalarCount}`);
+ scalars = TelemetryTestUtils.getProcessScalars(
+ "parent",
+ false,
+ true /* clear */
+ );
+ is(
+ scalars[MEDIA_SCALAR_NAME],
+ expectedScalarCount,
+ "media scalar count is correct"
+ );
+ info("============= Next Testcase =============");
+}
+
+/**
+ * The following are helper functions.
+ */
+async function runMediaPage(url, options = {}) {
+ const tabNums = options.tabNums ? options.tabNums : 1;
+ for (let idx = 0; idx < tabNums; idx++) {
+ info(`open a tab loading media page`);
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ if (options.iframeUrl) {
+ let iframeURL = options.CORSIframe ? CORS_LOCATION : LOCATION;
+ iframeURL += options.iframeUrl;
+ await loadPageForIframe(tab, iframeURL);
+ }
+
+ if (options.createSecondMedia) {
+ info(`create second media in the page`);
+ await createMedia(tab);
+ }
+
+ info(`remove tab`);
+ await BrowserTestUtils.removeTab(tab);
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+ }
+}
+
+function createMedia(tab) {
+ return SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ const video = content.document.createElement("VIDEO");
+ video.src = "gizmo.mp4";
+ video.loop = true;
+ content.document.body.appendChild(video);
+ });
+}
+
+function loadPageForIframe(tab, url) {
+ return SpecialPowers.spawn(tab.linkedBrowser, [url], async url => {
+ const iframe = content.document.getElementById("iframe");
+ iframe.src = url;
+ await new Promise(r => (iframe.onload = r));
+ });
+}
diff --git a/toolkit/components/telemetry/tests/browser/file_iframe.html b/toolkit/components/telemetry/tests/browser/file_iframe.html
new file mode 100644
index 0000000000..271c179eb2
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/file_iframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Media loaded in iframe</title>
+</head>
+<body>
+<iframe id="iframe"></iframe>
+</body>
+</html>
diff --git a/toolkit/components/telemetry/tests/browser/file_media.html b/toolkit/components/telemetry/tests/browser/file_media.html
new file mode 100644
index 0000000000..e2109d18f5
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/file_media.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>media page</title>
+</head>
+<body>
+<video id="video" src="gizmo.mp4" loop></video>
+</body>
+</html>
diff --git a/toolkit/components/telemetry/tests/browser/gizmo.mp4 b/toolkit/components/telemetry/tests/browser/gizmo.mp4
new file mode 100644
index 0000000000..87efad5ade
--- /dev/null
+++ b/toolkit/components/telemetry/tests/browser/gizmo.mp4
Binary files differ