Add test on TimelineItemsSubscriber.
This commit is contained in:
parent
17f706b8a7
commit
9f44cff403
7 changed files with 254 additions and 3 deletions
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.fixtures.factories
|
||||||
|
|
||||||
|
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo
|
||||||
|
|
||||||
|
fun anEventTimelineItemDebugInfo(
|
||||||
|
model: String = "model",
|
||||||
|
originalJson: String? = null,
|
||||||
|
latestEditJson: String? = null,
|
||||||
|
) = EventTimelineItemDebugInfo(
|
||||||
|
model = model,
|
||||||
|
originalJson = originalJson,
|
||||||
|
latestEditJson = latestEditJson
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.impl.fixtures.factories.anEventTimelineItemDebugInfo
|
||||||
|
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
|
import org.matrix.rustcomponents.sdk.EventSendState
|
||||||
|
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||||
|
import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo
|
||||||
|
import org.matrix.rustcomponents.sdk.NoPointer
|
||||||
|
import org.matrix.rustcomponents.sdk.ProfileDetails
|
||||||
|
import org.matrix.rustcomponents.sdk.Reaction
|
||||||
|
import org.matrix.rustcomponents.sdk.Receipt
|
||||||
|
import org.matrix.rustcomponents.sdk.ShieldState
|
||||||
|
import org.matrix.rustcomponents.sdk.TimelineItemContent
|
||||||
|
import uniffi.matrix_sdk_ui.EventItemOrigin
|
||||||
|
|
||||||
|
class FakeRustEventTimelineItem(
|
||||||
|
private val origin: EventItemOrigin? = null,
|
||||||
|
) : EventTimelineItem(NoPointer) {
|
||||||
|
override fun origin(): EventItemOrigin? = origin
|
||||||
|
override fun eventId(): String = AN_EVENT_ID.value
|
||||||
|
override fun transactionId(): String? = null
|
||||||
|
override fun isEditable(): Boolean = false
|
||||||
|
override fun canBeRepliedTo(): Boolean = false
|
||||||
|
override fun isLocal(): Boolean = false
|
||||||
|
override fun isOwn(): Boolean = false
|
||||||
|
override fun isRemote(): Boolean = false
|
||||||
|
override fun localSendState(): EventSendState? = null
|
||||||
|
override fun reactions(): List<Reaction> = emptyList()
|
||||||
|
override fun readReceipts(): Map<String, Receipt> = emptyMap()
|
||||||
|
override fun sender(): String = A_USER_ID.value
|
||||||
|
override fun senderProfile(): ProfileDetails = ProfileDetails.Unavailable
|
||||||
|
override fun timestamp(): ULong = 0u
|
||||||
|
override fun content(): TimelineItemContent = FakeRustTimelineItemContent()
|
||||||
|
override fun debugInfo(): EventTimelineItemDebugInfo = anEventTimelineItemDebugInfo()
|
||||||
|
override fun getShield(strict: Boolean): ShieldState? = null
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||||
|
|
||||||
|
import org.matrix.rustcomponents.sdk.NoPointer
|
||||||
|
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||||
|
import org.matrix.rustcomponents.sdk.Timeline
|
||||||
|
import org.matrix.rustcomponents.sdk.TimelineDiff
|
||||||
|
import org.matrix.rustcomponents.sdk.TimelineListener
|
||||||
|
|
||||||
|
class FakeRustTimeline(
|
||||||
|
) : Timeline(NoPointer) {
|
||||||
|
private var listener: TimelineListener? = null
|
||||||
|
override suspend fun addListener(listener: TimelineListener): TaskHandle {
|
||||||
|
this.listener = listener
|
||||||
|
return FakeRustTaskHandle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emitDiff(diff: List<TimelineDiff>) {
|
||||||
|
listener!!.onUpdate(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,8 +12,10 @@ import org.matrix.rustcomponents.sdk.NoPointer
|
||||||
import org.matrix.rustcomponents.sdk.TimelineItem
|
import org.matrix.rustcomponents.sdk.TimelineItem
|
||||||
import org.matrix.rustcomponents.sdk.VirtualTimelineItem
|
import org.matrix.rustcomponents.sdk.VirtualTimelineItem
|
||||||
|
|
||||||
class FakeRustTimelineItem : TimelineItem(NoPointer) {
|
class FakeRustTimelineItem(
|
||||||
override fun asEvent(): EventTimelineItem? = null
|
private val asEventResult: EventTimelineItem? = null,
|
||||||
|
) : TimelineItem(NoPointer) {
|
||||||
|
override fun asEvent(): EventTimelineItem? = asEventResult
|
||||||
override fun asVirtual(): VirtualTimelineItem? = null
|
override fun asVirtual(): VirtualTimelineItem? = null
|
||||||
override fun fmtDebug(): String = "fmtDebug"
|
override fun fmtDebug(): String = "fmtDebug"
|
||||||
override fun uniqueId(): String = "uniqueId"
|
override fun uniqueId(): String = "uniqueId"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||||
|
|
||||||
|
import org.matrix.rustcomponents.sdk.Message
|
||||||
|
import org.matrix.rustcomponents.sdk.NoPointer
|
||||||
|
import org.matrix.rustcomponents.sdk.TimelineItemContent
|
||||||
|
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
|
||||||
|
|
||||||
|
class FakeRustTimelineItemContent : TimelineItemContent(NoPointer) {
|
||||||
|
override fun asMessage(): Message? = null
|
||||||
|
override fun kind(): TimelineItemContentKind = TimelineItemContentKind.Message
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTim
|
||||||
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
||||||
import io.element.android.libraries.matrix.test.A_UNIQUE_ID_2
|
import io.element.android.libraries.matrix.test.A_UNIQUE_ID_2
|
||||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -155,7 +156,7 @@ class MatrixTimelineDiffProcessorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun TestScope.createMatrixTimelineDiffProcessor(
|
internal fun TestScope.createMatrixTimelineDiffProcessor(
|
||||||
timelineItems: MutableStateFlow<List<MatrixTimelineItem>>,
|
timelineItems: MutableSharedFlow<List<MatrixTimelineItem>>,
|
||||||
): MatrixTimelineDiffProcessor {
|
): MatrixTimelineDiffProcessor {
|
||||||
val timelineEventContentMapper = TimelineEventContentMapper()
|
val timelineEventContentMapper = TimelineEventContentMapper()
|
||||||
val timelineItemMapper = MatrixTimelineItemMapper(
|
val timelineItemMapper = MatrixTimelineItemMapper(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.timeline
|
||||||
|
|
||||||
|
import app.cash.turbine.test
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustEventTimelineItem
|
||||||
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimeline
|
||||||
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineDiff
|
||||||
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineItem
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
import io.element.android.tests.testutils.runCancellableScopeTestWithTestScope
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runCurrent
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.rustcomponents.sdk.Timeline
|
||||||
|
import org.matrix.rustcomponents.sdk.TimelineChange
|
||||||
|
import uniffi.matrix_sdk_ui.EventItemOrigin
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class TimelineItemsSubscriberTest {
|
||||||
|
@Test
|
||||||
|
fun `when timeline emits an empty list of items, the flow must emits an empty list`() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||||
|
val timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> =
|
||||||
|
MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE)
|
||||||
|
val timeline = FakeRustTimeline()
|
||||||
|
val timelineItemsSubscriber = testScope.createTimelineItemsSubscriber(
|
||||||
|
coroutineScope = cancellableScope,
|
||||||
|
timeline = timeline,
|
||||||
|
timelineItems = timelineItems,
|
||||||
|
)
|
||||||
|
timelineItems.test {
|
||||||
|
timelineItemsSubscriber.subscribeIfNeeded()
|
||||||
|
// Wait for the listener to be set.
|
||||||
|
testScope.runCurrent()
|
||||||
|
timeline.emitDiff(listOf(FakeRustTimelineDiff(item = null, change = TimelineChange.RESET)))
|
||||||
|
val final = awaitItem()
|
||||||
|
assertThat(final).isEmpty()
|
||||||
|
timelineItemsSubscriber.unsubscribeIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when timeline emits a non empty list of items, the flow must emits a non empty list`() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||||
|
val timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> =
|
||||||
|
MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE)
|
||||||
|
val timeline = FakeRustTimeline()
|
||||||
|
val timelineItemsSubscriber = testScope.createTimelineItemsSubscriber(
|
||||||
|
coroutineScope = cancellableScope,
|
||||||
|
timeline = timeline,
|
||||||
|
timelineItems = timelineItems,
|
||||||
|
)
|
||||||
|
timelineItems.test {
|
||||||
|
timelineItemsSubscriber.subscribeIfNeeded()
|
||||||
|
// Wait for the listener to be set.
|
||||||
|
testScope.runCurrent()
|
||||||
|
timeline.emitDiff(listOf(FakeRustTimelineDiff(item = FakeRustTimelineItem(), change = TimelineChange.RESET)))
|
||||||
|
val final = awaitItem()
|
||||||
|
assertThat(final).isNotEmpty()
|
||||||
|
timelineItemsSubscriber.unsubscribeIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when timeline emits an item with SYNC origin, the callback onNewSyncedEvent is invoked`() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||||
|
val timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> =
|
||||||
|
MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE)
|
||||||
|
val timeline = FakeRustTimeline()
|
||||||
|
val onNewSyncedEventRecorder = lambdaRecorder<Unit> { }
|
||||||
|
val timelineItemsSubscriber = testScope.createTimelineItemsSubscriber(
|
||||||
|
coroutineScope = cancellableScope,
|
||||||
|
timeline = timeline,
|
||||||
|
timelineItems = timelineItems,
|
||||||
|
onNewSyncedEvent = onNewSyncedEventRecorder,
|
||||||
|
)
|
||||||
|
timelineItems.test {
|
||||||
|
timelineItemsSubscriber.subscribeIfNeeded()
|
||||||
|
// Wait for the listener to be set.
|
||||||
|
testScope.runCurrent()
|
||||||
|
timeline.emitDiff(
|
||||||
|
listOf(
|
||||||
|
FakeRustTimelineDiff(
|
||||||
|
item = FakeRustTimelineItem(
|
||||||
|
asEventResult = FakeRustEventTimelineItem(origin = EventItemOrigin.SYNC)
|
||||||
|
),
|
||||||
|
change = TimelineChange.RESET,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val final = awaitItem()
|
||||||
|
assertThat(final).isNotEmpty()
|
||||||
|
timelineItemsSubscriber.unsubscribeIfNeeded()
|
||||||
|
}
|
||||||
|
onNewSyncedEventRecorder.assertions().isCalledOnce()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `multiple subscriptions does not have side effect`() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||||
|
val timelineItemsSubscriber = testScope.createTimelineItemsSubscriber(
|
||||||
|
coroutineScope = cancellableScope,
|
||||||
|
)
|
||||||
|
timelineItemsSubscriber.subscribeIfNeeded()
|
||||||
|
timelineItemsSubscriber.subscribeIfNeeded()
|
||||||
|
timelineItemsSubscriber.unsubscribeIfNeeded()
|
||||||
|
timelineItemsSubscriber.unsubscribeIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createTimelineItemsSubscriber(
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
timeline: Timeline = FakeRustTimeline(),
|
||||||
|
timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> = MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE),
|
||||||
|
initLatch: CompletableDeferred<Unit> = CompletableDeferred(),
|
||||||
|
isTimelineInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false),
|
||||||
|
onNewSyncedEvent: () -> Unit = { lambdaError() },
|
||||||
|
): TimelineItemsSubscriber {
|
||||||
|
return TimelineItemsSubscriber(
|
||||||
|
timelineCoroutineScope = coroutineScope,
|
||||||
|
dispatcher = StandardTestDispatcher(testScheduler),
|
||||||
|
timeline = timeline,
|
||||||
|
timelineDiffProcessor = createMatrixTimelineDiffProcessor(timelineItems),
|
||||||
|
initLatch = initLatch,
|
||||||
|
isTimelineInitialized = isTimelineInitialized,
|
||||||
|
onNewSyncedEvent = onNewSyncedEvent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue