diff --git a/app/src/main/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetry.kt b/app/src/main/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetry.kt index f5025f92e..d7c0b53b1 100644 --- a/app/src/main/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetry.kt +++ b/app/src/main/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetry.kt @@ -91,7 +91,7 @@ abstract class BaseSearchTelemetry { return null } - protected fun installWebExtension( + internal fun installWebExtension( engine: Engine, store: BrowserStore, extensionInfo: ExtensionInfo @@ -138,8 +138,10 @@ abstract class BaseSearchTelemetry { /** * This method is used to process any valid json message coming from a web-extension */ - protected abstract fun processMessage(message: JSONObject) + @VisibleForTesting + internal abstract fun processMessage(message: JSONObject) + @VisibleForTesting internal inner class SearchTelemetryMessageHandler : MessageHandler { override fun onMessage(message: Any, source: EngineSession?): Any? { diff --git a/app/src/main/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetry.kt b/app/src/main/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetry.kt index 848046c37..645efc10e 100644 --- a/app/src/main/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetry.kt +++ b/app/src/main/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetry.kt @@ -59,6 +59,7 @@ class AdsTelemetry(private val metrics: MetricController) : BaseSearchTelemetry( internal const val ADS_MESSAGE_SESSION_URL_KEY = "url" @VisibleForTesting internal const val ADS_MESSAGE_DOCUMENT_URLS_KEY = "urls" - private const val ADS_MESSAGE_ID = "MozacBrowserAds" + @VisibleForTesting + internal const val ADS_MESSAGE_ID = "MozacBrowserAds" } } diff --git a/app/src/main/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetry.kt b/app/src/main/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetry.kt index 5571ca8ef..d98423970 100644 --- a/app/src/main/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetry.kt +++ b/app/src/main/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetry.kt @@ -30,7 +30,7 @@ class InContentTelemetry(private val metrics: MetricController) : BaseSearchTele override fun processMessage(message: JSONObject) { val cookies = getMessageList( message, - COOKIES_MESSAGE_DOCUMENT_URLS_KEY + COOKIES_MESSAGE_LIST_KEY ) trackPartnerUrlTypeMetric(message.getString(COOKIES_MESSAGE_SESSION_URL_KEY), cookies) } @@ -138,8 +138,9 @@ class InContentTelemetry(private val metrics: MetricController) : BaseSearchTele @VisibleForTesting internal const val COOKIES_MESSAGE_SESSION_URL_KEY = "url" @VisibleForTesting - internal const val COOKIES_MESSAGE_DOCUMENT_URLS_KEY = "cookies" - private const val COOKIES_MESSAGE_ID = "BrowserCookiesMessage" + internal const val COOKIES_MESSAGE_LIST_KEY = "cookies" + @VisibleForTesting + internal const val COOKIES_MESSAGE_ID = "BrowserCookiesMessage" private const val SEARCH_TYPE_ORGANIC = "organic" private const val SEARCH_TYPE_SAP = "sap" diff --git a/app/src/test/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetryTest.kt b/app/src/test/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetryTest.kt new file mode 100644 index 000000000..244e9d1cf --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/search/telemetry/BaseSearchTelemetryTest.kt @@ -0,0 +1,76 @@ +package org.mozilla.fenix.search.telemetry + +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.Engine +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Test + +class BaseSearchTelemetryTest { + + private lateinit var baseTelemetry: BaseSearchTelemetry + private lateinit var handler: BaseSearchTelemetry.SearchTelemetryMessageHandler + + @org.junit.Before + fun setUp() { + baseTelemetry = spyk(object : BaseSearchTelemetry() { + + override fun install(engine: Engine, store: BrowserStore) { + // mock, do nothing + } + + override fun processMessage(message: JSONObject) { + // mock, do nothing + } + }) + handler = baseTelemetry.SearchTelemetryMessageHandler() + } + + @Test + fun install() { + val engine = mockk(relaxed = true) + val store = mockk(relaxed = true) + val id = "id" + val resourceUrl = "resourceUrl" + val messageId = "messageId" + val extensionInfo = ExtensionInfo(id, resourceUrl, messageId) + + baseTelemetry.installWebExtension(engine, store, extensionInfo) + + verify { + engine.installWebExtension( + id = id, + url = resourceUrl, + allowContentMessaging = true, + onSuccess = any(), + onError = any() + ) + } + } + + @Test + fun `get provider for google url`() { + val url = "https://www.google.com/search?q=computers" + + assertEquals("google", baseTelemetry.getProviderForUrl(url)?.name) + } + + @Test + fun `message handler finds a valid json object`() { + val message = JSONObject() + + handler.onMessage(message, mockk()) + + verify { baseTelemetry.processMessage(message) } + } + + @Test(expected = IllegalStateException::class) + fun `message handler finds no json object`() { + val message = "message" + + handler.onMessage(message, mockk()) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetryTest.kt b/app/src/test/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetryTest.kt index def2b5ce9..ff6e290b4 100644 --- a/app/src/test/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetryTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/telemetry/ads/AdsTelemetryTest.kt @@ -19,9 +19,11 @@ import org.junit.runner.RunWith import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.search.telemetry.ExtensionInfo import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry.Companion.ADS_EXTENSION_ID import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry.Companion.ADS_EXTENSION_RESOURCE_URL import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry.Companion.ADS_MESSAGE_DOCUMENT_URLS_KEY +import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry.Companion.ADS_MESSAGE_ID import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry.Companion.ADS_MESSAGE_SESSION_URL_KEY @RunWith(FenixRobolectricTestRunner::class) @@ -29,12 +31,10 @@ class AdsTelemetryTest { private val metrics: MetricController = mockk(relaxed = true) private lateinit var ads: AdsTelemetry - private lateinit var adsMessageHandler: AdsTelemetry.AdsTelemetryContentMessageHandler @Before fun setUp() { ads = spyk(AdsTelemetry(metrics)) - adsMessageHandler = ads.AdsTelemetryContentMessageHandler() } @Test @@ -71,22 +71,18 @@ class AdsTelemetryTest { fun install() { val engine = mockk(relaxed = true) val store = mockk(relaxed = true) + val extensionInfo = slot() ads.install(engine, store) - verify { - engine.installWebExtension( - id = ADS_EXTENSION_ID, - url = ADS_EXTENSION_RESOURCE_URL, - allowContentMessaging = true, - onSuccess = any(), - onError = any() - ) - } + verify { ads.installWebExtension(engine, store, capture(extensionInfo)) } + assertEquals(ADS_EXTENSION_ID, extensionInfo.captured.id) + assertEquals(ADS_EXTENSION_RESOURCE_URL, extensionInfo.captured.resourceUrl) + assertEquals(ADS_MESSAGE_ID, extensionInfo.captured.messageId) } @Test - fun `message handler processes the document urls and reports an ad`() { + fun `process the document urls and reports an ad`() { val metricEvent = slot() val first = "https://www.google.com/aclk" val second = "https://www.google.com/aaa" @@ -97,14 +93,14 @@ class AdsTelemetryTest { message.put(ADS_MESSAGE_DOCUMENT_URLS_KEY, array) message.put(ADS_MESSAGE_SESSION_URL_KEY, "https://www.google.com/search?q=aaa") - assertEquals("", adsMessageHandler.onMessage(message, mockk())) + ads.processMessage(message) verify { metrics.track(capture(metricEvent)) } assertEquals(ads.providerList[0].name, metricEvent.captured.label) } @Test - fun `message handler processes the document urls and doesn't find ads`() { + fun `process the document urls and don't find ads`() { val first = "https://www.google.com/aaaaaa" val second = "https://www.google.com/aaa" val array = JSONArray() @@ -114,15 +110,8 @@ class AdsTelemetryTest { message.put(ADS_MESSAGE_DOCUMENT_URLS_KEY, array) message.put(ADS_MESSAGE_SESSION_URL_KEY, "https://www.google.com/search?q=aaa") - assertEquals("", adsMessageHandler.onMessage(message, mockk())) + ads.processMessage(message) verify(exactly = 0) { metrics.track(any()) } } - - @Test(expected = IllegalStateException::class) - fun `message handler finds no json object`() { - val message = "message" - - adsMessageHandler.onMessage(message, mockk()) - } } diff --git a/app/src/test/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetryTest.kt b/app/src/test/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetryTest.kt new file mode 100644 index 000000000..4714fe086 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/search/telemetry/incontent/InContentTelemetryTest.kt @@ -0,0 +1,175 @@ +package org.mozilla.fenix.search.telemetry.incontent + +import io.mockk.mockk +import io.mockk.slot +import io.mockk.spyk +import io.mockk.verify +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.Engine +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.search.telemetry.ExtensionInfo +import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry.Companion.COOKIES_EXTENSION_ID +import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry.Companion.COOKIES_EXTENSION_RESOURCE_URL +import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry.Companion.COOKIES_MESSAGE_ID +import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry.Companion.COOKIES_MESSAGE_LIST_KEY +import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry.Companion.COOKIES_MESSAGE_SESSION_URL_KEY + +@RunWith(FenixRobolectricTestRunner::class) +class InContentTelemetryTest { + + private val metrics: MetricController = mockk(relaxed = true) + private lateinit var telemetry: InContentTelemetry + + @Before + fun setUp() { + telemetry = spyk(InContentTelemetry(metrics)) + } + + @Test + fun install() { + val engine = mockk(relaxed = true) + val store = mockk(relaxed = true) + val extensionInfo = slot() + + telemetry.install(engine, store) + + verify { telemetry.installWebExtension(engine, store, capture(extensionInfo)) } + Assert.assertEquals(COOKIES_EXTENSION_ID, extensionInfo.captured.id) + Assert.assertEquals(COOKIES_EXTENSION_RESOURCE_URL, extensionInfo.captured.resourceUrl) + Assert.assertEquals(COOKIES_MESSAGE_ID, extensionInfo.captured.messageId) + } + + @Test + fun processMessage() { + val first = JSONObject() + val second = JSONObject() + val array = JSONArray() + array.put(first) + array.put(second) + val message = JSONObject() + val url = "https://www.google.com/search?q=aaa" + message.put(COOKIES_MESSAGE_LIST_KEY, array) + message.put(COOKIES_MESSAGE_SESSION_URL_KEY, url) + + telemetry.processMessage(message) + + verify { telemetry.trackPartnerUrlTypeMetric(url, any()) } + } + + @Test + fun `track google sap metric`() { + val url = "https://www.google.com/search?q=aaa&client=firefox-b-m" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("google.in-content.sap.firefox-b-m")) } + } + + @Test + fun `track duckduckgo sap metric`() { + val url = "https://duckduckgo.com/?q=aaa&t=fpas" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("duckduckgo.in-content.sap.fpas")) } + } + + @Test + fun `track baidu sap metric`() { + val url = "https://www.baidu.com/from=844b/s?wd=aaa&tn=34046034_firefox" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("baidu.in-content.sap.34046034_firefox")) } + } + + @Test + fun `track bing sap metric`() { + val url = "https://www.bing.com/search?q=aaa&pc=MOZMBA" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("bing.in-content.sap.mozmba")) } + } + + @Test + fun `track google sap-follow-on metric`() { + val url = "https://www.google.com/search?q=aaa&client=firefox-b-m&oq=random" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("google.in-content.sap-follow-on.firefox-b-m")) } + } + + @Test + fun `track baidu sap-follow-on metric`() { + val url = "https://www.baidu.com/from=844b/s?wd=aaa&tn=34046034_firefox&oq=random" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("baidu.in-content.sap-follow-on.34046034_firefox")) } + } + + @Test + fun `track bing sap-follow-on metric by cookies`() { + val url = "https://www.bing.com/search?q=aaa&pc=MOZMBA&form=QBRERANDOM" + + telemetry.trackPartnerUrlTypeMetric(url, createCookieList()) + + verify { metrics.track(Event.SearchInContent("bing.in-content.sap-follow-on.mozmba")) } + } + + @Test + fun `track google organic metric`() { + val url = "https://www.google.com/search?q=aaa" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("google.in-content.organic.none")) } + } + + @Test + fun `track duckduckgo organic metric`() { + val url = "https://duckduckgo.com/?q=aaa" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("duckduckgo.in-content.organic.none")) } + } + + @Test + fun `track bing organic metric`() { + val url = "https://www.bing.com/search?q=aaa" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("bing.in-content.organic.none")) } + } + + @Test + fun `track baidu organic metric`() { + val url = "https://www.baidu.com/from=844b/s?wd=aaa" + + telemetry.trackPartnerUrlTypeMetric(url, listOf()) + + verify { metrics.track(Event.SearchInContent("baidu.in-content.organic.none")) } + } + + private fun createCookieList(): List { + val first = JSONObject() + first.put("name", "SRCHS") + first.put("value", "PC=MOZMBA") + val second = JSONObject() + second.put("name", "RANDOM") + second.put("value", "RANDOM") + return listOf(first, second) + } +}