package org.mozilla.fenix.components.metrics import android.content.Context import android.util.Base64 import com.google.android.gms.ads.identifier.AdvertisingIdClient import com.google.android.gms.common.GooglePlayServicesNotAvailableException import com.google.android.gms.common.GooglePlayServicesRepairableException import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject import io.mockk.mockkStatic import io.mockk.slot import io.mockk.unmockkStatic import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Test import java.io.IOException class MetricsUtilsTest { private val context: Context = mockk(relaxed = true) @Test fun `getAdvertisingID() returns null if the API throws`() { mockkStatic("com.google.android.gms.ads.identifier.AdvertisingIdClient") val exceptions = listOf( GooglePlayServicesNotAvailableException(1), GooglePlayServicesRepairableException(0, "", mockk()), IllegalStateException(), IOException() ) exceptions.forEach { every { AdvertisingIdClient.getAdvertisingIdInfo(any()) } throws it assertNull(MetricsUtils.getAdvertisingID(context)) } unmockkStatic("com.google.android.gms.ads.identifier.AdvertisingIdClient") } @Test fun `getAdvertisingID() returns null if the API returns null info`() { mockkStatic(AdvertisingIdClient::class) every { AdvertisingIdClient.getAdvertisingIdInfo(any()) } returns null assertNull(MetricsUtils.getAdvertisingID(context)) } @Test fun `getAdvertisingID() returns a valid string if the API returns a valid ID`() { val testId = "test-value-id" mockkStatic(AdvertisingIdClient::class) every { AdvertisingIdClient.getAdvertisingIdInfo(any()) } returns AdvertisingIdClient.Info(testId, false) assertEquals(testId, MetricsUtils.getAdvertisingID(context)) } @Test fun `getHashedIdentifier() returns a hashed identifier`() { val testId = "test-value-id" val testPackageName = "org.mozilla-test.fenix" val mockedHexReturn = "mocked-HEX" // Mock the Base64 to record the byte array that is passed in, // which is the actual digest. We can't simply test the return value // of |getHashedIdentifier| as these Android tests require us to mock // Android-specific APIs. mockkStatic(Base64::class) val shaDigest = slot() every { Base64.encodeToString(capture(shaDigest), any()) } returns mockedHexReturn // Get the hash identifier. mockkObject(MetricsUtils) every { MetricsUtils.getAdvertisingID(context) } returns testId every { MetricsUtils.getHashingSalt() } returns testPackageName runBlocking { assertEquals(mockedHexReturn, MetricsUtils.getHashedIdentifier(context)) } // Check that the digest of the identifier matches with what we expect. // Please note that in the real world, Base64.encodeToString would encode // this to something much shorter, which we'd send with the ping. val expectedDigestBytes = "[52, -79, -84, 79, 101, 22, -82, -44, -44, -14, 21, 15, 48, 88, -94, -74, -8, 25, -72, -120, -37, 108, 47, 16, 2, -37, 126, 41, 102, -92, 103, 24]" assertEquals(expectedDigestBytes, shaDigest.captured.contentToString()) } companion object { const val ENGINE_SOURCE_IDENTIFIER = "google-2018" } }