PM-10140: Add caching for large string resources to avoid delays and reduce timeout when retrieving ciphers (#3638)

This commit is contained in:
David Perez 2024-07-29 10:46:13 -05:00 committed by GitHub
parent 74132de8ed
commit 39250e5cb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 230 additions and 100 deletions

View file

@ -21,7 +21,7 @@ private const val VAULT_LOCKED_TIMEOUT_MS: Long = 500L
/**
* The duration, in milliseconds, we should wait while retrieving ciphers before proceeding.
*/
private const val GET_CIPHERS_TIMEOUT_MS: Long = 5_000L
private const val GET_CIPHERS_TIMEOUT_MS: Long = 2_000L
/**
* The default [AutofillCipherProvider] implementation. This service is used for getting current

View file

@ -0,0 +1,22 @@
package com.x8bit.bitwarden.data.platform.manager
/**
* A manager for caching resources that are large and could be performance impacting to load
* multiple times.
*/
interface ResourceCacheManager {
/**
* Retrieves the exception suffix list used for matching a cipher against a domain.
*/
val domainExceptionSuffixes: List<String>
/**
* Retrieves the normal suffix list used for matching a cipher against a domain.
*/
val domainNormalSuffixes: List<String>
/**
* Retrieves the wild card suffix list used for matching a cipher against a domain.
*/
val domainWildCardSuffixes: List<String>
}

View file

@ -0,0 +1,32 @@
package com.x8bit.bitwarden.data.platform.manager
import android.content.Context
import com.x8bit.bitwarden.R
/**
* Primary implementation of [ResourceCacheManager].
*/
class ResourceCacheManagerImpl(
private val context: Context,
) : ResourceCacheManager {
override val domainExceptionSuffixes: List<String> by lazy {
context
.resources
.getStringArray(R.array.exception_suffixes)
.toList()
}
override val domainNormalSuffixes: List<String> by lazy {
context
.resources
.getStringArray(R.array.normal_suffixes)
.toList()
}
override val domainWildCardSuffixes: List<String> by lazy {
context
.resources
.getStringArray(R.array.wild_card_suffixes)
.toList()
}
}

View file

@ -1,9 +1,9 @@
package com.x8bit.bitwarden.data.platform.manager.ciphermatching
import android.content.Context
import com.bitwarden.vault.CipherView
import com.bitwarden.vault.LoginUriView
import com.bitwarden.vault.UriMatchType
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.util.firstWithTimeoutOrNull
import com.x8bit.bitwarden.data.platform.util.getDomainOrNull
@ -31,7 +31,7 @@ private const val GET_DOMAINS_TIMEOUT_MS: Long = 1_000L
* ciphers based on special criteria.
*/
class CipherMatchingManagerImpl(
private val context: Context,
private val resourceCacheManager: ResourceCacheManager,
private val settingsRepository: SettingsRepository,
private val vaultRepository: VaultRepository,
) : CipherMatchingManager {
@ -48,7 +48,7 @@ class CipherMatchingManagerImpl(
val isAndroidApp = matchUri.isAndroidApp()
val defaultUriMatchType = settingsRepository.defaultUriMatchType.toSdkUriMatchType()
val domain = matchUri
.getDomainOrNull(context = context)
.getDomainOrNull(resourceCacheManager = resourceCacheManager)
?.lowercase()
// Retrieve domains that are considered equivalent to the specified matchUri for cipher
@ -67,8 +67,8 @@ class CipherMatchingManagerImpl(
ciphers
.forEach { cipherView ->
val matchResult = checkForCipherMatch(
resourceCacheManager = resourceCacheManager,
cipherView = cipherView,
context = context,
defaultUriMatchType = defaultUriMatchType,
isAndroidApp = isAndroidApp,
matchUri = matchUri,
@ -142,7 +142,7 @@ private fun getMatchingDomains(
* provide details on the match quality.
*
* @param cipherView The cipher to be judged for a match.
* @param context A context for getting string resources.
* @param resourceCacheManager The [ResourceCacheManager] for fetching cached resources.
* @param defaultUriMatchType The global default [UriMatchType].
* @param isAndroidApp Whether or not the [matchUri] belongs to an Android app.
* @param matchingDomains The set of domains that match the domain of [matchUri].
@ -150,8 +150,8 @@ private fun getMatchingDomains(
*/
@Suppress("LongParameterList")
private fun checkForCipherMatch(
resourceCacheManager: ResourceCacheManager,
cipherView: CipherView,
context: Context,
defaultUriMatchType: UriMatchType,
isAndroidApp: Boolean,
matchingDomains: MatchingDomains,
@ -162,7 +162,7 @@ private fun checkForCipherMatch(
?.uris
?.map { loginUriView ->
loginUriView.checkForMatch(
context = context,
resourceCacheManager = resourceCacheManager,
defaultUriMatchType = defaultUriMatchType,
isAndroidApp = isAndroidApp,
matchingDomains = matchingDomains,
@ -180,14 +180,14 @@ private fun checkForCipherMatch(
/**
* Check to see how well this [LoginUriView] matches [matchUri].
*
* @param context A context for getting app information.
* @param resourceCacheManager The [ResourceCacheManager] for fetching cached resources.
* @param defaultUriMatchType The global default [UriMatchType].
* @param isAndroidApp Whether or not the [matchUri] belongs to an Android app.
* @param matchingDomains The set of domains that match the domain of [matchUri].
* @param matchUri The uri that this [LoginUriView] is being matched to.
*/
private fun LoginUriView.checkForMatch(
context: Context,
resourceCacheManager: ResourceCacheManager,
defaultUriMatchType: UriMatchType,
isAndroidApp: Boolean,
matchingDomains: MatchingDomains,
@ -200,7 +200,7 @@ private fun LoginUriView.checkForMatch(
when (matchType) {
UriMatchType.DOMAIN -> {
checkUriForDomainMatch(
context = context,
resourceCacheManager = resourceCacheManager,
isAndroidApp = isAndroidApp,
matchingDomains = matchingDomains,
uri = loginViewUri,
@ -234,8 +234,8 @@ private fun LoginUriView.checkForMatch(
* Check to see if [uri] matches [matchingDomains] in some way.
*/
private fun checkUriForDomainMatch(
resourceCacheManager: ResourceCacheManager,
isAndroidApp: Boolean,
context: Context,
matchingDomains: MatchingDomains,
uri: String,
): MatchResult = when {
@ -243,7 +243,7 @@ private fun checkUriForDomainMatch(
isAndroidApp && matchingDomains.fuzzyMatches.contains(uri) -> MatchResult.FUZZY
else -> {
val domain = uri
.getDomainOrNull(context = context)
.getDomainOrNull(resourceCacheManager = resourceCacheManager)
?.lowercase()
// We only care about fuzzy matches if we are isAndroidApp is true because the fuzzu

View file

@ -30,6 +30,8 @@ import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManagerImpl
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.PushManagerImpl
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManagerImpl
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
import com.x8bit.bitwarden.data.platform.manager.SdkClientManagerImpl
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
@ -90,12 +92,12 @@ object PlatformManagerModule {
@Provides
@Singleton
fun providesCipherMatchingManager(
@ApplicationContext context: Context,
resourceCacheManager: ResourceCacheManager,
settingsRepository: SettingsRepository,
vaultRepository: VaultRepository,
): CipherMatchingManager =
CipherMatchingManagerImpl(
context = context,
resourceCacheManager = resourceCacheManager,
settingsRepository = settingsRepository,
vaultRepository = vaultRepository,
)
@ -229,4 +231,10 @@ object PlatformManagerModule {
environmentRepository = environmentRepository,
restrictionsManager = requireNotNull(context.getSystemService()),
)
@Provides
@Singleton
fun provideResourceCacheManager(
@ApplicationContext context: Context,
): ResourceCacheManager = ResourceCacheManagerImpl(context = context)
}

View file

@ -1,7 +1,7 @@
package com.x8bit.bitwarden.data.platform.util
import android.content.Context
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import java.net.URI
import java.net.URISyntaxException
@ -47,10 +47,10 @@ fun String.getWebHostFromAndroidUriOrNull(): String? =
/**
* Extract the domain name from this [String] if possible, otherwise return null.
*/
fun String.getDomainOrNull(context: Context): String? =
fun String.getDomainOrNull(resourceCacheManager: ResourceCacheManager): String? =
this
.toUriOrNull()
?.parseDomainOrNull(context = context)
?.parseDomainOrNull(resourceCacheManager = resourceCacheManager)
/**
* Extract the host with optional port from this [String] if possible, otherwise return null.

View file

@ -1,7 +1,6 @@
package com.x8bit.bitwarden.data.platform.util
import android.content.Context
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.manager.model.DomainName
import java.net.URI
@ -17,7 +16,7 @@ private const val IP_REGEX: String =
/**
* Parses the base domain from the URL. Returns null if unavailable.
*/
fun URI.parseDomainOrNull(context: Context): String? {
fun URI.parseDomainOrNull(resourceCacheManager: ResourceCacheManager): String? {
val host = this.host ?: return null
val isIpAddress = host.matches(IP_REGEX.toRegex())
@ -25,7 +24,7 @@ fun URI.parseDomainOrNull(context: Context): String? {
host
} else {
parseDomainNameOrNullInternal(
context = context,
resourceCacheManager = resourceCacheManager,
host = host,
)
?.domain
@ -35,13 +34,13 @@ fun URI.parseDomainOrNull(context: Context): String? {
/**
* Parses a URL to get the breakdown of a URL's domain. Returns null if invalid.
*/
fun URI.parseDomainNameOrNull(context: Context): DomainName? =
fun URI.parseDomainNameOrNull(resourceCacheManager: ResourceCacheManager): DomainName? =
this
// URI is a platform type and host can be null.
.host
?.let { nonNullHost ->
parseDomainNameOrNullInternal(
context = context,
resourceCacheManager = resourceCacheManager,
host = nonNullHost,
)
}
@ -53,21 +52,12 @@ fun URI.parseDomainNameOrNull(context: Context): DomainName? =
*/
@Suppress("LongMethod")
private fun parseDomainNameOrNullInternal(
context: Context,
resourceCacheManager: ResourceCacheManager,
host: String,
): DomainName? {
val exceptionSuffixes = context
.resources
.getStringArray(R.array.exception_suffixes)
.toList()
val normalSuffixes = context
.resources
.getStringArray(R.array.normal_suffixes)
.toList()
val wildCardSuffixes = context
.resources
.getStringArray(R.array.wild_card_suffixes)
.toList()
val exceptionSuffixes = resourceCacheManager.domainExceptionSuffixes
val normalSuffixes = resourceCacheManager.domainNormalSuffixes
val wildCardSuffixes = resourceCacheManager.domainWildCardSuffixes
// Split the host into parts separated by a period. Start with the last part and incrementally
// add back the earlier parts to build a list of any matching domains in the data set.

View file

@ -196,7 +196,7 @@ class AutofillCipherProviderTest {
testScheduler.runCurrent()
assertFalse(actual.isCompleted)
testScheduler.advanceTimeBy(delayTimeMillis = 5_000L)
testScheduler.advanceTimeBy(delayTimeMillis = 2_000L)
testScheduler.runCurrent()
// Verify
@ -268,7 +268,7 @@ class AutofillCipherProviderTest {
testScheduler.runCurrent()
assertFalse(actual.isCompleted)
testScheduler.advanceTimeBy(delayTimeMillis = 5_000L)
testScheduler.advanceTimeBy(delayTimeMillis = 2_000L)
testScheduler.runCurrent()
// Verify

View file

@ -0,0 +1,92 @@
package com.x8bit.bitwarden.data.platform.manager
import android.content.Context
import android.content.res.Resources
import com.x8bit.bitwarden.R
import io.mockk.clearMocks
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class ResourceCacheManagerTest {
private val resources: Resources = mockk {
every { getStringArray(R.array.exception_suffixes) } returns arrayOf("uk")
every { getStringArray(R.array.normal_suffixes) } returns arrayOf("co.uk")
every { getStringArray(R.array.wild_card_suffixes) } returns arrayOf("amazonaws.com")
}
private val context: Context = mockk {
every { this@mockk.resources } returns this@ResourceCacheManagerTest.resources
}
private val resourceCacheManager: ResourceCacheManager = ResourceCacheManagerImpl(
context = context,
)
@Test
fun `domainExceptionSuffixes should return cached value the second time`() {
val expected = listOf("uk")
val firstResult = resourceCacheManager.domainExceptionSuffixes
assertEquals(expected, firstResult)
verify(exactly = 1) {
context.resources
resources.getStringArray(R.array.exception_suffixes)
}
clearMocks(context, resources)
val secondResult = resourceCacheManager.domainExceptionSuffixes
assertEquals(expected, secondResult)
verify(exactly = 0) {
context.resources
resources.getStringArray(R.array.exception_suffixes)
}
}
@Test
fun `domainNormalSuffixes should return cached value the second time`() {
val expected = listOf("co.uk")
val firstResult = resourceCacheManager.domainNormalSuffixes
assertEquals(expected, firstResult)
verify(exactly = 1) {
context.resources
resources.getStringArray(R.array.normal_suffixes)
}
clearMocks(context, resources)
val secondResult = resourceCacheManager.domainNormalSuffixes
assertEquals(expected, secondResult)
verify(exactly = 0) {
context.resources
resources.getStringArray(R.array.normal_suffixes)
}
}
@Test
fun `domainWildCardSuffixes should return cached value the second time`() {
val expected = listOf("amazonaws.com")
val firstResult = resourceCacheManager.domainWildCardSuffixes
assertEquals(expected, firstResult)
verify(exactly = 1) {
context.resources
resources.getStringArray(R.array.wild_card_suffixes)
}
clearMocks(context, resources)
val secondResult = resourceCacheManager.domainWildCardSuffixes
assertEquals(expected, secondResult)
verify(exactly = 0) {
context.resources
resources.getStringArray(R.array.wild_card_suffixes)
}
}
}

View file

@ -1,10 +1,10 @@
package com.x8bit.bitwarden.data.platform.manager.ciphermatching
import android.content.Context
import com.bitwarden.vault.CipherView
import com.bitwarden.vault.LoginUriView
import com.bitwarden.vault.LoginView
import com.bitwarden.vault.UriMatchType
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.util.getDomainOrNull
@ -33,7 +33,7 @@ class CipherMatchingManagerTest {
private lateinit var cipherMatchingManager: CipherMatchingManager
// Setup dependencies
private val context: Context = mockk()
private val resourceCacheManager: ResourceCacheManager = mockk()
private val settingsRepository: SettingsRepository = mockk {
every { defaultUriMatchType } returns DEFAULT_URI_MATCH_TYPE
}
@ -172,7 +172,7 @@ class CipherMatchingManagerTest {
String::getWebHostFromAndroidUriOrNull,
)
cipherMatchingManager = CipherMatchingManagerImpl(
context = context,
resourceCacheManager = resourceCacheManager,
settingsRepository = settingsRepository,
vaultRepository = vaultRepository,
)
@ -333,7 +333,7 @@ class CipherMatchingManagerTest {
)
with(uri) {
every { isAndroidApp() } returns false
every { getDomainOrNull(context = context) } returns this
every { getDomainOrNull(resourceCacheManager = resourceCacheManager) } returns this
every { getWebHostFromAndroidUriOrNull() } returns null
}
@ -356,26 +356,30 @@ class CipherMatchingManagerTest {
) {
with(uri) {
every { isAndroidApp() } returns isAndroidApp
every { getDomainOrNull(context = context) } returns this.takeIf { isAndroidApp }
every {
getDomainOrNull(resourceCacheManager = resourceCacheManager)
} returns this.takeIf { isAndroidApp }
every { getHostWithPortOrNull() } returns HOST_WITH_PORT
every {
getWebHostFromAndroidUriOrNull()
} returns ANDROID_APP_WEB_URL.takeIf { isAndroidApp }
}
every {
DEFAULT_LOGIN_VIEW_URI_ONE.getDomainOrNull(context = context)
DEFAULT_LOGIN_VIEW_URI_ONE.getDomainOrNull(resourceCacheManager = resourceCacheManager)
} returns DEFAULT_LOGIN_VIEW_URI_ONE
every {
DEFAULT_LOGIN_VIEW_URI_TWO.getDomainOrNull(context = context)
DEFAULT_LOGIN_VIEW_URI_TWO.getDomainOrNull(resourceCacheManager = resourceCacheManager)
} returns null
every {
DEFAULT_LOGIN_VIEW_URI_THREE.getDomainOrNull(context = context)
DEFAULT_LOGIN_VIEW_URI_THREE.getDomainOrNull(
resourceCacheManager = resourceCacheManager,
)
} returns uri
every {
DEFAULT_LOGIN_VIEW_URI_FOUR.getDomainOrNull(context = context)
DEFAULT_LOGIN_VIEW_URI_FOUR.getDomainOrNull(resourceCacheManager = resourceCacheManager)
} returns "bitwarden.com"
every {
DEFAULT_LOGIN_VIEW_URI_FIVE.getDomainOrNull(context = context)
DEFAULT_LOGIN_VIEW_URI_FIVE.getDomainOrNull(resourceCacheManager = resourceCacheManager)
} returns null
every { HOST_LOGIN_VIEW_URI_MATCHING.getHostWithPortOrNull() } returns HOST_WITH_PORT

View file

@ -1,6 +1,6 @@
package com.x8bit.bitwarden.data.util
import android.content.Context
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.util.findLastSubstringIndicesOrNull
import com.x8bit.bitwarden.data.platform.util.getDomainOrNull
import com.x8bit.bitwarden.data.platform.util.getWebHostFromAndroidUriOrNull
@ -74,21 +74,21 @@ class StringExtensionsTest {
fun `getDomainOrNull should invoke parseDomainOrNull when URI is created`() {
// Setup
mockkStatic(URI::parseDomainOrNull)
val context: Context = mockk()
val resourceCacheManager: ResourceCacheManager = mockk()
val expected = "google.com"
every {
any<URI>().parseDomainOrNull(context = context)
any<URI>().parseDomainOrNull(resourceCacheManager = resourceCacheManager)
} returns expected
// Test
val actual = "www.google.com".getDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
assertEquals(expected, actual)
verify(exactly = 1) {
any<URI>().parseDomainOrNull(context = context)
any<URI>().parseDomainOrNull(resourceCacheManager = resourceCacheManager)
}
}
@ -96,17 +96,17 @@ class StringExtensionsTest {
fun `getDomainOrNull should not invoke parseDomainOrNull when URI is not created`() {
// Setup
mockkStatic(URI::parseDomainOrNull)
val context: Context = mockk()
val resourceCacheManager: ResourceCacheManager = mockk()
// Test
val actual = "not a URI".getDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
assertNull(actual)
verify(exactly = 0) {
any<URI>().parseDomainOrNull(context = context)
any<URI>().parseDomainOrNull(resourceCacheManager = resourceCacheManager)
}
}

View file

@ -1,8 +1,6 @@
package com.x8bit.bitwarden.data.util
import android.content.Context
import android.content.res.Resources
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.manager.model.DomainName
import com.x8bit.bitwarden.data.platform.util.parseDomainNameOrNull
import com.x8bit.bitwarden.data.platform.util.parseDomainOrNull
@ -14,13 +12,10 @@ import org.junit.jupiter.api.Test
import java.net.URI
class UriExtensionsTest {
private val resources: Resources = mockk {
every { getStringArray(R.array.exception_suffixes) } returns emptyArray()
every { getStringArray(R.array.normal_suffixes) } returns emptyArray()
every { getStringArray(R.array.wild_card_suffixes) } returns emptyArray()
}
private val context: Context = mockk {
every { this@mockk.resources } returns this@UriExtensionsTest.resources
private val resourceCacheManager: ResourceCacheManager = mockk {
every { domainExceptionSuffixes } returns emptyList()
every { domainNormalSuffixes } returns emptyList()
every { domainWildCardSuffixes } returns emptyList()
}
@Test
@ -32,7 +27,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -49,7 +44,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -66,7 +61,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -83,9 +78,7 @@ class UriExtensionsTest {
"example.uk",
null,
)
every {
resources.getStringArray(R.array.exception_suffixes)
} returns arrayOf("co.uk", "uk")
every { resourceCacheManager.domainExceptionSuffixes } returns listOf("co.uk", "uk")
// Test & Verify
listOf(
@ -100,7 +93,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -118,10 +111,7 @@ class UriExtensionsTest {
"example.uk",
null,
)
every { resources.getStringArray(R.array.exception_suffixes) } returns arrayOf(
"co.uk",
"uk",
)
every { resourceCacheManager.domainExceptionSuffixes } returns listOf("co.uk", "uk")
// Test & Verify
listOf(
@ -136,7 +126,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -154,10 +144,9 @@ class UriExtensionsTest {
"example.foo.amazonaws.com",
null,
)
every { resources.getStringArray(R.array.wild_card_suffixes) } returns arrayOf(
"compute.amazonaws.com",
"amazonaws.com",
)
every {
resourceCacheManager.domainWildCardSuffixes
} returns listOf("compute.amazonaws.com", "amazonaws.com")
// Test & Verify
listOf(
@ -172,7 +161,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -189,7 +178,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainNameOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -218,10 +207,7 @@ class UriExtensionsTest {
),
null,
)
every { resources.getStringArray(R.array.exception_suffixes) } returns arrayOf(
"co.uk",
"uk",
)
every { resourceCacheManager.domainExceptionSuffixes } returns listOf("co.uk", "uk")
// Test & Verify
listOf(
@ -236,7 +222,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainNameOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -266,10 +252,7 @@ class UriExtensionsTest {
),
null,
)
every { resources.getStringArray(R.array.exception_suffixes) } returns arrayOf(
"co.uk",
"uk",
)
every { resourceCacheManager.domainExceptionSuffixes } returns listOf("co.uk", "uk")
// Test & Verify
listOf(
@ -284,7 +267,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainNameOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify
@ -314,10 +297,9 @@ class UriExtensionsTest {
),
null,
)
every { resources.getStringArray(R.array.wild_card_suffixes) } returns arrayOf(
"compute.amazonaws.com",
"amazonaws.com",
)
every {
resourceCacheManager.domainWildCardSuffixes
} returns listOf("compute.amazonaws.com", "amazonaws.com")
// Test & Verify
listOf(
@ -332,7 +314,7 @@ class UriExtensionsTest {
// Test
val actual = uri.parseDomainNameOrNull(
context = context,
resourceCacheManager = resourceCacheManager,
)
// Verify