[fenix] Add updated wallpaper use cases and activate them with feature flag.

pull/600/head
MatthewTighe 2 years ago committed by mergify[bot]
parent 0b1667df4b
commit f98b9275da

@ -7,6 +7,7 @@ package org.mozilla.fenix.ui
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.customannotations.SmokeTest
@ -143,6 +144,7 @@ class SettingsHomepageTest {
@SmokeTest
@Test
@Ignore("Intermittent test: https://github.com/mozilla-mobile/fenix/issues/26559")
fun setWallpaperTest() {
val wallpapers = listOf(
"Wallpaper Item: amethyst",

@ -185,7 +185,7 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = ""
)
var currentWallpaper by stringPreference(
var currentWallpaperName by stringPreference(
appContext.getPreferenceKey(R.string.pref_key_current_wallpaper),
default = Wallpaper.Default.name
)

@ -11,6 +11,13 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
/**
* Manages various functions related to the locally-stored wallpaper assets.
*
* @property rootDirectory The top level app-local storage directory.
* @param coroutineDispatcher Dispatcher used to execute suspending functions. Default parameter
* should be likely be used except for when under test.
*/
class LegacyWallpaperFileManager(
private val rootDirectory: File,
coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO

@ -83,14 +83,20 @@ data class Wallpaper(
* @param type The type of image that should be retrieved.
* @param name The name of the wallpaper.
*/
fun getLocalPath(type: ImageType, name: String) = "wallpapers/$name/${type.lowercase()}.png"
fun getLocalPath(name: String, type: ImageType) = "wallpapers/$name/${type.lowercase()}.png"
}
/**
* Defines various image asset types that can be downloaded for each wallpaper.
*/
enum class ImageType {
Portrait,
Landscape,
Thumbnail;
/**
* Get a lowercase string representation of the [ImageType.name] for use in path segments.
*/
fun lowercase(): String = this.name.lowercase()
}
}

@ -10,7 +10,6 @@ import kotlinx.coroutines.withContext
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.Request
import mozilla.components.concept.fetch.isSuccess
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.wallpapers.Wallpaper.Companion.getLocalPath
import java.io.File
@ -18,11 +17,13 @@ import java.io.File
/**
* Can download wallpapers from a remote host.
*
* @param filesDir The top level app-local storage directory.
* @param storageRootDirectory The top level app-local storage directory.
* @param client Required for fetching files from network.
* @param dispatcher Dispatcher used to execute suspending functions. Default parameter
* should be likely be used except for when under test.
*/
class WallpaperDownloader(
private val filesDir: File,
private val storageRootDirectory: File,
private val client: Client,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
@ -37,7 +38,7 @@ class WallpaperDownloader(
*/
suspend fun downloadWallpaper(wallpaper: Wallpaper) = withContext(dispatcher) {
for (metadata in wallpaper.toMetadata()) {
val localFile = File(filesDir.absolutePath, metadata.localPath)
val localFile = File(storageRootDirectory.absolutePath, metadata.localPath)
// Don't overwrite an asset if it exists
if (localFile.exists()) continue
val request = Request(
@ -54,7 +55,7 @@ class WallpaperDownloader(
input.copyTo(localFile.outputStream())
}
}.onFailure {
// This should clean up any partial downloads.
// This should clean up any partial downloads
Result.runCatching {
if (localFile.exists()) {
localFile.delete()
@ -68,8 +69,8 @@ class WallpaperDownloader(
private fun Wallpaper.toMetadata(): List<WallpaperMetadata> =
listOf(Wallpaper.ImageType.Portrait, Wallpaper.ImageType.Landscape).map { orientation ->
val localPath = getLocalPath(orientation, this.name)
val remotePath = "${collection.name}/${this.name}/${orientation.lowercase()}.png"
WallpaperMetadata(remotePath, localPath)
}
val localPath = getLocalPath(this.name, orientation)
val remotePath = "${collection.name}/${this.name}/${orientation.lowercase()}.png"
WallpaperMetadata(remotePath, localPath)
}
}

@ -12,19 +12,26 @@ import kotlinx.coroutines.withContext
import org.mozilla.fenix.wallpapers.Wallpaper.Companion.getLocalPath
import java.io.File
/**
* Manages various functions related to the locally-stored wallpaper assets.
*
* @property storageRootDirectory The top level app-local storage directory.
* @param coroutineDispatcher Dispatcher used to execute suspending functions. Default parameter
* should be likely be used except for when under test.
*/
class WallpaperFileManager(
private val rootDirectory: File,
private val storageRootDirectory: File,
coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
private val scope = CoroutineScope(coroutineDispatcher)
private val wallpapersDirectory = File(rootDirectory, "wallpapers")
private val wallpapersDirectory = File(storageRootDirectory, "wallpapers")
/**
* Lookup all the files for a wallpaper name. This lookup will fail if there are not
* files for each of a portrait and landscape orientation as well as a thumbnail.
*/
suspend fun lookupExpiredWallpaper(name: String): Wallpaper? = withContext(Dispatchers.IO) {
if (getAllLocalWallpaperPaths(name).all { File(rootDirectory, it).exists() }) {
if (getAllLocalWallpaperPaths(name).all { File(storageRootDirectory, it).exists() }) {
Wallpaper(
name = name,
collection = Wallpaper.DefaultCollection,
@ -36,16 +43,16 @@ class WallpaperFileManager(
private fun getAllLocalWallpaperPaths(name: String): List<String> =
Wallpaper.ImageType.values().map { orientation ->
getLocalPath(orientation, name)
getLocalPath(name, orientation)
}
/**
* Remove all wallpapers that are not the [currentWallpaper] or in [availableWallpapers].
*/
fun clean(currentWallpaper: Wallpaper, availableWallpapers: List<Wallpaper>) {
suspend fun clean(currentWallpaper: Wallpaper, availableWallpapers: List<Wallpaper>) = withContext(Dispatchers.IO) {
scope.launch {
val wallpapersToKeep = (listOf(currentWallpaper) + availableWallpapers).map { it.name }
for (file in wallpapersDirectory.listFiles()?.toList() ?: listOf()) {
wallpapersDirectory.listFiles()?.forEach { file ->
if (file.isDirectory && !wallpapersToKeep.contains(file.name)) {
file.deleteRecursively()
}

@ -24,7 +24,7 @@ class WallpaperManager(
/**
* Get whether the default wallpaper should be used.
*/
fun isDefaultTheCurrentWallpaper(settings: Settings): Boolean = with(settings.currentWallpaper) {
fun isDefaultTheCurrentWallpaper(settings: Settings): Boolean = with(settings.currentWallpaperName) {
return isEmpty() || equals(defaultWallpaper.name)
}

@ -48,11 +48,11 @@ class WallpaperMetadataFetcher(
private fun JSONObject.toCollectionOfWallpapers(): List<Wallpaper> {
val collectionId = getString("id")
val heading = optString("heading")
val description = optString("description")
val heading = optStringOrNull("heading")
val description = optStringOrNull("description")
val availableLocales = optJSONArray("available-locales")?.getAvailableLocales()
val availabilityRange = optJSONObject("availability-range")?.getAvailabilityRange()
val learnMoreUrl = optString("learn-more-url")
val learnMoreUrl = optStringOrNull("learn-more-url")
val collection = Wallpaper.Collection(
name = collectionId,
heading = heading,
@ -87,6 +87,15 @@ class WallpaperMetadataFetcher(
}
}
/**
* Normally, if a field is specified in json as null, then optString will return it as "null". If
* a field is missing completely, optString will return "". This will correctly return null in
* both those cases so that optional properties are marked as missing.
*/
private fun JSONObject.optStringOrNull(propName: String) = optString(propName).takeIf {
it != "null" && it.isNotEmpty()
}
/**
* The wallpaper metadata has 6 digit hex color codes for compatibility with iOS. Since Android
* expects 8 digit ARBG values, we prepend FF for the "fully visible" version of the color

@ -14,6 +14,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import mozilla.components.concept.fetch.Client
import mozilla.components.support.locale.LocaleManager
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.GleanMetrics.Wallpapers
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
@ -41,26 +42,53 @@ class WallpapersUseCases(
context: Context,
store: AppStore,
client: Client,
strictMode: StrictModeManager
strictMode: StrictModeManager,
) {
val initialize: InitializeWallpapersUseCase by lazy {
// Required to even access context.filesDir property and to retrieve current locale
val (fileManager, currentLocale) = strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
val fileManager = LegacyWallpaperFileManager(context.filesDir)
val currentLocale = LocaleManager.getCurrentLocale(context)?.toLanguageTag()
?: LocaleManager.getSystemDefault().toLanguageTag()
fileManager to currentLocale
if (FeatureFlags.wallpaperV2Enabled) {
// Required to even access context.filesDir property and to retrieve current locale
val (storageRootDirectory, currentLocale) = strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
val storageRootDirectory = context.filesDir
val currentLocale = LocaleManager.getCurrentLocale(context)?.toLanguageTag()
?: LocaleManager.getSystemDefault().toLanguageTag()
storageRootDirectory to currentLocale
}
val downloader = WallpaperDownloader(storageRootDirectory, client)
val fileManager = WallpaperFileManager(storageRootDirectory)
val metadataFetcher = WallpaperMetadataFetcher(client)
DefaultInitializeWallpaperUseCase(
store = store,
downloader = downloader,
fileManager = fileManager,
metadataFetcher = metadataFetcher,
settings = context.settings(),
currentLocale = currentLocale
)
} else {
// Required to even access context.filesDir property and to retrieve current locale
val (fileManager, currentLocale) = strictMode.resetAfter(StrictMode.allowThreadDiskReads()) {
val fileManager = LegacyWallpaperFileManager(context.filesDir)
val currentLocale = LocaleManager.getCurrentLocale(context)?.toLanguageTag()
?: LocaleManager.getSystemDefault().toLanguageTag()
fileManager to currentLocale
}
val downloader = LegacyWallpaperDownloader(context, client)
LegacyInitializeWallpaperUseCase(
store = store,
downloader = downloader,
fileManager = fileManager,
settings = context.settings(),
currentLocale = currentLocale
)
}
}
val loadBitmap: LoadBitmapUseCase by lazy {
if (FeatureFlags.wallpaperV2Enabled) {
DefaultLoadBitmapUseCase(context)
} else {
LegacyLoadBitmapUseCase(context)
}
val downloader = LegacyWallpaperDownloader(context, client)
LegacyInitializeWallpaperUseCase(
store = store,
downloader = downloader,
fileManager = fileManager,
settings = context.settings(),
currentLocale = currentLocale
)
}
val loadBitmap: LoadBitmapUseCase by lazy { LegacyLoadBitmapUseCase(context) }
val selectWallpaper: SelectWallpaperUseCase by lazy { DefaultSelectWallpaperUseCase(context.settings(), store) }
/**
@ -97,7 +125,7 @@ class WallpapersUseCases(
// and download utilities.
withContext(Dispatchers.IO) {
val availableWallpapers = possibleWallpapers.getAvailableWallpapers()
val currentWallpaperName = settings.currentWallpaper
val currentWallpaperName = settings.currentWallpaperName
val currentWallpaper = possibleWallpapers.find { it.name == currentWallpaperName }
?: fileManager.lookupExpiredWallpaper(currentWallpaperName)
?: Wallpaper.Default
@ -125,7 +153,7 @@ class WallpapersUseCases(
private fun Wallpaper.isExpired(): Boolean {
val expired = this.collection.endDate?.let { Date().after(it) } ?: false
return expired && this.name != settings.currentWallpaper
return expired && this.name != settings.currentWallpaperName
}
private fun Wallpaper.isAvailableInLocale(): Boolean =
@ -179,6 +207,49 @@ class WallpapersUseCases(
}
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal class DefaultInitializeWallpaperUseCase(
private val store: AppStore,
private val downloader: WallpaperDownloader,
private val fileManager: WallpaperFileManager,
private val metadataFetcher: WallpaperMetadataFetcher,
private val settings: Settings,
private val currentLocale: String,
) : InitializeWallpapersUseCase {
override suspend fun invoke() {
val currentWallpaperName = withContext(Dispatchers.IO) { settings.currentWallpaperName }
val possibleWallpapers = metadataFetcher.downloadWallpaperList().filter {
!it.isExpired() && it.isAvailableInLocale()
}
val currentWallpaper = possibleWallpapers.find { it.name == currentWallpaperName }
?: fileManager.lookupExpiredWallpaper(currentWallpaperName)
?: Wallpaper.Default
// Dispatching this early will make it accessible to the home screen ASAP
store.dispatch(AppAction.WallpaperAction.UpdateCurrentWallpaper(currentWallpaper))
fileManager.clean(
currentWallpaper,
possibleWallpapers
)
possibleWallpapers.forEach { downloader.downloadWallpaper(it) }
val defaultIncluded = listOf(Wallpaper.Default) + possibleWallpapers
store.dispatch(AppAction.WallpaperAction.UpdateAvailableWallpapers(defaultIncluded))
}
private fun Wallpaper.isExpired(): Boolean = when (this) {
Wallpaper.Default -> false
else -> {
val expired = this.collection.endDate?.let { Date().after(it) } ?: false
expired && this.name != settings.currentWallpaperName
}
}
private fun Wallpaper.isAvailableInLocale(): Boolean =
this.collection.availableLocales?.contains(currentLocale) ?: true
}
/**
* Contract for usecase for loading bitmaps related to a specific wallpaper.
*/
@ -254,6 +325,40 @@ class WallpapersUseCases(
}
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal class DefaultLoadBitmapUseCase(private val context: Context) : LoadBitmapUseCase {
override suspend fun invoke(wallpaper: Wallpaper): Bitmap? =
loadWallpaperFromDisk(context, wallpaper)
private suspend fun loadWallpaperFromDisk(
context: Context,
wallpaper: Wallpaper
): Bitmap? = Result.runCatching {
val path = wallpaper.getLocalPathFromContext(context)
withContext(Dispatchers.IO) {
val file = File(context.filesDir, path)
BitmapFactory.decodeStream(file.inputStream())
}
}.getOrNull()
/**
* Get the expected local path on disk for a wallpaper. This will differ depending
* on orientation and app theme.
*/
private fun Wallpaper.getLocalPathFromContext(context: Context): String {
val orientation = if (context.isLandscape()) {
Wallpaper.ImageType.Landscape
} else {
Wallpaper.ImageType.Portrait
}
return Wallpaper.getLocalPath(name, orientation)
}
private fun Context.isLandscape(): Boolean {
return resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
}
}
/**
* Contract for usecase of selecting a new wallpaper.
*/
@ -277,7 +382,7 @@ class WallpapersUseCases(
* @param wallpaper The selected wallpaper.
*/
override fun invoke(wallpaper: Wallpaper) {
settings.currentWallpaper = wallpaper.name
settings.currentWallpaperName = wallpaper.name
store.dispatch(AppAction.WallpaperAction.UpdateCurrentWallpaper(wallpaper))
Wallpapers.wallpaperSelected.record(
Wallpapers.WallpaperSelectedExtra(

@ -2,16 +2,13 @@ package org.mozilla.fenix.wallpapers
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.Request
import mozilla.components.concept.fetch.Response
import mozilla.components.concept.fetch.isSuccess
import org.junit.Assert.*
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -56,14 +53,8 @@ class WallpaperDownloaderTest {
@Test
fun `GIVEN that request is successful WHEN downloading THEN file is created in expected location`() = runTest {
val wallpaper = generateWallpaper()
val portraitRequest = Request(
url = "$remoteHost/${wallpaper.collection.name}/${wallpaper.name}/portrait.png",
method = Request.Method.GET
)
val landscapeRequest = Request(
url = "$remoteHost/${wallpaper.collection.name}/${wallpaper.name}/landscape.png",
method = Request.Method.GET
)
val portraitRequest = wallpaper.generateRequest("portrait")
val landscapeRequest = wallpaper.generateRequest("landscape")
every { mockPortraitResponse.status } returns 200
every { mockLandscapeResponse.status } returns 200
every { mockPortraitResponse.body } returns portraitResponseBodySuccess
@ -82,14 +73,8 @@ class WallpaperDownloaderTest {
@Test
fun `GIVEN that request fails WHEN downloading THEN file is not created`() = runTest {
val wallpaper = generateWallpaper()
val portraitRequest = Request(
url = "$remoteHost/${wallpaper.collection.name}/${wallpaper.name}/portrait.png",
method = Request.Method.GET
)
val landscapeRequest = Request(
url = "$remoteHost/${wallpaper.collection.name}/${wallpaper.name}/landscape.png",
method = Request.Method.GET
)
val portraitRequest = wallpaper.generateRequest("portrait")
val landscapeRequest = wallpaper.generateRequest("landscape")
every { mockPortraitResponse.status } returns 400
every { mockLandscapeResponse.status } returns 400
every { mockClient.fetch(portraitRequest) } returns mockPortraitResponse
@ -106,14 +91,8 @@ class WallpaperDownloaderTest {
@Test
fun `GIVEN that copying the file fails WHEN downloading THEN file is not created`() = runTest {
val wallpaper = generateWallpaper()
val portraitRequest = Request(
url = "$remoteHost/${wallpaper.collection.name}/${wallpaper.name}/portrait.png",
method = Request.Method.GET
)
val landscapeRequest = Request(
url = "$remoteHost/${wallpaper.collection.name}/${wallpaper.name}/landscape.png",
method = Request.Method.GET
)
val portraitRequest = wallpaper.generateRequest("portrait")
val landscapeRequest = wallpaper.generateRequest("landscape")
every { mockPortraitResponse.status } returns 200
every { mockLandscapeResponse.status } returns 200
every { mockPortraitResponse.body } throws IllegalStateException()
@ -134,4 +113,9 @@ class WallpaperDownloaderTest {
textColor = null,
cardColor = null
)
}
private fun Wallpaper.generateRequest(type: String) = Request(
url = "$remoteHost/${collection.name}/$name/$type.png",
method = Request.Method.GET
)
}

@ -2,7 +2,7 @@ package org.mozilla.fenix.wallpapers
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
@ -24,7 +24,7 @@ class WallpaperFileManagerTest {
fun setup() {
wallpapersFolder = File(tempFolder.root, "wallpapers")
fileManager = WallpaperFileManager(
rootDirectory = tempFolder.root,
storageRootDirectory = tempFolder.root,
coroutineDispatcher = dispatcher,
)
}
@ -37,7 +37,7 @@ class WallpaperFileManagerTest {
val result = fileManager.lookupExpiredWallpaper(wallpaperName)
val expected = generateWallpaper(name = wallpaperName)
Assert.assertEquals(expected, result)
assertEquals(expected, result)
}
@Test
@ -54,7 +54,7 @@ class WallpaperFileManagerTest {
val result = fileManager.lookupExpiredWallpaper(wallpaperName)
Assert.assertEquals(null, result)
assertEquals(null, result)
}
@Test
@ -71,7 +71,7 @@ class WallpaperFileManagerTest {
val result = fileManager.lookupExpiredWallpaper(wallpaperName)
Assert.assertEquals(null, result)
assertEquals(null, result)
}
@Test
@ -88,11 +88,11 @@ class WallpaperFileManagerTest {
val result = fileManager.lookupExpiredWallpaper(wallpaperName)
Assert.assertEquals(null, result)
assertEquals(null, result)
}
@Test
fun `WHEN cleaned THEN current wallpaper and available wallpapers kept`() {
fun `WHEN cleaned THEN current wallpaper and available wallpapers kept`() = runTest {
val currentName = "current"
val currentWallpaper = generateWallpaper(name = currentName)
val availableName = "available"
@ -132,4 +132,4 @@ class WallpaperFileManagerTest {
cardColor = null,
collection = Wallpaper.DefaultCollection
)
}
}

@ -13,7 +13,7 @@ class WallpaperManagerTest {
@Test
fun `GIVEN no custom wallpaper set WHEN checking whether the current wallpaper should be default THEN return true`() {
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
val result = WallpaperManager.isDefaultTheCurrentWallpaper(mockSettings)
@ -22,7 +22,7 @@ class WallpaperManagerTest {
@Test
fun `GIVEN the default wallpaper is set to be shown WHEN checking whether the current wallpaper should be default THEN return true`() {
every { mockSettings.currentWallpaper } returns WallpaperManager.defaultWallpaper.name
every { mockSettings.currentWallpaperName } returns WallpaperManager.defaultWallpaper.name
val result = WallpaperManager.isDefaultTheCurrentWallpaper(mockSettings)
@ -31,7 +31,7 @@ class WallpaperManagerTest {
@Test
fun `GIVEN a custom wallpaper is set to be shown WHEN checking whether the current wallpaper should be default THEN return false`() {
every { mockSettings.currentWallpaper } returns "test"
every { mockSettings.currentWallpaperName } returns "test"
val result = WallpaperManager.isDefaultTheCurrentWallpaper(mockSettings)

@ -346,4 +346,45 @@ class WallpaperMetadataFetcherTest {
}
)
}
@Test
fun `GIVEN string fields with null values WHEN parsed THEN fields are correctly null`() = runTest {
val json = """
{
"last-updated-date": "2022-01-01",
"collections": [
{
"id": "classic-firefox",
"heading": null,
"description": null,
"available-locales": null,
"availability-range": null,
"learn-more-url": null,
"wallpapers": [
{
"id": "beach-vibes",
"text-color": "FBFBFE",
"card-color": "15141A"
},
{
"id": "sunrise",
"text-color": "15141A",
"card-color": "FBFBFE"
}
]
}
]
}
""".trimIndent()
every { mockResponse.body } returns Response.Body(json.byteInputStream())
val wallpapers = metadataFetcher.downloadWallpaperList()
assertTrue(wallpapers.isNotEmpty())
assertTrue(
wallpapers.all {
it.collection.heading == null && it.collection.description == null
}
)
}
}

@ -37,26 +37,214 @@ class WallpapersUseCasesTest {
private val appStore = AppStore()
private val mockSettings = mockk<Settings>()
private val mockDownloader = mockk<LegacyWallpaperDownloader>(relaxed = true)
private val mockFileManager = mockk<LegacyWallpaperFileManager> {
private val mockLegacyDownloader = mockk<LegacyWallpaperDownloader>(relaxed = true)
private val mockLegacyFileManager = mockk<LegacyWallpaperFileManager> {
every { clean(any(), any()) } just runs
}
private val mockMetadataFetcher = mockk<WallpaperMetadataFetcher>()
private val mockDownloader = mockk<WallpaperDownloader>(relaxed = true)
private val mockFileManager = mockk<WallpaperFileManager> {
coEvery { clean(any(), any()) } returns mockk()
}
@Test
fun `GIVEN legacy use case WHEN initializing THEN the default wallpaper is not downloaded`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = listOf(Wallpaper.Default) + fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
coVerify(exactly = 0) { mockLegacyDownloader.downloadWallpaper(Wallpaper.Default) }
}
@Test
fun `GIVEN legacy use case WHEN initializing THEN default wallpaper is included in available wallpapers`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = listOf(Wallpaper.Default) + fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
assertTrue(appStore.state.wallpaperState.availableWallpapers.contains(Wallpaper.Default))
}
@Test
fun `GIVEN legacy use case and wallpapers that expired WHEN invoking initialize use case THEN expired wallpapers are filtered out and cleaned up`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val fakeExpiredRemoteWallpapers = listOf("expired").map { name ->
makeFakeRemoteWallpaper(TimeRelation.BEFORE, name)
}
val possibleWallpapers = fakeRemoteWallpapers + fakeExpiredRemoteWallpapers
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = possibleWallpapers
).invoke()
val expectedFilteredWallpaper = fakeExpiredRemoteWallpapers[0]
appStore.waitUntilIdle()
assertFalse(appStore.state.wallpaperState.availableWallpapers.contains(expectedFilteredWallpaper))
verify { mockLegacyFileManager.clean(Wallpaper.Default, possibleWallpapers) }
}
@Test
fun `GIVEN leagacy use case and wallpapers that expired and an expired one is selected WHEN invoking initialize use case THEN selected wallpaper is not filtered out`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val expiredWallpaper = makeFakeRemoteWallpaper(TimeRelation.BEFORE, "expired")
every { mockSettings.currentWallpaperName } returns expiredWallpaper.name
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = fakeRemoteWallpapers + listOf(expiredWallpaper)
).invoke()
appStore.waitUntilIdle()
assertTrue(appStore.state.wallpaperState.availableWallpapers.contains(expiredWallpaper))
assertEquals(expiredWallpaper, appStore.state.wallpaperState.currentWallpaper)
}
@Test
fun `GIVEN legacy use case and wallpapers that are in promotions outside of locale WHEN invoking initialize use case THEN promotional wallpapers are filtered out`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val locale = "en-CA"
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
locale,
possibleWallpapers = fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
assertTrue(appStore.state.wallpaperState.availableWallpapers.isEmpty())
}
@Test
fun `GIVEN legacy use case and available wallpapers WHEN invoking initialize use case THEN available wallpapers downloaded`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = fakeRemoteWallpapers
).invoke()
for (fakeRemoteWallpaper in fakeRemoteWallpapers) {
coVerify { mockLegacyDownloader.downloadWallpaper(fakeRemoteWallpaper) }
}
}
@Test
fun `GIVEN legacy use case and a wallpaper has not been selected WHEN invoking initialize use case THEN store contains default`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
assertTrue(appStore.state.wallpaperState.currentWallpaper == Wallpaper.Default)
}
@Test
fun `GIVEN legacy use case a wallpaper is selected and there are available wallpapers WHEN invoking initialize use case THEN these are dispatched to the store`() = runTest {
val selectedWallpaper = makeFakeRemoteWallpaper(TimeRelation.LATER, "selected")
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val possibleWallpapers = listOf(selectedWallpaper) + fakeRemoteWallpapers
every { mockSettings.currentWallpaperName } returns selectedWallpaper.name
coEvery { mockLegacyFileManager.lookupExpiredWallpaper(any()) } returns null
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
appStore,
mockLegacyDownloader,
mockLegacyFileManager,
mockSettings,
"en-US",
possibleWallpapers = possibleWallpapers
).invoke()
appStore.waitUntilIdle()
assertEquals(selectedWallpaper, appStore.state.wallpaperState.currentWallpaper)
assertEquals(possibleWallpapers, appStore.state.wallpaperState.availableWallpapers)
}
@Test
fun `WHEN initializing THEN the default wallpaper is not downloaded`() = runTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns fakeRemoteWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = listOf(Wallpaper.Default) + fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
@ -68,16 +256,17 @@ class WallpapersUseCasesTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns fakeRemoteWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = listOf(Wallpaper.Default) + fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
@ -93,22 +282,23 @@ class WallpapersUseCasesTest {
makeFakeRemoteWallpaper(TimeRelation.BEFORE, name)
}
val possibleWallpapers = fakeRemoteWallpapers + fakeExpiredRemoteWallpapers
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns possibleWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = possibleWallpapers
).invoke()
val expectedFilteredWallpaper = fakeExpiredRemoteWallpapers[0]
appStore.waitUntilIdle()
assertFalse(appStore.state.wallpaperState.availableWallpapers.contains(expectedFilteredWallpaper))
verify { mockFileManager.clean(Wallpaper.Default, possibleWallpapers) }
coVerify { mockFileManager.clean(Wallpaper.Default, fakeRemoteWallpapers) }
}
@Test
@ -117,16 +307,17 @@ class WallpapersUseCasesTest {
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val expiredWallpaper = makeFakeRemoteWallpaper(TimeRelation.BEFORE, "expired")
every { mockSettings.currentWallpaper } returns expiredWallpaper.name
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
every { mockSettings.currentWallpaperName } returns expiredWallpaper.name
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns expiredWallpaper
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns listOf(expiredWallpaper) + fakeRemoteWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = fakeRemoteWallpapers + listOf(expiredWallpaper)
).invoke()
appStore.waitUntilIdle()
@ -140,20 +331,22 @@ class WallpapersUseCasesTest {
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val locale = "en-CA"
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns fakeRemoteWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
locale,
possibleWallpapers = fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
assertTrue(appStore.state.wallpaperState.availableWallpapers.isEmpty())
assertEquals(1, appStore.state.wallpaperState.availableWallpapers.size)
assertEquals(Wallpaper.Default, appStore.state.wallpaperState.availableWallpapers[0])
}
@Test
@ -161,16 +354,17 @@ class WallpapersUseCasesTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns fakeRemoteWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = fakeRemoteWallpapers
).invoke()
for (fakeRemoteWallpaper in fakeRemoteWallpapers) {
@ -183,16 +377,17 @@ class WallpapersUseCasesTest {
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaperName } returns ""
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns fakeRemoteWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = fakeRemoteWallpapers
).invoke()
appStore.waitUntilIdle()
@ -206,21 +401,23 @@ class WallpapersUseCasesTest {
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
}
val possibleWallpapers = listOf(selectedWallpaper) + fakeRemoteWallpapers
every { mockSettings.currentWallpaper } returns selectedWallpaper.name
val allWallpapers = listOf(Wallpaper.Default) + possibleWallpapers
every { mockSettings.currentWallpaperName } returns selectedWallpaper.name
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
coEvery { mockMetadataFetcher.downloadWallpaperList() } returns possibleWallpapers
WallpapersUseCases.LegacyInitializeWallpaperUseCase(
WallpapersUseCases.DefaultInitializeWallpaperUseCase(
appStore,
mockDownloader,
mockFileManager,
mockMetadataFetcher,
mockSettings,
"en-US",
possibleWallpapers = possibleWallpapers
).invoke()
appStore.waitUntilIdle()
assertEquals(selectedWallpaper, appStore.state.wallpaperState.currentWallpaper)
assertEquals(possibleWallpapers, appStore.state.wallpaperState.availableWallpapers)
assertEquals(allWallpapers, appStore.state.wallpaperState.availableWallpapers)
}
@Test
@ -228,8 +425,8 @@ class WallpapersUseCasesTest {
val selectedWallpaper = makeFakeRemoteWallpaper(TimeRelation.LATER, "selected")
val slot = slot<String>()
coEvery { mockFileManager.lookupExpiredWallpaper(any()) } returns null
every { mockSettings.currentWallpaper } returns ""
every { mockSettings.currentWallpaper = capture(slot) } just runs
every { mockSettings.currentWallpaperName } returns ""
every { mockSettings.currentWallpaperName = capture(slot) } just runs
WallpapersUseCases.DefaultSelectWallpaperUseCase(
mockSettings,

Loading…
Cancel
Save