diff --git a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt index 4dca5c358..de94bdaf3 100644 --- a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.components.searchengine import android.content.Context +import androidx.annotation.VisibleForTesting import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -21,16 +22,15 @@ import org.mozilla.fenix.ext.settings import java.util.Locale @SuppressWarnings("TooManyFunctions") -class FenixSearchEngineProvider( +open class FenixSearchEngineProvider( private val context: Context ) : SearchEngineProvider, CoroutineScope by CoroutineScope(Job() + Dispatchers.IO) { - private val baseSearchEngines = async { + open val baseSearchEngines = async { AssetsSearchEngineProvider(LocaleSearchLocalizationProvider()).loadSearchEngines(context) } - private val bundledSearchEngines = async { + open val bundledSearchEngines = async { val defaultEngineIdentifiers = baseSearchEngines.await().list.map { it.identifier }.toSet() - AssetsSearchEngineProvider( LocaleSearchLocalizationProvider(), filters = listOf(object : SearchEngineFilter { @@ -43,7 +43,7 @@ class FenixSearchEngineProvider( ).loadSearchEngines(context) } - private var customSearchEngines = async { + open var customSearchEngines = async { CustomSearchEngineProvider().loadSearchEngines(context) } @@ -56,6 +56,10 @@ class FenixSearchEngineProvider( return engines.list.find { it.name == selectedName } ?: engines.default ?: engines.list.first() } + /** + * @return a list of all SearchEngines that are currently active. These are the engines that + * are readily available throughout the app. + */ fun installedSearchEngines(context: Context): SearchEngineList = runBlocking { val engineList = loadedSearchEngines.await() val installedIdentifiers = installedSearchEngineIdentifiers(context) @@ -141,16 +145,17 @@ class FenixSearchEngineProvider( prefs.edit().putStringSet(INSTALLED_ENGINES_KEY, defaultSet).apply() defaultSet } else { - prefs(context).getStringSet(INSTALLED_ENGINES_KEY, setOf()) ?: setOf() + prefs.getStringSet(INSTALLED_ENGINES_KEY, setOf()) ?: setOf() } val customEngineIdentifiers = customSearchEngines.await().list.map { it.identifier }.toSet() return identifiers + customEngineIdentifiers } + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) companion object { private val BUNDLED_SEARCH_ENGINES = listOf("ecosia", "reddit", "startpage", "yahoo", "youtube") - private const val PREF_FILE = "fenix-search-engine-provider" + const val PREF_FILE = "fenix-search-engine-provider" private const val INSTALLED_ENGINES_KEY = "fenix-installed-search-engines" } } diff --git a/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt b/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt new file mode 100644 index 000000000..5d6c75dc8 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt @@ -0,0 +1,109 @@ +package org.mozilla.fenix.components.searchengine + +import android.content.Context +import android.graphics.Bitmap +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import mozilla.components.browser.search.SearchEngine +import mozilla.components.browser.search.provider.SearchEngineList +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock +import java.util.UUID + +@ExperimentalCoroutinesApi +class FenixSearchEngineProviderTest { + + private val testContext = mockk() + + private lateinit var fenixSearchEngineProvider: FenixSearchEngineProvider + + @Before + fun before() { + fenixSearchEngineProvider = FakeFenixSearchEngineProvider(testContext) + + every { + testContext.getSharedPreferences(FenixSearchEngineProvider.PREF_FILE, Context.MODE_PRIVATE) + } returns mockk(relaxed = true) + } + + /* + TODO TEST: + - public API happy path + - list ordering + - deduping + - the above after adding/removing + */ + + @Test + fun `temp test class inits`() = runBlockingTest { + val t = fenixSearchEngineProvider.loadSearchEngines(testContext) + + println(t) + } + + + +} + +class FakeFenixSearchEngineProvider(context: Context) : FenixSearchEngineProvider(context) { + override val defaultEngines: Deferred + get() { + val google = mockSearchEngine(id = "google-b-1-m", n = "Google") + + return CompletableDeferred( + SearchEngineList( + listOf( + google, + mockSearchEngine("bing", "Bing"), + mockSearchEngine("amazondotcom", "Amazon.com") + ), default = google + ) + ) + } + + override val bundledEngines = CompletableDeferred( + SearchEngineList( + listOf( + mockSearchEngine("ecosia", "Ecosia"), + mockSearchEngine("reddit", "Reddit"), + mockSearchEngine("startpage", "Startpage.com") + ), default = null + ) + ) + + override var customEngines: Deferred + get() { + return CompletableDeferred( + SearchEngineList( + listOf( + mockSearchEngine("my custom site", "my custom site") + ), default = null + ) + ) + } + set(_) = throw RuntimeException("Setting not currently supported on this fake") + + private fun mockSearchEngine( + id: String, + n: String = id + // TODO this fails with `Missing calls inside every { ... } block`. Not sure why +// ) = mockk { +// every { identifier } returns id +// every { name } returns n +// every { icon } returns mockk() +// } + ): SearchEngine { + return mock(SearchEngine::class.java).apply { + `when`(identifier).thenReturn(id) + `when`(name).thenReturn(n) + `when`(icon).thenReturn(mock(Bitmap::class.java)) + } + } +}