diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriberTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriberTest.kt index a1b582a678..4333525fe0 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriberTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/TimelineItemsSubscriberTest.kt @@ -16,7 +16,6 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineD 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 @@ -25,6 +24,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.rustcomponents.sdk.Timeline import org.matrix.rustcomponents.sdk.TimelineChange @@ -33,115 +33,107 @@ 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> = - 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() - } + fun `when timeline emits an empty list of items, the flow must emits an empty list`() = runTest { + val timelineItems: MutableSharedFlow> = + MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE) + val timeline = FakeRustTimeline() + val timelineItemsSubscriber = createTimelineItemsSubscriber( + coroutineScope = backgroundScope, + timeline = timeline, + timelineItems = timelineItems, + ) + timelineItems.test { + timelineItemsSubscriber.subscribeIfNeeded() + // Wait for the listener to be set. + 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> = - 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() - } + fun `when timeline emits a non empty list of items, the flow must emits a non empty list`() = runTest { + val timelineItems: MutableSharedFlow> = + MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE) + val timeline = FakeRustTimeline() + val timelineItemsSubscriber = createTimelineItemsSubscriber( + coroutineScope = backgroundScope, + timeline = timeline, + timelineItems = timelineItems, + ) + timelineItems.test { + timelineItemsSubscriber.subscribeIfNeeded() + // Wait for the listener to be set. + 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> = - MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE) - val timeline = FakeRustTimeline() - val onNewSyncedEventRecorder = lambdaRecorder { } - 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, - ) + fun `when timeline emits an item with SYNC origin, the callback onNewSyncedEvent is invoked`() = runTest { + val timelineItems: MutableSharedFlow> = + MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE) + val timeline = FakeRustTimeline() + val onNewSyncedEventRecorder = lambdaRecorder { } + val timelineItemsSubscriber = createTimelineItemsSubscriber( + coroutineScope = backgroundScope, + timeline = timeline, + timelineItems = timelineItems, + onNewSyncedEvent = onNewSyncedEventRecorder, + ) + timelineItems.test { + timelineItemsSubscriber.subscribeIfNeeded() + // Wait for the listener to be set. + 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() + ) + 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> = MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE), - initLatch: CompletableDeferred = CompletableDeferred(), - isTimelineInitialized: MutableStateFlow = MutableStateFlow(false), - onNewSyncedEvent: () -> Unit = { lambdaError() }, - ): TimelineItemsSubscriber { - return TimelineItemsSubscriber( - timelineCoroutineScope = coroutineScope, - dispatcher = StandardTestDispatcher(testScheduler), - timeline = timeline, - timelineDiffProcessor = createMatrixTimelineDiffProcessor(timelineItems), - initLatch = initLatch, - isTimelineInitialized = isTimelineInitialized, - onNewSyncedEvent = onNewSyncedEvent, + fun `multiple subscriptions does not have side effect`() = runTest { + val timelineItemsSubscriber = createTimelineItemsSubscriber( + coroutineScope = backgroundScope, ) + timelineItemsSubscriber.subscribeIfNeeded() + timelineItemsSubscriber.subscribeIfNeeded() + timelineItemsSubscriber.unsubscribeIfNeeded() + timelineItemsSubscriber.unsubscribeIfNeeded() } } + +private fun TestScope.createTimelineItemsSubscriber( + coroutineScope: CoroutineScope, + timeline: Timeline = FakeRustTimeline(), + timelineItems: MutableSharedFlow> = MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE), + initLatch: CompletableDeferred = CompletableDeferred(), + isTimelineInitialized: MutableStateFlow = MutableStateFlow(false), + onNewSyncedEvent: () -> Unit = { lambdaError() }, +): TimelineItemsSubscriber { + return TimelineItemsSubscriber( + timelineCoroutineScope = coroutineScope, + dispatcher = StandardTestDispatcher(testScheduler), + timeline = timeline, + timelineDiffProcessor = createMatrixTimelineDiffProcessor(timelineItems), + initLatch = initLatch, + isTimelineInitialized = isTimelineInitialized, + onNewSyncedEvent = onNewSyncedEvent, + ) +}