[fenix] closes https://github.com/mozilla-mobile/fenix/issues/23565: expire remote wallpapers but let users keep selected
parent
1ff747b109
commit
72a0985403
@ -0,0 +1,56 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.wallpapers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class WallpaperFileManager(
|
||||||
|
private val rootDirectory: File,
|
||||||
|
coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
) {
|
||||||
|
private val scope = CoroutineScope(coroutineDispatcher)
|
||||||
|
private val portraitDirectory = File(rootDirectory, "wallpapers/portrait")
|
||||||
|
private val landscapeDirectory = File(rootDirectory, "wallpapers/landscape")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup all the files for a wallpaper name. This lookup will fail if there are not
|
||||||
|
* files for each of the following orientation and theme combinations:
|
||||||
|
* light/portrait - light/landscape - dark/portrait - dark/landscape
|
||||||
|
*/
|
||||||
|
fun lookupExpiredWallpaper(name: String): Wallpaper.Remote.Expired? {
|
||||||
|
return if (getAllLocalWallpaperPaths(name).all { File(rootDirectory, it).exists() }) {
|
||||||
|
Wallpaper.Remote.Expired(name)
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAllLocalWallpaperPaths(name: String): List<String> =
|
||||||
|
listOf("landscape", "portrait").flatMap { orientation ->
|
||||||
|
listOf("light", "dark").map { theme ->
|
||||||
|
Wallpaper.getBaseLocalPath(orientation, theme, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all wallpapers that are not the [currentWallpaper] or in [availableWallpapers].
|
||||||
|
*/
|
||||||
|
fun clean(currentWallpaper: Wallpaper, availableWallpapers: List<Wallpaper.Remote>) {
|
||||||
|
val wallpapersToKeep = (listOf(currentWallpaper) + availableWallpapers).map { it.name }
|
||||||
|
cleanChildren(portraitDirectory, wallpapersToKeep)
|
||||||
|
cleanChildren(landscapeDirectory, wallpapersToKeep)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanChildren(dir: File, wallpapersToKeep: List<String>) {
|
||||||
|
for (file in dir.walkTopDown()) {
|
||||||
|
if (file.isDirectory || file.nameWithoutExtension in wallpapersToKeep) continue
|
||||||
|
scope.launch {
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.wallpapers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TemporaryFolder
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class WallpaperFileManagerTest {
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val tempFolder = TemporaryFolder()
|
||||||
|
private lateinit var portraitLightFolder: File
|
||||||
|
private lateinit var portraitDarkFolder: File
|
||||||
|
private lateinit var landscapeLightFolder: File
|
||||||
|
private lateinit var landscapeDarkFolder: File
|
||||||
|
|
||||||
|
private val dispatcher = TestCoroutineDispatcher()
|
||||||
|
|
||||||
|
private lateinit var fileManager: WallpaperFileManager
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
portraitLightFolder = tempFolder.newFolder("wallpapers", "portrait", "light")
|
||||||
|
portraitDarkFolder = tempFolder.newFolder("wallpapers", "portrait", "dark")
|
||||||
|
landscapeLightFolder = tempFolder.newFolder("wallpapers", "landscape", "light")
|
||||||
|
landscapeDarkFolder = tempFolder.newFolder("wallpapers", "landscape", "dark")
|
||||||
|
fileManager = WallpaperFileManager(
|
||||||
|
rootDirectory = tempFolder.root,
|
||||||
|
coroutineDispatcher = dispatcher,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN files exist in all directories WHEN expired wallpaper looked up THEN expired wallpaper returned`() {
|
||||||
|
val wallpaperName = "name"
|
||||||
|
createAllFiles(wallpaperName)
|
||||||
|
|
||||||
|
val expected = Wallpaper.Remote.Expired(name = wallpaperName)
|
||||||
|
assertEquals(expected, fileManager.lookupExpiredWallpaper(wallpaperName))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN any missing file in directories WHEN expired wallpaper looked up THEN null returned`() {
|
||||||
|
val wallpaperName = "name"
|
||||||
|
File(landscapeLightFolder, "$wallpaperName.png").createNewFile()
|
||||||
|
File(landscapeDarkFolder, "$wallpaperName.png").createNewFile()
|
||||||
|
|
||||||
|
assertEquals(null, fileManager.lookupExpiredWallpaper(wallpaperName))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN cleaned THEN current wallpaper and available wallpapers kept`() {
|
||||||
|
val currentName = "current"
|
||||||
|
val currentWallpaper = Wallpaper.Remote.Expired(currentName)
|
||||||
|
val availableName = "available"
|
||||||
|
val available = Wallpaper.Remote.Focus(name = availableName)
|
||||||
|
val unavailableName = "unavailable"
|
||||||
|
createAllFiles(currentName)
|
||||||
|
createAllFiles(availableName)
|
||||||
|
createAllFiles(unavailableName)
|
||||||
|
|
||||||
|
fileManager.clean(currentWallpaper, listOf(available))
|
||||||
|
|
||||||
|
assertTrue(getAllFiles(currentName).all { it.exists() })
|
||||||
|
assertTrue(getAllFiles(availableName).all { it.exists() })
|
||||||
|
assertTrue(getAllFiles(unavailableName).none { it.exists() })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAllFiles(name: String) {
|
||||||
|
for (file in getAllFiles(name)) {
|
||||||
|
file.createNewFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAllFiles(name: String): List<File> {
|
||||||
|
return listOf(
|
||||||
|
File(portraitLightFolder, "$name.png"),
|
||||||
|
File(portraitDarkFolder, "$name.png"),
|
||||||
|
File(landscapeLightFolder, "$name.png"),
|
||||||
|
File(landscapeDarkFolder, "$name.png"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,150 @@
|
|||||||
package org.mozilla.fenix.wallpapers
|
package org.mozilla.fenix.wallpapers
|
||||||
|
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.runs
|
import io.mockk.runs
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
import org.mozilla.fenix.components.metrics.MetricController
|
||||||
import org.mozilla.fenix.utils.Settings
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class WallpaperManagerTest {
|
class WallpaperManagerTest {
|
||||||
|
|
||||||
|
// initialize this once, so it can be shared throughout tests
|
||||||
|
private val baseFakeDate = Date()
|
||||||
|
private val fakeCalendar = Calendar.getInstance()
|
||||||
|
|
||||||
private val mockSettings: Settings = mockk()
|
private val mockSettings: Settings = mockk()
|
||||||
private val mockMetrics: MetricController = mockk()
|
private val mockMetrics: MetricController = mockk()
|
||||||
|
private val mockDownloader: WallpaperDownloader = mockk {
|
||||||
|
coEvery { downloadWallpaper(any()) } just runs
|
||||||
|
}
|
||||||
|
private val mockFileManager: WallpaperFileManager = mockk {
|
||||||
|
every { clean(any(), any()) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `WHEN wallpaper set THEN current wallpaper updated in settings`() {
|
fun `WHEN wallpaper set THEN current wallpaper updated in settings`() {
|
||||||
every { mockMetrics.track(any()) } just runs
|
every { mockMetrics.track(any()) } just runs
|
||||||
|
|
||||||
val currentCaptureSlot = slot<String>()
|
val currentCaptureSlot = slot<String>()
|
||||||
every { mockSettings.currentWallpaper } returns "a different name"
|
every { mockSettings.currentWallpaper } returns ""
|
||||||
every { mockSettings.currentWallpaper = capture(currentCaptureSlot) } just runs
|
every { mockSettings.currentWallpaper = capture(currentCaptureSlot) } just runs
|
||||||
|
|
||||||
val updatedWallpaper = WallpaperManager.defaultWallpaper
|
val updatedName = "new name"
|
||||||
val wallpaperManager = WallpaperManager(mockSettings, mockk(), mockk())
|
val updatedWallpaper = Wallpaper.Local.Firefox(updatedName, drawableId = 0)
|
||||||
|
val wallpaperManager = WallpaperManager(mockSettings, mockk(), mockFileManager, mockk(), listOf())
|
||||||
wallpaperManager.currentWallpaper = updatedWallpaper
|
wallpaperManager.currentWallpaper = updatedWallpaper
|
||||||
|
|
||||||
assertEquals(updatedWallpaper.name, currentCaptureSlot.captured)
|
assertEquals(updatedWallpaper.name, currentCaptureSlot.captured)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN no remote wallpapers expired WHEN downloading remote wallpapers THEN all downloaded`() = runBlockingTest {
|
||||||
|
every { mockSettings.currentWallpaper } returns ""
|
||||||
|
val fakeRemoteWallpapers = listOf("first", "second", "third").map { name ->
|
||||||
|
makeFakeRemoteWallpaper(TimeRelation.LATER, name)
|
||||||
|
}
|
||||||
|
val wallpaperManager = WallpaperManager(
|
||||||
|
mockSettings,
|
||||||
|
mockDownloader,
|
||||||
|
mockFileManager,
|
||||||
|
mockk(),
|
||||||
|
allWallpapers = fakeRemoteWallpapers
|
||||||
|
)
|
||||||
|
wallpaperManager.downloadAllRemoteWallpapers()
|
||||||
|
|
||||||
|
for (fakeRemoteWallpaper in fakeRemoteWallpapers) {
|
||||||
|
coVerify { mockDownloader.downloadWallpaper(fakeRemoteWallpaper) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN some expired wallpapers WHEN initialized THEN wallpapers are not available`() {
|
||||||
|
every { mockSettings.currentWallpaper } returns ""
|
||||||
|
val expiredRemoteWallpaper = makeFakeRemoteWallpaper(TimeRelation.BEFORE, "expired")
|
||||||
|
val activeRemoteWallpaper = makeFakeRemoteWallpaper(TimeRelation.LATER, "expired")
|
||||||
|
val wallpaperManager = WallpaperManager(
|
||||||
|
mockSettings,
|
||||||
|
mockDownloader,
|
||||||
|
mockFileManager,
|
||||||
|
mockk(),
|
||||||
|
allWallpapers = listOf(expiredRemoteWallpaper, activeRemoteWallpaper)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertFalse(wallpaperManager.wallpapers.contains(expiredRemoteWallpaper))
|
||||||
|
assertTrue(wallpaperManager.wallpapers.contains(activeRemoteWallpaper))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN current wallpaper is expired THEN it is available as expired even when others are filtered`() {
|
||||||
|
val currentWallpaperName = "named"
|
||||||
|
val currentExpiredWallpaper = makeFakeRemoteWallpaper(TimeRelation.BEFORE, name = currentWallpaperName)
|
||||||
|
every { mockSettings.currentWallpaper } returns currentWallpaperName
|
||||||
|
val expiredRemoteWallpaper = makeFakeRemoteWallpaper(TimeRelation.BEFORE, "expired")
|
||||||
|
val expected = Wallpaper.Remote.Expired(currentWallpaperName)
|
||||||
|
every { mockFileManager.lookupExpiredWallpaper(currentWallpaperName) } returns expected
|
||||||
|
|
||||||
|
val wallpaperManager = WallpaperManager(
|
||||||
|
mockSettings,
|
||||||
|
mockDownloader,
|
||||||
|
mockFileManager,
|
||||||
|
mockk(),
|
||||||
|
allWallpapers = listOf(expiredRemoteWallpaper)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertFalse(wallpaperManager.wallpapers.contains(expiredRemoteWallpaper))
|
||||||
|
assertFalse(wallpaperManager.wallpapers.contains(currentExpiredWallpaper))
|
||||||
|
assertEquals(expected, wallpaperManager.currentWallpaper)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN current wallpaper is expired THEN it is available even if not listed in initial parameter`() {
|
||||||
|
val currentWallpaperName = "named"
|
||||||
|
every { mockSettings.currentWallpaper } returns currentWallpaperName
|
||||||
|
val expected = Wallpaper.Remote.Expired(currentWallpaperName)
|
||||||
|
every { mockFileManager.lookupExpiredWallpaper(currentWallpaperName) } returns expected
|
||||||
|
|
||||||
|
val wallpaperManager = WallpaperManager(
|
||||||
|
mockSettings,
|
||||||
|
mockDownloader,
|
||||||
|
mockFileManager,
|
||||||
|
mockk(),
|
||||||
|
allWallpapers = listOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(expected, wallpaperManager.currentWallpaper)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class TimeRelation {
|
||||||
|
BEFORE,
|
||||||
|
NOW,
|
||||||
|
LATER,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [timeRelation] should specify a time relative to the time the tests are run
|
||||||
|
*/
|
||||||
|
private fun makeFakeRemoteWallpaper(
|
||||||
|
timeRelation: TimeRelation,
|
||||||
|
name: String = "name"
|
||||||
|
): Wallpaper.Remote {
|
||||||
|
fakeCalendar.time = baseFakeDate
|
||||||
|
when (timeRelation) {
|
||||||
|
TimeRelation.BEFORE -> fakeCalendar.add(Calendar.DATE, -5)
|
||||||
|
TimeRelation.NOW -> Unit
|
||||||
|
TimeRelation.LATER -> fakeCalendar.add(Calendar.DATE, 5)
|
||||||
|
}
|
||||||
|
val relativeTime = fakeCalendar.time
|
||||||
|
return Wallpaper.Remote.Focus(name = name, expirationDate = relativeTime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue