diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js index 357d75e2c0b2..673cfa87316f 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/background.js @@ -52,6 +52,17 @@ port.onMessage.addListener(async message => { sendResponse(id, browser.test.getRequestedLocales()); } break; + + case "AddHistogram": + { + const { + id, + args: { id: histogramId, value }, + } = message; + browser.test.addHistogram(histogramId, value); + sendResponse(id, null); + } + break; } }); diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js index 2258fa51b722..b8c36cd851a3 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-api.js @@ -104,6 +104,10 @@ this.test = class extends ExtensionAPI { async getRequestedLocales() { return Services.locale.requestedLocales; }, + + async addHistogram(id, value) { + return Services.telemetry.getHistogramById(id).add(value); + }, }, }; } diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json index bc41896f8825..fa5fbfe1f109 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-schema.json @@ -72,6 +72,22 @@ "async": true, "description": "Gets the requested locales.", "parameters": [] + }, + { + "name": "addHistogram", + "type": "function", + "async": true, + "description": "Add a sample with the given value to the histogram with the given id.", + "parameters": [ + { + "type": "string", + "name": "id" + }, + { + "type": "any", + "name": "value" + } + ] } ] } diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt index c9e752e993b2..a6169731703e 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt @@ -27,6 +27,7 @@ import org.hamcrest.Matchers.* import org.json.JSONObject import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mozilla.geckoview.test.rule.GeckoSessionTestRule @@ -583,6 +584,7 @@ class NavigationDelegateTest : BaseSessionTest() { innerWidth, closeTo(mobileInnerWidth, 0.1)) } + @Ignore // This test needs to set RuntimeSettings, TODO: Bug 1572245 @Test fun telemetrySnapshots() { sessionRule.session.loadTestPath(HELLO_HTML_PATH) sessionRule.waitForPageStop() diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt new file mode 100644 index 000000000000..62402a6f2622 --- /dev/null +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TelemetryTest.kt @@ -0,0 +1,56 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +package org.mozilla.geckoview.test + +import android.support.test.filters.MediumTest +import android.support.test.runner.AndroidJUnit4 +import android.util.Log +import org.hamcrest.CoreMatchers.equalTo +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.geckoview.GeckoResult +import org.mozilla.geckoview.RuntimeTelemetry +import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled + +@RunWith(AndroidJUnit4::class) +@MediumTest +class TelemetryTest : BaseSessionTest() { + @Test + fun testOnTelemetryReceived() { + sessionRule.addExternalDelegateUntilTestEnd( + RuntimeTelemetry.Delegate::class, + sessionRule::setTelemetryDelegate, + { sessionRule.setTelemetryDelegate(null) }, + object : RuntimeTelemetry.Delegate {} + ) + + // Let's make sure we batch the two telemetry calls + sessionRule.setPrefsUntilTestEnd( + mapOf("toolkit.telemetry.geckoview.batchDurationMS" to 100000)) + + sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", 401) + sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", 12) + sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", 1) + sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", 109) + + // Forces flushing telemetry data at next histogram + sessionRule.setPrefsUntilTestEnd(mapOf("toolkit.telemetry.geckoview.batchDurationMS" to 0)) + + val telemetryReceived = GeckoResult() + sessionRule.delegateDuringNextWait(object : RuntimeTelemetry.Delegate { + @AssertCalled + override fun onTelemetryReceived(metric: RuntimeTelemetry.Metric) { + assertThat("Metric name should be correct", metric.name, + equalTo("TELEMETRY_TEST_STREAMING")) + assertThat("Metric name should be correct", metric.values, + equalTo(longArrayOf(401, 12, 1, 109, 2000))) + telemetryReceived.complete(null) + } + }) + + sessionRule.addHistogram("TELEMETRY_TEST_STREAMING", 2000) + sessionRule.waitForResult(telemetryReceived) + } +} diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java index a05e5b18ced9..10c774640cb8 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java @@ -16,6 +16,7 @@ import org.mozilla.geckoview.GeckoResult; import org.mozilla.geckoview.GeckoRuntime; import org.mozilla.geckoview.GeckoSession; import org.mozilla.geckoview.GeckoSessionSettings; +import org.mozilla.geckoview.RuntimeTelemetry; import org.mozilla.geckoview.SessionTextInput; import org.mozilla.geckoview.WebExtension; import org.mozilla.geckoview.test.util.HttpBin; @@ -551,7 +552,7 @@ public class GeckoSessionTestRule implements TestRule { public void delegate(final @Nullable GeckoSession session, final @NonNull Object callback) { - for (final Class ifce : DEFAULT_DELEGATES) { + for (final Class ifce : mAllDelegates) { if (!ifce.isInstance(callback)) { continue; } @@ -859,6 +860,10 @@ public class GeckoSessionTestRule implements TestRule { return RuntimeCreator.getRuntime(); } + public void setTelemetryDelegate(RuntimeTelemetry.Delegate delegate) { + RuntimeCreator.setTelemetryDelegate(delegate); + } + public @Nullable GeckoDisplay getDisplay() { return mDisplay; } @@ -1221,6 +1226,7 @@ public class GeckoSessionTestRule implements TestRule { mLastWaitStart = 0; mLastWaitEnd = 0; mTimeoutMillis = 0; + RuntimeCreator.setTelemetryDelegate(null); } @Override @@ -2036,6 +2042,24 @@ public class GeckoSessionTestRule implements TestRule { } } + /** + * Adds value to the given histogram. + * + * @param id the histogram id to increment. + * @param value to add to the histogram. + */ + public void addHistogram(final String id, final long value) { + try { + final JSONObject args = new JSONObject(); + args.put("id", id); + args.put("value", value); + + webExtensionApiCall("AddHistogram", args); + } catch (JSONException ex) { + throw new RuntimeException(ex); + } + } + private Object webExtensionApiCall(final String apiName, JSONObject args) throws JSONException { // Ensure background script is connected UiThreadUtils.waitForCondition(() -> RuntimeCreator.backgroundPort() != null, diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java index b89c15b8cf63..30344cf673a7 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/RuntimeCreator.java @@ -3,6 +3,7 @@ package org.mozilla.geckoview.test.util; import org.mozilla.geckoview.GeckoResult; import org.mozilla.geckoview.GeckoRuntime; import org.mozilla.geckoview.GeckoRuntimeSettings; +import org.mozilla.geckoview.RuntimeTelemetry; import org.mozilla.geckoview.WebExtension; import org.mozilla.geckoview.test.TestCrashHandler; @@ -28,6 +29,22 @@ public class RuntimeCreator { "test-support@mozilla.com", WebExtension.Flags.ALLOW_CONTENT_MESSAGING); + // The RuntimeTelemetry.Delegate can only be set when creating the RuntimeCreator, to + // let tests set their own Delegate we need to create a proxy here. + public static class RuntimeTelemetryDelegate implements RuntimeTelemetry.Delegate { + public RuntimeTelemetry.Delegate delegate = null; + + @Override + public void onTelemetryReceived(@NonNull RuntimeTelemetry.Metric metric) { + if (delegate != null) { + delegate.onTelemetryReceived(metric); + } + } + } + + public static final RuntimeTelemetryDelegate sRuntimeTelemetryProxy = + new RuntimeTelemetryDelegate(); + private static WebExtension.Port sBackgroundPort; private static WebExtension.PortDelegate sPortDelegate; @@ -66,6 +83,17 @@ public class RuntimeCreator { }); } + /** + * Set the {@link RuntimeTelemetry.Delegate} instance for this test. Application code can only + * register this delegate when the {@link GeckoRuntime} is created, so we need to proxy it + * for test code. + * + * @param delegate the {@link RuntimeTelemetry.Delegate} for this test run. + */ + public static void setTelemetryDelegate(RuntimeTelemetry.Delegate delegate) { + sRuntimeTelemetryProxy.delegate = delegate; + } + public static void setPortDelegate(WebExtension.PortDelegate portDelegate) { sPortDelegate = portDelegate; } @@ -76,19 +104,20 @@ public class RuntimeCreator { return sRuntime; } - final GeckoRuntimeSettings.Builder runtimeSettingsBuilder = - new GeckoRuntimeSettings.Builder(); - runtimeSettingsBuilder.arguments(new String[]{"-purgecaches"}) + TEST_SUPPORT_WEB_EXTENSION.setMessageDelegate(sMessageDelegate, "browser"); + + final GeckoRuntimeSettings runtimeSettings = new GeckoRuntimeSettings.Builder() + .arguments(new String[]{"-purgecaches"}) .extras(InstrumentationRegistry.getArguments()) .remoteDebuggingEnabled(true) .consoleOutput(true) - .crashHandler(TestCrashHandler.class); - - TEST_SUPPORT_WEB_EXTENSION.setMessageDelegate(sMessageDelegate, "browser"); + .crashHandler(TestCrashHandler.class) + .telemetryDelegate(sRuntimeTelemetryProxy) + .build(); sRuntime = GeckoRuntime.create( InstrumentationRegistry.getTargetContext(), - runtimeSettingsBuilder.build()); + runtimeSettings); registerTestSupport(); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 196a63a745d0..174c6c35a406 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -8925,6 +8925,7 @@ "low": 1, "high": 2147483646, "n_buckets": 10, + "releaseChannelCollection": "opt-out", "bug_numbers": [1566366], "description": "a testing histogram; not meant to be touched" },