Bug 1461965 - Add GeckoView test coverage for content histograms. r?chutten,janerik
This patch uses the GeckoView testing IDL interface and introduces a test
to verify that content histograms are correctly persisted and unpersisted.
MozReview-Commit-ID: 3Ja0hOTvEki
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_GeckoView.js
@@ -0,0 +1,148 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm", this);
+ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm", this);
+
+const Telemetry = Services.telemetry;
+const TelemetryGeckoView = Cc["@mozilla.org/telemetry/geckoview-testing;1"]
+ .createInstance(Ci.nsITelemetryGeckoViewTesting);
+
+/**
+ * Run a file in the content process.
+ * @param aFileName - The file to execute in the content process.
+ * @return {Promise} A promise resolved after the execution in the other process
+ * finishes.
+ */
+async function run_in_child(aFileName) {
+ const PREF_GECKOVIEW_MODE = "toolkit.telemetry.isGeckoViewMode";
+ // We don't ship GeckoViewTelemetryController.jsm outside of Android. If
+ // |toolkit.telemetry.isGeckoViewMode| is true, this makes Gecko crash on
+ // other platforms because ContentProcessSingleton.js requires it. Work
+ // around this by temporarily setting the pref to false.
+ const currentValue = Services.prefs.getBoolPref(PREF_GECKOVIEW_MODE, false);
+ Services.prefs.setBoolPref(PREF_GECKOVIEW_MODE, false);
+ await run_test_in_child(aFileName);
+ Services.prefs.setBoolPref(PREF_GECKOVIEW_MODE, currentValue);
+}
+
+/**
+ * Builds a promise to wait for the GeckoView data loading to finish.
+ * @return {Promise} A promise resolved when the data loading finishes.
+ */
+function waitGeckoViewLoadComplete() {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observe() {
+ Services.obs.removeObserver(observe, "internal-telemetry-geckoview-load-complete");
+ resolve();
+ }, "internal-telemetry-geckoview-load-complete");
+ });
+}
+
+/**
+ * This function waits until the desired histogram is reported into the
+ * snapshot of the relevant process.
+ * @param aHistogramName - The name of the histogram to look for.
+ * @param aProcessName - The name of the process to look in.
+ * @param aKeyed - Whether or not to look in keyed snapshots.
+ */
+async function waitForSnapshotData(aHistogramName, aProcessName, aKeyed) {
+ await ContentTaskUtils.waitForCondition(() => {
+ const data = aKeyed
+ ? Telemetry.snapshotKeyedHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false)
+ : Telemetry.snapshotHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+
+ return (aProcessName in data)
+ && (aHistogramName in data[aProcessName]);
+ });
+}
+
+add_task(async function setup() {
+ // Init the profile.
+ let profileDir = do_get_profile(true);
+
+ // Set geckoview mode.
+ Services.prefs.setBoolPref("toolkit.telemetry.isGeckoViewMode", true);
+
+ // Set the ANDROID_DATA_DIR to the profile dir.
+ let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ env.set("MOZ_ANDROID_DATA_DIR", profileDir.path);
+});
+
+add_task(async function test_persistContentHistograms() {
+ // Get and clear the histograms.
+ let plainHist = Telemetry.getHistogramById("TELEMETRY_TEST_MULTIPRODUCT");
+ plainHist.clear();
+ let keyedHist = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT");
+ keyedHist.clear();
+
+ TelemetryGeckoView.initPersistence();
+
+ // Set the histograms in parent.
+ plainHist.add(37);
+ keyedHist.add("parent-test-key", 73);
+
+ // Set content histograms and wait for the execution in the other
+ // process to finish.
+ await run_in_child("test_GeckoView_content_histograms.js");
+
+ // Wait for the data to be collected by the parent process.
+ await waitForSnapshotData("TELEMETRY_TEST_MULTIPRODUCT", "content", false /* aKeyed */);
+ await waitForSnapshotData("TELEMETRY_TEST_KEYED_COUNT", "content", true /* aKeyed */);
+
+ // Force persisting the measurements to file.
+ TelemetryGeckoView.forcePersist();
+ TelemetryGeckoView.deInitPersistence();
+
+ // Clear the histograms for all processes.
+ Telemetry.snapshotHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true /* clear */);
+ Telemetry.snapshotKeyedHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true /* clear */);
+
+ // Start the persistence system again, to unpersist the data.
+ let loadPromise = waitGeckoViewLoadComplete();
+ TelemetryGeckoView.initPersistence();
+ // Wait for the load to finish.
+ await loadPromise;
+
+ // Validate the snapshot data.
+ const snapshot =
+ Telemetry.snapshotHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false /* clear */);
+ Assert.ok("parent" in snapshot, "The snapshot object must have a 'parent' entry.");
+ Assert.ok("content" in snapshot, "The snapshot object must have a 'content' entry.");
+ Assert.ok("TELEMETRY_TEST_MULTIPRODUCT" in snapshot.parent,
+ "The TELEMETRY_TEST_MULTIPRODUCT histogram must exist in the parent section.");
+ Assert.equal(snapshot.parent.TELEMETRY_TEST_MULTIPRODUCT.sum, 37,
+ "The TELEMETRY_TEST_MULTIPRODUCT must have the expected value in the parent section.");
+ Assert.ok("TELEMETRY_TEST_MULTIPRODUCT" in snapshot.content,
+ "The TELEMETRY_TEST_MULTIPRODUCT histogram must exist in the content section.");
+ Assert.equal(snapshot.content.TELEMETRY_TEST_MULTIPRODUCT.sum, 73,
+ "The TELEMETRY_TEST_MULTIPRODUCT must have the expected value in the content section.");
+
+ const keyedSnapshot =
+ Telemetry.snapshotKeyedHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false /* clear */);
+ Assert.ok("parent" in keyedSnapshot, "The keyed snapshot object must have a 'parent' entry.");
+ Assert.ok("content" in keyedSnapshot, "The keyed snapshot object must have a 'content' entry.");
+ const parentData = keyedSnapshot.parent;
+ Assert.ok("TELEMETRY_TEST_KEYED_COUNT" in parentData,
+ "The TELEMETRY_TEST_KEYED_COUNT histogram must exist in the parent section.");
+ Assert.ok("parent-test-key" in parentData.TELEMETRY_TEST_KEYED_COUNT,
+ "The histogram in the parent process should have the expected key.");
+ Assert.equal(parentData.TELEMETRY_TEST_KEYED_COUNT["parent-test-key"].sum, 73,
+ "The TELEMETRY_TEST_KEYED_COUNT must have the expected value in the parent section.");
+ const contentData = keyedSnapshot.content;
+ Assert.ok("TELEMETRY_TEST_KEYED_COUNT" in contentData,
+ "The TELEMETRY_TEST_KEYED_COUNT histogram must exist in the content section.");
+ Assert.ok("content-test-key" in contentData.TELEMETRY_TEST_KEYED_COUNT,
+ "The histogram in the content process should have the expected key.");
+ Assert.equal(contentData.TELEMETRY_TEST_KEYED_COUNT["content-test-key"].sum, 37,
+ "The TELEMETRY_TEST_KEYED_COUNT must have the expected value in the content section.");
+
+ TelemetryGeckoView.deInitPersistence();
+});
+
+add_task(async function cleanup() {
+ Services.prefs.clearUserPref("toolkit.telemetry.isGeckoViewMode");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_GeckoView_content_histograms.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+
+// Note: this test file is only supposed to be run by
+// test_GeckoView.js. It assumes to be in the content
+// process.
+function run_test() {
+ // Get the histograms and set some values in the content process.
+ Services.telemetry.getHistogramById("TELEMETRY_TEST_MULTIPRODUCT")
+ .add(73);
+ Services.telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT")
+ .add("content-test-key", 37);
+}
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -17,16 +17,21 @@ support-files =
testNoPDB64.dll
!/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
generated-files =
dictionary.xpi
experiment.xpi
system.xpi
restartless.xpi
+[test_GeckoView.js]
+skip-if = os == "android" # Disabled due to crashes (see bug 1331366)
+head =
+support-files =
+ test_GeckoView_content_histograms.js
[test_MigratePendingPings.js]
[test_TelemetryHistograms.js]
[test_TelemetryStorage.js]
[test_SubsessionChaining.js]
tags = addons
[test_TelemetryEnvironment.js]
skip-if = os == "android"
tags = addons