Browse Source

Media: add more tests

feature/jme/open-room-member-details-when-clicking-on-user-data
ganfra 1 year ago
parent
commit
ced60c672e
  1. 11
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt
  2. 10
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt
  3. 11
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt
  4. 10
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
  5. 2
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
  6. 95
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
  7. 36
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt
  8. 37
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt
  9. 102
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
  10. 7
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
  11. 12
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt
  12. 5
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt
  13. 7
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt
  14. 18
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/MediaSource.kt
  15. 24
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

11
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/AndroidLocalMediaFactory.kt

@ -18,10 +18,12 @@ package io.element.android.features.messages.impl.media.local
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.media.MediaFile
import javax.inject.Inject import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
@ -29,8 +31,13 @@ class AndroidLocalMediaFactory @Inject constructor(
@ApplicationContext private val context: Context @ApplicationContext private val context: Context
) : LocalMediaFactory { ) : LocalMediaFactory {
override fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? { override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia {
if (uri == null) return null 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 val resolvedMimeType = mimeType ?: context.contentResolver.getType(uri) ?: MimeTypes.OctetStream
return LocalMedia(uri, resolvedMimeType) return LocalMedia(uri, resolvedMimeType)
} }

10
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaFactory.kt

@ -17,12 +17,20 @@
package io.element.android.features.messages.impl.media.local package io.element.android.features.messages.impl.media.local
import android.net.Uri import android.net.Uri
import io.element.android.libraries.matrix.api.media.MediaFile
interface LocalMediaFactory { 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] * 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. * 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
} }

11
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt

@ -24,7 +24,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.net.toUri
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject 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.features.messages.impl.media.local.LocalMediaFactory
import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter 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 io.element.android.libraries.matrix.api.media.MediaFile
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -40,7 +39,7 @@ import kotlinx.coroutines.launch
class MediaViewerPresenter @AssistedInject constructor( class MediaViewerPresenter @AssistedInject constructor(
@Assisted private val inputs: MediaViewerNode.Inputs, @Assisted private val inputs: MediaViewerNode.Inputs,
private val localMediaFactory: LocalMediaFactory, private val localMediaFactory: LocalMediaFactory,
private val client: MatrixClient, private val mediaLoader: MatrixMediaLoader,
) : Presenter<MediaViewerState> { ) : Presenter<MediaViewerState> {
@AssistedFactory @AssistedFactory
@ -79,14 +78,12 @@ class MediaViewerPresenter @AssistedInject constructor(
} }
private fun CoroutineScope.loadMedia(mediaFile: MutableState<MediaFile?>, localMedia: MutableState<Async<LocalMedia>>) = launch { private fun CoroutineScope.loadMedia(mediaFile: MutableState<MediaFile?>, localMedia: MutableState<Async<LocalMedia>>) = launch {
mediaFile.value = null
localMedia.value = Async.Loading() localMedia.value = Async.Loading()
client.mediaLoader.loadMediaFile(inputs.mediaSource, inputs.mimeType) mediaLoader.loadMediaFile(inputs.mediaSource, inputs.mimeType)
.onSuccess { .onSuccess {
mediaFile.value = it mediaFile.value = it
}.mapCatching { }.mapCatching {
val uri = it.path().toUri() localMediaFactory.createFromMediaFile(it, inputs.mimeType)
localMediaFactory.createFromUri(uri, inputs.mimeType)!!
}.onSuccess { }.onSuccess {
localMedia.value = Async.Success(it) localMedia.value = Async.Success(it)
}.onFailure { }.onFailure {

10
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt

@ -206,10 +206,11 @@ class MessageComposerPresenter @Inject constructor(
mimeType: String? = null, mimeType: String? = null,
compressIfPossible: Boolean = true, compressIfPossible: Boolean = true,
) { ) {
if (uri == null) {
attachmentsState.value = AttachmentsState.None
return
}
val localMedia = localMediaFactory.createFromUri(uri, mimeType) val localMedia = localMediaFactory.createFromUri(uri, mimeType)
attachmentsState.value = if (localMedia == null) {
AttachmentsState.None
} else {
val mediaAttachment = Attachment.Media(localMedia, compressIfPossible) val mediaAttachment = Attachment.Media(localMedia, compressIfPossible)
val isPreviewable = when { val isPreviewable = when {
MimeTypes.isImage(localMedia.mimeType) -> true MimeTypes.isImage(localMedia.mimeType) -> true
@ -217,13 +218,12 @@ class MessageComposerPresenter @Inject constructor(
MimeTypes.isAudio(localMedia.mimeType) -> true MimeTypes.isAudio(localMedia.mimeType) -> true
else -> false else -> false
} }
if (isPreviewable) { attachmentsState.value = if (isPreviewable) {
AttachmentsState.Previewing(persistentListOf(mediaAttachment)) AttachmentsState.Previewing(persistentListOf(mediaAttachment))
} else { } else {
AttachmentsState.Sending(persistentListOf(mediaAttachment)) AttachmentsState.Sending(persistentListOf(mediaAttachment))
} }
} }
}
private suspend fun sendMedia( private suspend fun sendMedia(
uri: Uri, uri: Uri,

2
features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt

@ -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.MessagesPresenter
import io.element.android.features.messages.impl.actionlist.ActionListPresenter 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.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.messagecomposer.MessageComposerPresenter
import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.TimelinePresenter
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.features.networkmonitor.test.FakeNetworkMonitor

95
features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt

@ -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)
)
}
}

36
features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt

@ -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,
)

37
features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt

@ -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)
}
}

102
features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt

@ -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
)
}
}

7
features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt

@ -21,7 +21,7 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.ReceiveTurbine import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat 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.AttachmentsState
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter 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.element.android.libraries.textcomposer.MessageComposerMode
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
@ -392,7 +391,9 @@ class MessageComposerPresenterTest {
val sendingState = awaitItem() val sendingState = awaitItem()
assertThat(sendingState.showAttachmentSourcePicker).isFalse() assertThat(sendingState.showAttachmentSourcePicker).isFalse()
assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending::class.java) assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending::class.java)
cancelAndIgnoreRemainingEvents() val sentState = awaitItem()
assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None)
assertThat(room.sendMediaCount).isEqualTo(1)
} }
} }

12
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt

@ -22,16 +22,16 @@ import dagger.Provides
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@Module @Module
@ContributesTo(SessionScope::class) @ContributesTo(SessionScope::class)
object SessionMatrixModule { object SessionMatrixModule {
@Provides @Provides
@SingleIn(SessionScope::class) @SingleIn(SessionScope::class)
fun providesRustSessionVerificationService(matrixClient: MatrixClient): SessionVerificationService { fun providesSessionVerificationService(matrixClient: MatrixClient): SessionVerificationService {
return matrixClient.sessionVerificationService() return matrixClient.sessionVerificationService()
} }
@ -40,4 +40,10 @@ object SessionMatrixModule {
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver { fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
return matrixClient.roomMembershipObserver() return matrixClient.roomMembershipObserver()
} }
@Provides
@SingleIn(SessionScope::class)
fun provideMediaLoader(matrixClient: MatrixClient): MatrixMediaLoader {
return matrixClient.mediaLoader
}
} }

5
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt

@ -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.SpaceId
import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.UserId 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_USER_NAME = "alice"
const val A_PASSWORD = "password" 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 AN_AVATAR_URL = "mxc://data"
const val A_FAILURE_REASON = "There has been a failure" 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 A_THROWABLE = Throwable(A_FAILURE_REASON)
val AN_EXCEPTION = Exception(A_FAILURE_REASON) val AN_EXCEPTION = Exception(A_FAILURE_REASON)

7
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/FakeMediaLoader.kt

@ -17,14 +17,17 @@
package io.element.android.libraries.matrix.test.media 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.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.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 { class FakeMediaLoader : MatrixMediaLoader {
var shouldFail = false var shouldFail = false
override suspend fun loadMediaContent(source: MediaSource): Result<ByteArray> { override suspend fun loadMediaContent(source: MediaSource): Result<ByteArray> {
delay(FAKE_DELAY_IN_MS)
return if (shouldFail) { return if (shouldFail) {
Result.failure(RuntimeException()) Result.failure(RuntimeException())
} else { } else {
@ -33,6 +36,7 @@ class FakeMediaLoader : MatrixMediaLoader {
} }
override suspend fun loadMediaThumbnail(source: MediaSource, width: Long, height: Long): Result<ByteArray> { override suspend fun loadMediaThumbnail(source: MediaSource, width: Long, height: Long): Result<ByteArray> {
delay(FAKE_DELAY_IN_MS)
return if (shouldFail) { return if (shouldFail) {
Result.failure(RuntimeException()) Result.failure(RuntimeException())
} else { } else {
@ -41,6 +45,7 @@ class FakeMediaLoader : MatrixMediaLoader {
} }
override suspend fun loadMediaFile(source: MediaSource, mimeType: String?): Result<MediaFile> { override suspend fun loadMediaFile(source: MediaSource, mimeType: String?): Result<MediaFile> {
delay(FAKE_DELAY_IN_MS)
return if (shouldFail) { return if (shouldFail) {
Result.failure(RuntimeException()) Result.failure(RuntimeException())
} else { } else {

18
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/FakeLocalMediaFactory.kt → libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/media/MediaSource.kt

@ -14,17 +14,11 @@
* limitations under the License. * 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.matrix.api.media.MediaSource
import io.element.android.libraries.core.mimetype.MimeTypes
class FakeLocalMediaFactory() : LocalMediaFactory { fun aMediaSource(url: String = "") = MediaSource(
url = url,
var fallbackMimeType: String = MimeTypes.OctetStream json = null
)
override fun createFromUri(uri: Uri?, mimeType: String?): LocalMedia? {
if (uri == null) return null
return LocalMedia(uri, mimeType ?: fallbackMimeType)
}
}

24
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -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.api.timeline.MatrixTimeline
import io.element.android.libraries.matrix.test.A_ROOM_ID 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.A_SESSION_ID
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -94,7 +95,7 @@ class FakeMatrixRoom(
} }
override suspend fun sendMessage(message: String): Result<Unit> { override suspend fun sendMessage(message: String): Result<Unit> {
delay(100) delay(FAKE_DELAY_IN_MS)
return Result.success(Unit) return Result.success(Unit)
} }
@ -103,7 +104,7 @@ class FakeMatrixRoom(
override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> { override suspend fun editMessage(originalEventId: EventId, message: String): Result<Unit> {
editMessageParameter = message editMessageParameter = message
delay(100) delay(FAKE_DELAY_IN_MS)
return Result.success(Unit) return Result.success(Unit)
} }
@ -112,7 +113,7 @@ class FakeMatrixRoom(
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> { override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> {
replyMessageParameter = message replyMessageParameter = message
delay(100) delay(FAKE_DELAY_IN_MS)
return Result.success(Unit) return Result.success(Unit)
} }
@ -121,7 +122,7 @@ class FakeMatrixRoom(
override suspend fun redactEvent(eventId: EventId, reason: String?): Result<Unit> { override suspend fun redactEvent(eventId: EventId, reason: String?): Result<Unit> {
redactEventEventIdParam = eventId redactEventEventIdParam = eventId
delay(100) delay(FAKE_DELAY_IN_MS)
return Result.success(Unit) return Result.success(Unit)
} }
@ -136,13 +137,20 @@ class FakeMatrixRoom(
return rejectInviteResult 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 override fun close() = Unit

Loading…
Cancel
Save