Add more tests, particularly to the room list diffing (#1508)
* Add more tests to improve the covered area, particularly the room list diffing
This commit is contained in:
parent
fe4e3ba1da
commit
1bfe7b7224
5 changed files with 376 additions and 21 deletions
|
|
@ -20,18 +20,19 @@ import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||||
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
||||||
|
|
||||||
fun Throwable.mapAuthenticationException(): AuthenticationException {
|
fun Throwable.mapAuthenticationException(): AuthenticationException {
|
||||||
|
val message = this.message ?: "Unknown error"
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(this.message!!)
|
is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(message)
|
||||||
is RustAuthenticationException.Generic -> AuthenticationException.Generic(this.message!!)
|
is RustAuthenticationException.Generic -> AuthenticationException.Generic(message)
|
||||||
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(this.message!!)
|
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
|
||||||
is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(this.message!!)
|
is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(message)
|
||||||
is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(this.message!!)
|
is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(message)
|
||||||
is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message!!)
|
is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message)
|
||||||
is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message!!)
|
is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message)
|
||||||
is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message!!)
|
is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message)
|
||||||
is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message!!)
|
is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message)
|
||||||
is RustAuthenticationException.OidcCancelled -> AuthenticationException.OidcError("OidcCancelled", message!!)
|
is RustAuthenticationException.OidcCancelled -> AuthenticationException.OidcError("OidcCancelled", message)
|
||||||
is RustAuthenticationException.OidcCallbackUrlInvalid -> AuthenticationException.OidcError("OidcCallbackUrlInvalid", message!!)
|
is RustAuthenticationException.OidcCallbackUrlInvalid -> AuthenticationException.OidcError("OidcCallbackUrlInvalid", message)
|
||||||
else -> AuthenticationException.Generic(this.message ?: "Unknown error")
|
else -> AuthenticationException.Generic(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,14 @@ import kotlinx.coroutines.channels.trySendBlocking
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.buffer
|
import kotlinx.coroutines.flow.buffer
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import org.matrix.rustcomponents.sdk.RoomList
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
|
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
import org.matrix.rustcomponents.sdk.RoomListEntry
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListInterface
|
||||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||||
import org.matrix.rustcomponents.sdk.RoomListLoadingStateListener
|
import org.matrix.rustcomponents.sdk.RoomListLoadingStateListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListService
|
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||||
|
|
@ -40,7 +40,7 @@ import timber.log.Timber
|
||||||
private const val SYNC_INDICATOR_DELAY_BEFORE_SHOWING = 1000u
|
private const val SYNC_INDICATOR_DELAY_BEFORE_SHOWING = 1000u
|
||||||
private const val SYNC_INDICATOR_DELAY_BEFORE_HIDING = 0u
|
private const val SYNC_INDICATOR_DELAY_BEFORE_HIDING = 0u
|
||||||
|
|
||||||
fun RoomList.loadingStateFlow(): Flow<RoomListLoadingState> =
|
fun RoomListInterface.loadingStateFlow(): Flow<RoomListLoadingState> =
|
||||||
mxCallbackFlow {
|
mxCallbackFlow {
|
||||||
val listener = object : RoomListLoadingStateListener {
|
val listener = object : RoomListLoadingStateListener {
|
||||||
override fun onUpdate(state: RoomListLoadingState) {
|
override fun onUpdate(state: RoomListLoadingState) {
|
||||||
|
|
@ -58,7 +58,7 @@ fun RoomList.loadingStateFlow(): Flow<RoomListLoadingState> =
|
||||||
Timber.d(it, "loadingStateFlow() failed")
|
Timber.d(it, "loadingStateFlow() failed")
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomList.entriesFlow(onInitialList: suspend (List<RoomListEntry>) -> Unit): Flow<List<RoomListEntriesUpdate>> =
|
fun RoomListInterface.entriesFlow(onInitialList: suspend (List<RoomListEntry>) -> Unit): Flow<List<RoomListEntriesUpdate>> =
|
||||||
mxCallbackFlow {
|
mxCallbackFlow {
|
||||||
val listener = object : RoomListEntriesListener {
|
val listener = object : RoomListEntriesListener {
|
||||||
override fun onUpdate(roomEntriesUpdate: List<RoomListEntriesUpdate>) {
|
override fun onUpdate(roomEntriesUpdate: List<RoomListEntriesUpdate>) {
|
||||||
|
|
@ -76,7 +76,7 @@ fun RoomList.entriesFlow(onInitialList: suspend (List<RoomListEntry>) -> Unit):
|
||||||
Timber.d(it, "entriesFlow() failed")
|
Timber.d(it, "entriesFlow() failed")
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListService.stateFlow(): Flow<RoomListServiceState> =
|
fun RoomListServiceInterface.stateFlow(): Flow<RoomListServiceState> =
|
||||||
mxCallbackFlow {
|
mxCallbackFlow {
|
||||||
val listener = object : RoomListServiceStateListener {
|
val listener = object : RoomListServiceStateListener {
|
||||||
override fun onUpdate(state: RoomListServiceState) {
|
override fun onUpdate(state: RoomListServiceState) {
|
||||||
|
|
@ -88,7 +88,7 @@ fun RoomListService.stateFlow(): Flow<RoomListServiceState> =
|
||||||
}
|
}
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListService.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
|
fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
|
||||||
mxCallbackFlow {
|
mxCallbackFlow {
|
||||||
val listener = object : RoomListServiceSyncIndicatorListener {
|
val listener = object : RoomListServiceSyncIndicatorListener {
|
||||||
override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) {
|
override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) {
|
||||||
|
|
@ -104,7 +104,7 @@ fun RoomListService.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
|
||||||
}
|
}
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListService.roomOrNull(roomId: String): RoomListItem? {
|
fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {
|
||||||
return try {
|
return try {
|
||||||
room(roomId)
|
room(roomId)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
import org.matrix.rustcomponents.sdk.RoomListEntry
|
||||||
import org.matrix.rustcomponents.sdk.RoomListService
|
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class RoomSummaryListProcessor(
|
class RoomSummaryListProcessor(
|
||||||
private val roomSummaries: MutableStateFlow<List<RoomSummary>>,
|
private val roomSummaries: MutableStateFlow<List<RoomSummary>>,
|
||||||
private val roomListService: RoomListService,
|
private val roomListService: RoomListServiceInterface,
|
||||||
private val dispatcher: CoroutineDispatcher,
|
private val dispatcher: CoroutineDispatcher,
|
||||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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.libraries.matrix.impl.auth
|
||||||
|
|
||||||
|
import com.google.common.truth.ThrowableSubject
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
||||||
|
|
||||||
|
class AuthenticationExceptionMappingTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `mapping an exception with no message returns 'Unknown error' message`() {
|
||||||
|
val exception = Exception()
|
||||||
|
val mappedException = exception.mapAuthenticationException()
|
||||||
|
assertThat(mappedException.message).isEqualTo("Unknown error")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `mapping a generic exception returns a Generic AuthenticationException`() {
|
||||||
|
val exception = Exception("Generic exception")
|
||||||
|
val mappedException = exception.mapAuthenticationException()
|
||||||
|
assertThat(mappedException).isException<AuthenticationException.Generic>("Generic exception")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `mapping specific exceptions map to their kotlin counterparts`() {
|
||||||
|
assertThat(RustAuthenticationException.ClientMissing("Client missing").mapAuthenticationException())
|
||||||
|
.isException<AuthenticationException.ClientMissing>("Client missing")
|
||||||
|
|
||||||
|
assertThat(RustAuthenticationException.Generic("Generic").mapAuthenticationException()).isException<AuthenticationException.Generic>("Generic")
|
||||||
|
|
||||||
|
assertThat(RustAuthenticationException.InvalidServerName("Invalid server name").mapAuthenticationException())
|
||||||
|
.isException<AuthenticationException.InvalidServerName>("Invalid server name")
|
||||||
|
|
||||||
|
assertThat(RustAuthenticationException.SessionMissing("Session missing").mapAuthenticationException())
|
||||||
|
.isException<AuthenticationException.SessionMissing>("Session missing")
|
||||||
|
|
||||||
|
assertThat(RustAuthenticationException.SlidingSyncNotAvailable("Sliding sync not available").mapAuthenticationException())
|
||||||
|
.isException<AuthenticationException.SlidingSyncNotAvailable>("Sliding sync not available")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `mapping Oidc related exceptions creates an 'OidcError' with different types`() {
|
||||||
|
assertIsOidcError(
|
||||||
|
throwable = RustAuthenticationException.OidcException("Oidc exception"),
|
||||||
|
type = "OidcException",
|
||||||
|
message = "Oidc exception"
|
||||||
|
)
|
||||||
|
assertIsOidcError(
|
||||||
|
throwable = RustAuthenticationException.OidcMetadataInvalid("Oidc metadata invalid"),
|
||||||
|
type = "OidcMetadataInvalid",
|
||||||
|
message = "Oidc metadata invalid"
|
||||||
|
)
|
||||||
|
assertIsOidcError(
|
||||||
|
throwable = RustAuthenticationException.OidcMetadataMissing("Oidc metadata missing"),
|
||||||
|
type = "OidcMetadataMissing",
|
||||||
|
message = "Oidc metadata missing"
|
||||||
|
)
|
||||||
|
assertIsOidcError(
|
||||||
|
throwable = RustAuthenticationException.OidcNotSupported("Oidc not supported"),
|
||||||
|
type = "OidcNotSupported",
|
||||||
|
message = "Oidc not supported"
|
||||||
|
)
|
||||||
|
assertIsOidcError(
|
||||||
|
throwable = RustAuthenticationException.OidcCancelled("Oidc cancelled"),
|
||||||
|
type = "OidcCancelled",
|
||||||
|
message = "Oidc cancelled"
|
||||||
|
)
|
||||||
|
assertIsOidcError(
|
||||||
|
throwable = RustAuthenticationException.OidcCallbackUrlInvalid("Oidc callback url invalid"),
|
||||||
|
type = "OidcCallbackUrlInvalid",
|
||||||
|
message = "Oidc callback url invalid"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T> ThrowableSubject.isException(message: String) {
|
||||||
|
isInstanceOf(T::class.java)
|
||||||
|
hasMessageThat().isEqualTo(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun assertIsOidcError(throwable: Throwable, type: String, message: String) {
|
||||||
|
val authenticationException = throwable.mapAuthenticationException()
|
||||||
|
assertThat(authenticationException).isInstanceOf(AuthenticationException.OidcError::class.java)
|
||||||
|
assertThat((authenticationException as? AuthenticationException.OidcError)?.type).isEqualTo(type)
|
||||||
|
assertThat(authenticationException.message).isEqualTo(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
* 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.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.sun.jna.Pointer
|
||||||
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||||
|
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomList
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListEntry
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListInput
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
||||||
|
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
|
||||||
|
// Since we don't access the data in those objects, this is fine for our tests, but that's as far as we can test this class.
|
||||||
|
class RoomSummaryListProcessorTests {
|
||||||
|
|
||||||
|
private val summaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `postUpdates can't start until postEntries is done`() = runTest {
|
||||||
|
val processor = createProcessor()
|
||||||
|
val update = listOf(RoomListEntriesUpdate.Reset(emptyList()))
|
||||||
|
|
||||||
|
val timeoutError = runCatching {
|
||||||
|
withTimeout(10.milliseconds) { processor.postUpdate(update) }
|
||||||
|
}.exceptionOrNull()
|
||||||
|
assertThat(timeoutError).isInstanceOf(CancellationException::class.java)
|
||||||
|
|
||||||
|
processor.postEntries(listOf(RoomListEntry.Empty))
|
||||||
|
processor.postUpdate(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `postEntries adds all new entries with no diffing`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
|
val processor = createProcessor()
|
||||||
|
|
||||||
|
processor.postEntries(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Append adds new entries at the end of the list`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
|
val processor = createProcessor()
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(4)
|
||||||
|
assertThat(summaries.value.subList(1, 4).all { it is RoomSummary.Empty }).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
|
val processor = createProcessor()
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(RoomListEntry.Empty)))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
|
assertThat(summaries.value.last()).isInstanceOf(RoomSummary.Empty::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
|
val processor = createProcessor()
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(RoomListEntry.Empty)))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
|
assertThat(summaries.value.first()).isInstanceOf(RoomSummary.Empty::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Set replaces an entry at some index`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), RoomListEntry.Empty)))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
assertThat(summaries.value[index]).isInstanceOf(RoomSummary.Empty::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Insert inserts a new entry at the provided index`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), RoomListEntry.Empty)))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
|
assertThat(summaries.value[index]).isInstanceOf(RoomSummary.Empty::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Remove removes an entry at some index`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt())))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID_2.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `PopBack removes an entry at the end of the list`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `PopFront removes an entry at the start of the list`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID_2.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Clear removes all the entries`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Clear))
|
||||||
|
|
||||||
|
assertThat(summaries.value).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Truncate removes all entries after the provided length`() = runTest {
|
||||||
|
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||||
|
val processor = createProcessor()
|
||||||
|
val index = 0
|
||||||
|
|
||||||
|
// Start processing updates
|
||||||
|
processor.postEntries(listOf())
|
||||||
|
// Process actual update
|
||||||
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u)))
|
||||||
|
|
||||||
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
assertThat((summaries.value[index] as RoomSummary.Filled).identifier()).isEqualTo(A_ROOM_ID.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createProcessor() = RoomSummaryListProcessor(
|
||||||
|
summaries,
|
||||||
|
fakeRoomListService,
|
||||||
|
dispatcher = StandardTestDispatcher(testScheduler),
|
||||||
|
roomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fake room list service that returns Rust objects with null pointers. Luckily for us, they don't crash for our test cases
|
||||||
|
private val fakeRoomListService = object : RoomListServiceInterface {
|
||||||
|
override suspend fun allRooms(): RoomList {
|
||||||
|
return RoomList(Pointer.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun applyInput(input: RoomListInput) = Unit
|
||||||
|
|
||||||
|
override suspend fun invites(): RoomList {
|
||||||
|
return RoomList(Pointer.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun room(roomId: String): RoomListItem {
|
||||||
|
return RoomListItem(Pointer.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun state(listener: RoomListServiceStateListener): TaskHandle {
|
||||||
|
return TaskHandle(Pointer.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun syncIndicator(delayBeforeShowingInMs: UInt, delayBeforeHidingInMs: UInt, listener: RoomListServiceSyncIndicatorListener): TaskHandle {
|
||||||
|
return TaskHandle(Pointer.NULL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue