Media: add more tests
This commit is contained in:
parent
0a268dc27f
commit
ced60c672e
15 changed files with 354 additions and 53 deletions
|
|
@ -18,10 +18,12 @@ package io.element.android.features.messages.impl.media.local
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
|
|
@ -29,8 +31,13 @@ class AndroidLocalMediaFactory @Inject constructor(
|
|||
@ApplicationContext private val context: Context
|
||||
) : LocalMediaFactory {
|
||||
|
||||
override fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? {
|
||||
if (uri == null) return null
|
||||
override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: MimeTypes.OctetStream
|
||||
val uri = mediaFile.path().toUri()
|
||||
return LocalMedia(uri, resolvedMimeType)
|
||||
}
|
||||
|
||||
override fun createFromUri(uri: Uri, mimeType: String?): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: context.contentResolver.getType(uri) ?: MimeTypes.OctetStream
|
||||
return LocalMedia(uri, resolvedMimeType)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,20 @@
|
|||
package io.element.android.features.messages.impl.media.local
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
|
||||
interface LocalMediaFactory {
|
||||
|
||||
/**
|
||||
* This method will create a [LocalMedia] with the given [MediaFile] and [mimeType]
|
||||
*
|
||||
*/
|
||||
fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia
|
||||
|
||||
/**
|
||||
* This method will create a [LocalMedia] with the given [uri] and [mimeType]
|
||||
* If the [mimeType] is null, it'll try to read it from the content.
|
||||
*
|
||||
*/
|
||||
fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia?
|
||||
fun createFromUri(uri: Uri, mimeType: String?): LocalMedia
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.net.toUri
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -32,7 +31,7 @@ import io.element.android.features.messages.impl.media.local.LocalMedia
|
|||
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -40,7 +39,7 @@ import kotlinx.coroutines.launch
|
|||
class MediaViewerPresenter @AssistedInject constructor(
|
||||
@Assisted private val inputs: MediaViewerNode.Inputs,
|
||||
private val localMediaFactory: LocalMediaFactory,
|
||||
private val client: MatrixClient,
|
||||
private val mediaLoader: MatrixMediaLoader,
|
||||
) : Presenter<MediaViewerState> {
|
||||
|
||||
@AssistedFactory
|
||||
|
|
@ -79,14 +78,12 @@ class MediaViewerPresenter @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun CoroutineScope.loadMedia(mediaFile: MutableState<MediaFile?>, localMedia: MutableState<Async<LocalMedia>>) = launch {
|
||||
mediaFile.value = null
|
||||
localMedia.value = Async.Loading()
|
||||
client.mediaLoader.loadMediaFile(inputs.mediaSource, inputs.mimeType)
|
||||
mediaLoader.loadMediaFile(inputs.mediaSource, inputs.mimeType)
|
||||
.onSuccess {
|
||||
mediaFile.value = it
|
||||
}.mapCatching {
|
||||
val uri = it.path().toUri()
|
||||
localMediaFactory.createFromUri(uri, inputs.mimeType)!!
|
||||
localMediaFactory.createFromMediaFile(it, inputs.mimeType)
|
||||
}.onSuccess {
|
||||
localMedia.value = Async.Success(it)
|
||||
}.onFailure {
|
||||
|
|
|
|||
|
|
@ -206,22 +206,22 @@ class MessageComposerPresenter @Inject constructor(
|
|||
mimeType: String? = null,
|
||||
compressIfPossible: Boolean = true,
|
||||
) {
|
||||
if (uri == null) {
|
||||
attachmentsState.value = AttachmentsState.None
|
||||
return
|
||||
}
|
||||
val localMedia = localMediaFactory.createFromUri(uri, mimeType)
|
||||
attachmentsState.value = if (localMedia == null) {
|
||||
AttachmentsState.None
|
||||
val mediaAttachment = Attachment.Media(localMedia, compressIfPossible)
|
||||
val isPreviewable = when {
|
||||
MimeTypes.isImage(localMedia.mimeType) -> true
|
||||
MimeTypes.isVideo(localMedia.mimeType) -> true
|
||||
MimeTypes.isAudio(localMedia.mimeType) -> true
|
||||
else -> false
|
||||
}
|
||||
attachmentsState.value = if (isPreviewable) {
|
||||
AttachmentsState.Previewing(persistentListOf(mediaAttachment))
|
||||
} else {
|
||||
val mediaAttachment = Attachment.Media(localMedia, compressIfPossible)
|
||||
val isPreviewable = when {
|
||||
MimeTypes.isImage(localMedia.mimeType) -> true
|
||||
MimeTypes.isVideo(localMedia.mimeType) -> true
|
||||
MimeTypes.isAudio(localMedia.mimeType) -> true
|
||||
else -> false
|
||||
}
|
||||
if (isPreviewable) {
|
||||
AttachmentsState.Previewing(persistentListOf(mediaAttachment))
|
||||
} else {
|
||||
AttachmentsState.Sending(persistentListOf(mediaAttachment))
|
||||
}
|
||||
AttachmentsState.Sending(persistentListOf(mediaAttachment))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import io.element.android.features.messages.impl.MessagesEvents
|
|||
import io.element.android.features.messages.impl.MessagesPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.media.local.FakeLocalMediaFactory
|
||||
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.attachments
|
||||
|
||||
import androidx.media3.common.MimeTypes
|
||||
import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.fixtures.aLocalMedia
|
||||
import io.element.android.features.messages.impl.attachments.Attachment
|
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
|
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
|
||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class AttachmentsPreviewPresenterTest {
|
||||
|
||||
private val mediaPreProcessor = FakeMediaPreProcessor()
|
||||
|
||||
@Test
|
||||
fun `present - send media success scenario`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val presenter = anAttachmentsPreviewPresenter(room = room)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(Async.Uninitialized)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.sendActionState).isEqualTo(Async.Loading<Unit>())
|
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS)
|
||||
val successState = awaitItem()
|
||||
assertThat(successState.sendActionState).isEqualTo(Async.Success(Unit))
|
||||
assertThat(room.sendMediaCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send media failure scenario`() = runTest {
|
||||
val room = FakeMatrixRoom()
|
||||
val failure = MediaPreProcessor.Failure(null)
|
||||
room.givenSendMediaResult(Result.failure(failure))
|
||||
val presenter = anAttachmentsPreviewPresenter(room = room)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sendActionState).isEqualTo(Async.Uninitialized)
|
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.sendActionState).isEqualTo(Async.Loading<Unit>())
|
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS)
|
||||
val failureState = awaitItem()
|
||||
assertThat(failureState.sendActionState).isEqualTo(Async.Failure<Unit>(failure))
|
||||
assertThat(room.sendMediaCount).isEqualTo(0)
|
||||
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState)
|
||||
val clearedState = awaitItem()
|
||||
assertThat(clearedState.sendActionState).isEqualTo(Async.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
private fun anAttachmentsPreviewPresenter(
|
||||
localMedia: LocalMedia = aLocalMedia(mimeType = MimeTypes.IMAGE_JPEG),
|
||||
room: MatrixRoom = FakeMatrixRoom()
|
||||
): AttachmentsPreviewPresenter {
|
||||
return AttachmentsPreviewPresenter(
|
||||
attachment = Attachment.Media(localMedia, compressIfPossible = false),
|
||||
mediaSender = MediaSender(mediaPreProcessor, room)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.fixtures
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.features.messages.impl.attachments.Attachment
|
||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
||||
import io.mockk.mockk
|
||||
|
||||
fun aLocalMedia(
|
||||
uri: Uri = mockk("localMediaUri"),
|
||||
mimeType: String
|
||||
) = LocalMedia(
|
||||
uri = uri,
|
||||
mimeType = mimeType
|
||||
)
|
||||
|
||||
fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media(
|
||||
localMedia = localMedia,
|
||||
compressIfPossible = compressIfPossible,
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.media
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.features.messages.fixtures.aLocalMedia
|
||||
import io.element.android.features.messages.impl.media.local.LocalMedia
|
||||
import io.element.android.features.messages.impl.media.local.LocalMediaFactory
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
|
||||
class FakeLocalMediaFactory() : LocalMediaFactory {
|
||||
|
||||
var fallbackMimeType: String = MimeTypes.OctetStream
|
||||
|
||||
override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia {
|
||||
return aLocalMedia(mimeType = mimeType ?: fallbackMimeType)
|
||||
}
|
||||
|
||||
override fun createFromUri(uri: Uri, mimeType: String?): LocalMedia {
|
||||
return aLocalMedia(uri, mimeType ?: fallbackMimeType)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.media.viewer
|
||||
|
||||
import androidx.media3.common.MimeTypes
|
||||
import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerEvents
|
||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerNode
|
||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerPresenter
|
||||
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
|
||||
import io.element.android.libraries.matrix.test.media.aMediaSource
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
private const val TESTED_MIME_TYPE = MimeTypes.IMAGE_JPEG
|
||||
private const val TESTED_MEDIA_NAME = "MediaName"
|
||||
|
||||
class MediaViewerPresenterTest {
|
||||
|
||||
private val localMediaFactory = FakeLocalMediaFactory()
|
||||
private val mediaLoader = FakeMediaLoader()
|
||||
|
||||
@Test
|
||||
fun `present - download media success scenario`() = runTest {
|
||||
val presenter = aMediaViewerPresenter()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.downloadedMedia).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.name).isEqualTo(TESTED_MEDIA_NAME)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.downloadedMedia).isInstanceOf(Async.Loading::class.java)
|
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS)
|
||||
val successState = awaitItem()
|
||||
val successData = successState.downloadedMedia.dataOrNull()
|
||||
assertThat(successState.downloadedMedia).isInstanceOf(Async.Success::class.java)
|
||||
assertThat(successData).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - download media failure then retry with success scenario`() = runTest {
|
||||
val presenter = aMediaViewerPresenter()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
mediaLoader.shouldFail = true
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.downloadedMedia).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.name).isEqualTo(TESTED_MEDIA_NAME)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.downloadedMedia).isInstanceOf(Async.Loading::class.java)
|
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS)
|
||||
val failureState = awaitItem()
|
||||
assertThat(failureState.downloadedMedia).isInstanceOf(Async.Failure::class.java)
|
||||
mediaLoader.shouldFail = false
|
||||
failureState.eventSink(MediaViewerEvents.RetryLoading)
|
||||
//There is one recomposition because of the retry mechanism
|
||||
skipItems(1)
|
||||
val retryLoadingState = awaitItem()
|
||||
assertThat(retryLoadingState.downloadedMedia).isInstanceOf(Async.Loading::class.java)
|
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS)
|
||||
val successState = awaitItem()
|
||||
val successData = successState.downloadedMedia.dataOrNull()
|
||||
assertThat(successState.downloadedMedia).isInstanceOf(Async.Success::class.java)
|
||||
assertThat(successData).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun aMediaViewerPresenter(mimeType: String = TESTED_MIME_TYPE): MediaViewerPresenter {
|
||||
return MediaViewerPresenter(
|
||||
inputs = MediaViewerNode.Inputs(
|
||||
name = TESTED_MEDIA_NAME,
|
||||
mediaSource = aMediaSource(),
|
||||
mimeType = mimeType
|
||||
),
|
||||
localMediaFactory = localMediaFactory,
|
||||
mediaLoader = mediaLoader
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.ReceiveTurbine
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.media.local.FakeLocalMediaFactory
|
||||
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
||||
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||
|
|
@ -52,7 +52,6 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
|||
import io.element.android.libraries.textcomposer.MessageComposerMode
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
|
@ -392,7 +391,9 @@ class MessageComposerPresenterTest {
|
|||
val sendingState = awaitItem()
|
||||
assertThat(sendingState.showAttachmentSourcePicker).isFalse()
|
||||
assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending::class.java)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
val sentState = awaitItem()
|
||||
assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None)
|
||||
assertThat(room.sendMediaCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -440,7 +441,7 @@ class MessageComposerPresenterTest {
|
|||
initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles)
|
||||
val sendingState = awaitItem()
|
||||
assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending::class.java)
|
||||
val finalState= awaitItem()
|
||||
val finalState = awaitItem()
|
||||
assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.None::class.java)
|
||||
snackbarDispatcher.snackbarMessage.test {
|
||||
// Assert error message received
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@ import dagger.Provides
|
|||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
|
||||
@Module
|
||||
@ContributesTo(SessionScope::class)
|
||||
object SessionMatrixModule {
|
||||
@Provides
|
||||
@SingleIn(SessionScope::class)
|
||||
fun providesRustSessionVerificationService(matrixClient: MatrixClient): SessionVerificationService {
|
||||
fun providesSessionVerificationService(matrixClient: MatrixClient): SessionVerificationService {
|
||||
return matrixClient.sessionVerificationService()
|
||||
}
|
||||
|
||||
|
|
@ -40,4 +40,10 @@ object SessionMatrixModule {
|
|||
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
|
||||
return matrixClient.roomMembershipObserver()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SingleIn(SessionScope::class)
|
||||
fun provideMediaLoader(matrixClient: MatrixClient): MatrixMediaLoader {
|
||||
return matrixClient.mediaLoader
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import io.element.android.libraries.matrix.api.core.SessionId
|
|||
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
const val A_USER_NAME = "alice"
|
||||
const val A_PASSWORD = "password"
|
||||
|
|
@ -52,6 +54,9 @@ val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, true, null)
|
|||
const val AN_AVATAR_URL = "mxc://data"
|
||||
|
||||
const val A_FAILURE_REASON = "There has been a failure"
|
||||
|
||||
const val FAKE_DELAY_IN_MS = 100L
|
||||
|
||||
val A_THROWABLE = Throwable(A_FAILURE_REASON)
|
||||
val AN_EXCEPTION = Exception(A_FAILURE_REASON)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,17 @@
|
|||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class FakeMediaLoader : MatrixMediaLoader {
|
||||
|
||||
var shouldFail = false
|
||||
|
||||
override suspend fun loadMediaContent(source: MediaSource): Result<ByteArray> {
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
|
|
@ -33,6 +36,7 @@ class FakeMediaLoader : MatrixMediaLoader {
|
|||
}
|
||||
|
||||
override suspend fun loadMediaThumbnail(source: MediaSource, width: Long, height: Long): Result<ByteArray> {
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
|
|
@ -41,6 +45,7 @@ class FakeMediaLoader : MatrixMediaLoader {
|
|||
}
|
||||
|
||||
override suspend fun loadMediaFile(source: MediaSource, mimeType: String?): Result<MediaFile> {
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -14,17 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.media.local
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
|
||||
class FakeLocalMediaFactory() : LocalMediaFactory {
|
||||
|
||||
var fallbackMimeType: String = MimeTypes.OctetStream
|
||||
|
||||
override fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? {
|
||||
if (uri == null) return null
|
||||
return LocalMedia(uri, mimeType ?: fallbackMimeType)
|
||||
}
|
||||
}
|
||||
fun aMediaSource(url: String = "") = MediaSource(
|
||||
url = url,
|
||||
json = null
|
||||
)
|
||||
|
|
@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
|||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -94,7 +95,7 @@ class FakeMatrixRoom(
|
|||
}
|
||||
|
||||
override suspend fun sendMessage(message: String): Result<Unit> {
|
||||
delay(100)
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +104,7 @@ class FakeMatrixRoom(
|
|||
|
||||
override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> {
|
||||
editMessageParameter = message
|
||||
delay(100)
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ class FakeMatrixRoom(
|
|||
|
||||
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> {
|
||||
replyMessageParameter = message
|
||||
delay(100)
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +122,7 @@ class FakeMatrixRoom(
|
|||
|
||||
override suspend fun redactEvent(eventId: EventId, reason: String?): Result<Unit> {
|
||||
redactEventEventIdParam = eventId
|
||||
delay(100)
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
@ -136,13 +137,20 @@ class FakeMatrixRoom(
|
|||
return rejectInviteResult
|
||||
}
|
||||
|
||||
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result<Unit> = sendMediaResult.also { sendMediaCount++ }
|
||||
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result<Unit> = fakeSendMedia()
|
||||
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result<Unit> = sendMediaResult.also { sendMediaCount++ }
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result<Unit> = fakeSendMedia()
|
||||
|
||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo): Result<Unit> = sendMediaResult.also { sendMediaCount++ }
|
||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo): Result<Unit> = fakeSendMedia()
|
||||
|
||||
override suspend fun sendFile(file: File, fileInfo: FileInfo): Result<Unit> = sendMediaResult.also { sendMediaCount++ }
|
||||
override suspend fun sendFile(file: File, fileInfo: FileInfo): Result<Unit> = fakeSendMedia()
|
||||
|
||||
private suspend fun fakeSendMedia(): Result<Unit> {
|
||||
delay(FAKE_DELAY_IN_MS)
|
||||
return sendMediaResult.onSuccess {
|
||||
sendMediaCount++
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue