|
|
|
@ -16,7 +16,6 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineD
@@ -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
@@ -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
@@ -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<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() |
|
|
|
|
} |
|
|
|
|
fun `when timeline emits an empty list of items, the flow must emits an empty list`() = runTest { |
|
|
|
|
val timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> = |
|
|
|
|
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<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() |
|
|
|
|
} |
|
|
|
|
fun `when timeline emits a non empty list of items, the flow must emits a non empty list`() = runTest { |
|
|
|
|
val timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> = |
|
|
|
|
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<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, |
|
|
|
|
) |
|
|
|
|
fun `when timeline emits an item with SYNC origin, the callback onNewSyncedEvent is invoked`() = runTest { |
|
|
|
|
val timelineItems: MutableSharedFlow<List<MatrixTimelineItem>> = |
|
|
|
|
MutableSharedFlow(replay = 1, extraBufferCapacity = Int.MAX_VALUE) |
|
|
|
|
val timeline = FakeRustTimeline() |
|
|
|
|
val onNewSyncedEventRecorder = lambdaRecorder<Unit> { } |
|
|
|
|
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() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
fun `multiple subscriptions does not have side effect`() { |
|
|
|
|
runCancellableScopeTestWithTestScope { testScope, cancellableScope -> |
|
|
|
|
val timelineItemsSubscriber = testScope.createTimelineItemsSubscriber( |
|
|
|
|
coroutineScope = cancellableScope, |
|
|
|
|
) |
|
|
|
|
timelineItemsSubscriber.subscribeIfNeeded() |
|
|
|
|
timelineItemsSubscriber.subscribeIfNeeded() |
|
|
|
|
timelineItemsSubscriber.unsubscribeIfNeeded() |
|
|
|
|
val final = awaitItem() |
|
|
|
|
assertThat(final).isNotEmpty() |
|
|
|
|
timelineItemsSubscriber.unsubscribeIfNeeded() |
|
|
|
|
} |
|
|
|
|
onNewSyncedEventRecorder.assertions().isCalledOnce() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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, |
|
|
|
|
@Test |
|
|
|
|
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<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, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|