Add first test on RustMatrixClient

This commit is contained in:
Benoit Marty 2024-09-19 09:50:29 +02:00 committed by Benoit Marty
parent 929cefa21f
commit c8c79319eb
16 changed files with 279 additions and 39 deletions

View file

@ -54,6 +54,7 @@ import io.element.android.libraries.matrix.impl.pushers.RustPushersService
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
import io.element.android.libraries.matrix.impl.room.RustRoomFactory
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
@ -123,6 +124,7 @@ class RustMatrixClient(
baseCacheDirectory: File,
private val clock: SystemClock,
private val sessionDelegate: RustClientSessionDelegate,
timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
) : MatrixClient {
override val sessionId: UserId = UserId(client.userId())
override val deviceId: DeviceId = DeviceId(client.deviceId())
@ -185,6 +187,7 @@ class RustMatrixClient(
systemClock = clock,
roomContentForwarder = RoomContentForwarder(innerRoomListService),
roomSyncSubscriber = roomSyncSubscriber,
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
)
override val mediaLoader: MatrixMediaLoader = RustMediaLoader(

View file

@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.impl.certificates.UserCertificatesPro
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData
@ -45,6 +46,7 @@ class RustMatrixClientFactory @Inject constructor(
private val clock: SystemClock,
private val utdTracker: UtdTracker,
private val featureFlagService: FeatureFlagService,
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
) {
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
val sessionDelegate = RustClientSessionDelegate(sessionStore, appCoroutineScope, coroutineDispatchers)
@ -76,6 +78,7 @@ class RustMatrixClientFactory @Inject constructor(
baseCacheDirectory = cacheDirectory,
clock = clock,
sessionDelegate = sessionDelegate,
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
).also {
Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'")
}

View file

@ -27,12 +27,10 @@ import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
import org.matrix.rustcomponents.sdk.Membership
import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.RoomListException
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
import timber.log.Timber
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
@ -49,6 +47,7 @@ class RustRoomFactory(
private val roomListService: RoomListService,
private val innerRoomListService: InnerRoomListService,
private val roomSyncSubscriber: RoomSyncSubscriber,
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
) {
@OptIn(ExperimentalCoroutinesApi::class)
private val dispatcher = dispatchers.io.limitedParallelism(1)
@ -74,11 +73,7 @@ class RustRoomFactory(
private val eventFilters = TimelineConfig.excludedEvents
.takeIf { it.isNotEmpty() }
?.let { listStateEventType ->
TimelineEventTypeFilter.exclude(
listStateEventType.map { stateEventType ->
FilterTimelineEventType.State(stateEventType.map())
}
)
timelineEventTypeFilterFactory.create(listStateEventType)
}
suspend fun destroy() {

View file

@ -0,0 +1,30 @@
/*
* 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.room
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.room.StateEventType
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
import javax.inject.Inject
interface TimelineEventTypeFilterFactory {
fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter
}
@ContributesBinding(AppScope::class)
class RustTimelineEventTypeFilterFactory @Inject constructor() : TimelineEventTypeFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter {
return TimelineEventTypeFilter.exclude(
listStateEventType.map { stateEventType ->
FilterTimelineEventType.State(stateEventType.map())
}
)
}
}

View file

@ -0,0 +1,47 @@
/*
* 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
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustSyncService
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
import io.element.android.libraries.matrix.test.A_DEVICE_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.io.File
class RustMatrixClientTest {
@Test
fun `ensure that sessionId and deviceId can be retrieved from the client`() = runTest {
val sessionStore = InMemorySessionStore()
val sut = RustMatrixClient(
client = FakeRustClient(),
syncService = FakeRustSyncService(),
sessionStore = InMemorySessionStore(),
appCoroutineScope = this,
dispatchers = testCoroutineDispatchers(),
baseDirectory = File(""),
baseCacheDirectory = File(""),
clock = FakeSystemClock(),
sessionDelegate = RustClientSessionDelegate(
sessionStore = sessionStore,
appCoroutineScope = this,
coroutineDispatchers = testCoroutineDispatchers(),
),
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
)
assertThat(sut.sessionId).isEqualTo(A_USER_ID)
assertThat(sut.deviceId).isEqualTo(A_DEVICE_ID)
sut.close()
}
}

View file

@ -0,0 +1,39 @@
/*
* 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.aRustSession
import io.element.android.libraries.matrix.test.A_DEVICE_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.Encryption
import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.NotificationClient
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
import org.matrix.rustcomponents.sdk.NotificationSettings
import org.matrix.rustcomponents.sdk.Session
import org.matrix.rustcomponents.sdk.TaskHandle
class FakeRustClient(
private val userId: String = A_USER_ID.value,
private val deviceId: String = A_DEVICE_ID.value,
private val notificationClient: NotificationClient = FakeRustNotificationClient(),
private val notificationSettings: NotificationSettings = FakeRustNotificationSettings(),
private val encryption: Encryption = FakeRustEncryption(),
private val session: Session = aRustSession(),
) : Client(NoPointer) {
override fun userId(): String = userId
override fun deviceId(): String = deviceId
override suspend fun notificationClient(processSetup: NotificationProcessSetup) = notificationClient
override fun getNotificationSettings(): NotificationSettings = notificationSettings
override fun encryption(): Encryption = encryption
override fun session(): Session = session
override fun setDelegate(delegate: ClientDelegate?): TaskHandle = FakeRustTaskHandle()
override fun cachedAvatarUrl(): String? = null
}

View file

@ -0,0 +1,24 @@
/*
* 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.Encryption
import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.RecoveryStateListener
import org.matrix.rustcomponents.sdk.TaskHandle
import org.matrix.rustcomponents.sdk.VerificationStateListener
class FakeRustEncryption : Encryption(NoPointer) {
override fun verificationStateListener(listener: VerificationStateListener): TaskHandle {
return FakeRustTaskHandle()
}
override fun recoveryStateListener(listener: RecoveryStateListener): TaskHandle {
return FakeRustTaskHandle()
}
}

View file

@ -0,0 +1,13 @@
/*
* 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.NotificationClient
class FakeRustNotificationClient : NotificationClient(NoPointer)

View file

@ -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.fakes
import org.matrix.rustcomponents.sdk.NoPointer
import org.matrix.rustcomponents.sdk.NotificationSettings
import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate
class FakeRustNotificationSettings : NotificationSettings(NoPointer) {
private var delegate: NotificationSettingsDelegate? = null
override fun setDelegate(delegate: NotificationSettingsDelegate?) {
this.delegate = delegate
}
}

View file

@ -0,0 +1,13 @@
/*
* 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.RoomList
class FakeRustRoomList : RoomList(NoPointer)

View file

@ -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.NoPointer
import org.matrix.rustcomponents.sdk.RoomList
import org.matrix.rustcomponents.sdk.RoomListService
class FakeRustRoomListService : RoomListService(NoPointer) {
override suspend fun allRooms(): RoomList {
return FakeRustRoomList()
}
}

View file

@ -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.NoPointer
import org.matrix.rustcomponents.sdk.RoomListService
import org.matrix.rustcomponents.sdk.SyncService
class FakeRustSyncService(
private val roomListService: RoomListService = FakeRustRoomListService(),
) : SyncService(NoPointer) {
override fun roomListService(): RoomListService = roomListService
}

View file

@ -0,0 +1,16 @@
/*
* 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
class FakeRustTaskHandle : TaskHandle(NoPointer) {
override fun cancel() = Unit
override fun destroy() = Unit
}

View file

@ -0,0 +1,13 @@
/*
* 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.TimelineEventTypeFilter
class FakeRustTimelineEventTypeFilter : TimelineEventTypeFilter(NoPointer)

View file

@ -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.room
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineEventTypeFilter
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
class FakeTimelineEventTypeFilterFactory : TimelineEventTypeFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter {
return FakeRustTimelineEventTypeFilter()
}
}

View file

@ -8,9 +8,9 @@
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.impl.fixtures.fakes.FakeRustRoomListItem
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
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.A_ROOM_ID_3
@ -21,17 +21,8 @@ import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.matrix.rustcomponents.sdk.RoomList
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
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.RoomSubscription
import org.matrix.rustcomponents.sdk.TaskHandle
// 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 RoomSummaryListProcessorTest {
private val summaries = MutableStateFlow<List<RoomSummary>>(emptyList())
@ -163,29 +154,8 @@ class RoomSummaryListProcessorTest {
private fun TestScope.createProcessor() = RoomSummaryListProcessor(
summaries,
fakeRoomListService,
FakeRustRoomListService(),
coroutineContext = 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 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)
}
override fun subscribeToRooms(roomIds: List<String>, settings: RoomSubscription?) = Unit
}
}