Cover both MediaStore save paths with a fake provider

DefaultFileSave had no test at all and AndroidLocalMediaActions only exercised the
failing path, so neither the uniquifying retry nor the new IOException on a null
insert was ever executed.

FakeMediaStoreContentProvider stands in for the media authority: it records the
display names an insert is attempted with, keeps whatever is copied into it, and can
either collide a name the way MediaStore does past 32 files or refuse to create
anything. Both call sites now go through it for the plain save, the retry and the
rejected insert.

Its name cannot follow the Fake<Interface> convention because it extends the
framework ContentProvider, so it joins the existing exceptions in
KonsistClassNameTest next to FakeFileSystem and FakeKeyStore.
This commit is contained in:
hayaksi1
2026-08-10 11:49:15 +03:00
parent fbdb99019e
commit 4558590492
4 changed files with 174 additions and 0 deletions
@@ -12,18 +12,22 @@ import android.net.Uri
import androidx.activity.compose.LocalActivityResultRegistryOwner
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.core.net.toUri
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
import io.element.android.tests.testutils.fake.registerFakeMediaStoreContentProvider
import io.element.android.tests.testutils.robolectric.RobolectricTest
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.robolectric.RuntimeEnvironment
import java.io.File
import java.io.IOException
class AndroidLocalMediaActionsTest : RobolectricTest() {
@Test
@@ -62,6 +66,43 @@ class AndroidLocalMediaActionsTest : RobolectricTest() {
assertThat(result.exceptionOrNull()).isNotNull()
}
@Test
fun `test AndroidLocalMediaAction save on disk inserts the media under its own name`() = runTest {
val mediaStore = registerFakeMediaStoreContentProvider()
val sut = createAndroidLocalMediaActions()
val result = sut.saveOnDisk(aLocalMedia(aMediaFile().toUri()))
assertThat(result.exceptionOrNull()).isNull()
assertThat(mediaStore.insertedDisplayNames).containsExactly("an image file.jpg")
}
@Test
fun `test AndroidLocalMediaAction save on disk retries with a suffixed name when the name cannot be made unique`() = runTest {
val mediaStore = registerFakeMediaStoreContentProvider().apply {
conflictingDisplayNames = setOf("an image file.jpg")
}
val sut = createAndroidLocalMediaActions()
val result = sut.saveOnDisk(aLocalMedia(aMediaFile().toUri()))
assertThat(result.exceptionOrNull()).isNull()
assertThat(mediaStore.insertedDisplayNames).hasSize(2)
assertThat(mediaStore.insertedDisplayNames.last()).matches("an image file_\\d+\\.jpg")
}
@Test
fun `test AndroidLocalMediaAction save on disk fails when the destination cannot be created`() = runTest {
registerFakeMediaStoreContentProvider().apply {
returnsNoUriOnInsert = true
}
val sut = createAndroidLocalMediaActions()
val result = sut.saveOnDisk(aLocalMedia(aMediaFile().toUri()))
assertThat(result.exceptionOrNull()).isInstanceOf(IOException::class.java)
}
private fun aMediaFile(): File {
return File(RuntimeEnvironment.getApplication().cacheDir, "an image file.jpg").apply {
writeText("Some content")
}
}
private fun TestScope.createAndroidLocalMediaActions() = AndroidLocalMediaActions(
RuntimeEnvironment.getApplication(),
testCoroutineDispatchers(),