diff --git a/changelog.d/4546.bugfix b/changelog.d/4546.bugfix new file mode 100644 index 0000000000..a59fa6557a --- /dev/null +++ b/changelog.d/4546.bugfix @@ -0,0 +1 @@ +Fix lots of integration tests by introducing TestMatrix class and MatrixWorkerFactory. \ No newline at end of file diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index cf9b8f87c1..8e21828562 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -20,9 +20,10 @@ import android.content.Context import android.net.Uri import androidx.lifecycle.Observer import androidx.test.internal.runner.junit4.statement.UiThreadStatement -import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -30,7 +31,6 @@ import kotlinx.coroutines.withTimeout import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue -import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig @@ -45,7 +45,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.sync.SyncState -import java.util.ArrayList +import timber.log.Timber import java.util.UUID import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -56,13 +56,14 @@ import java.util.concurrent.TimeUnit */ class CommonTestHelper(context: Context) { - val matrix: Matrix + internal val matrix: TestMatrix + val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor + fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor init { UiThreadStatement.runOnUiThread { - Matrix.initialize( + TestMatrix.initialize( context, MatrixConfiguration( applicationFlavor = "TestFlavor", @@ -70,7 +71,7 @@ class CommonTestHelper(context: Context) { ) ) } - matrix = Matrix.getInstance(context) + matrix = TestMatrix.getInstance(context) } fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session { @@ -95,33 +96,47 @@ class CommonTestHelper(context: Context) { * * @param session the session to sync */ - @Suppress("EXPERIMENTAL_API_USAGE") - fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) { + fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) { val lock = CountDownLatch(1) - - val job = GlobalScope.launch(Dispatchers.Main) { - session.open() - } - runBlocking { job.join() } - - session.startSync(true) - - val syncLiveData = runBlocking(Dispatchers.Main) { - session.getSyncStateLive() - } - val syncObserver = object : Observer { - override fun onChanged(t: SyncState?) { - if (session.hasAlreadySynced()) { - lock.countDown() - syncLiveData.removeObserver(this) + coroutineScope.launch { + session.startSync(true) + val syncLiveData = session.getSyncStateLive() + val syncObserver = object : Observer { + override fun onChanged(t: SyncState?) { + if (session.hasAlreadySynced()) { + lock.countDown() + syncLiveData.removeObserver(this) + } } } + syncLiveData.observeForever(syncObserver) } - GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) } - await(lock, timeout) } + /** + * This methods clear the cache and waits for initialSync + * + * @param session the session to sync + */ + fun clearCacheAndSync(session: Session, timeout: Long = TestConstants.timeOutMillis) { + waitWithLatch(timeout) { latch -> + session.clearCache() + val syncLiveData = session.getSyncStateLive() + val syncObserver = object : Observer { + override fun onChanged(t: SyncState?) { + if (session.hasAlreadySynced()) { + Timber.v("Clear cache and synced") + syncLiveData.removeObserver(this) + latch.countDown() + } + } + } + syncLiveData.observeForever(syncObserver) + session.startSync(true) + } + } + /** * Sends text messages in a room * @@ -130,46 +145,57 @@ class CommonTestHelper(context: Context) { * @param nbOfMessages the number of time the message will be sent */ fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List { - val timeline = room.createTimeline(null, TimelineSettings(10)) val sentEvents = ArrayList(nbOfMessages) - val latch = CountDownLatch(1) - val timelineListener = object : Timeline.Listener { - override fun onTimelineFailure(throwable: Throwable) { - } + val timeline = room.createTimeline(null, TimelineSettings(10)) + timeline.start() + waitWithLatch(timeout + 1_000L * nbOfMessages) { latch -> + val timelineListener = object : Timeline.Listener { + override fun onTimelineFailure(throwable: Throwable) { + } - override fun onNewTimelineEvents(eventIds: List) { - // noop - } + override fun onNewTimelineEvents(eventIds: List) { + // noop + } - override fun onTimelineUpdated(snapshot: List) { - val newMessages = snapshot - .filter { it.root.sendState == SendState.SYNCED } - .filter { it.root.getClearType() == EventType.MESSAGE } - .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true } + override fun onTimelineUpdated(snapshot: List) { + val newMessages = snapshot + .filter { it.root.sendState == SendState.SYNCED } + .filter { it.root.getClearType() == EventType.MESSAGE } + .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true } - if (newMessages.size == nbOfMessages) { - sentEvents.addAll(newMessages) - // Remove listener now, if not at the next update sendEvents could change - timeline.removeListener(this) - latch.countDown() + Timber.v("New synced message size: ${newMessages.size}") + if (newMessages.size == nbOfMessages) { + sentEvents.addAll(newMessages) + // Remove listener now, if not at the next update sendEvents could change + timeline.removeListener(this) + latch.countDown() + } } } + timeline.addListener(timelineListener) + sendTextMessagesBatched(room, message, nbOfMessages) } - timeline.start() - timeline.addListener(timelineListener) - for (i in 0 until nbOfMessages) { - room.sendTextMessage(message + " #" + (i + 1)) - } - // Wait 3 second more per message - await(latch, timeout = timeout + 3_000L * nbOfMessages) timeline.dispose() - // Check that all events has been created assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong()) - return sentEvents } + /** + * Will send nb of messages provided by count parameter but waits a bit every 10 messages to avoid gap in sync + */ + private fun sendTextMessagesBatched(room: Room, message: String, count: Int) { + (1 until count + 1) + .map { "$message #$it" } + .chunked(10) + .forEach { batchedMessages -> + batchedMessages.forEach { formattedMessage -> + room.sendTextMessage(formattedMessage) + } + Thread.sleep(1_000L) + } + } + // PRIVATE METHODS ***************************************************************************** /** @@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) { assertTrue(registrationResult is RegistrationResult.Success) val session = (registrationResult as RegistrationResult.Success).session + session.open() if (sessionTestParams.withInitialSync) { syncSession(session, 60_000) } - return session } @@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) { .getLoginWizard() .login(userName, password, "myDevice") } - + session.open() if (sessionTestParams.withInitialSync) { syncSession(session) } @@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) { assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)) } - @Suppress("EXPERIMENTAL_API_USAGE") - fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { - GlobalScope.launch { - while (true) { - delay(1000) - if (condition()) { - latch.countDown() - return@launch - } + suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { + while (true) { + delay(1000) + if (condition()) { + latch.countDown() + return } } } - fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, block: (CountDownLatch) -> Unit) { + fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, dispatcher: CoroutineDispatcher = Dispatchers.Main, block: suspend (CountDownLatch) -> Unit) { val latch = CountDownLatch(1) - block(latch) + coroutineScope.launch(dispatcher) { + block(latch) + } await(latch, timeout) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index a8cbc160dd..ccea6f53b9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -19,10 +19,6 @@ package org.matrix.android.sdk.common import android.os.SystemClock import android.util.Log import androidx.lifecycle.Observer -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -31,6 +27,7 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction @@ -44,16 +41,16 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import java.util.UUID -import java.util.concurrent.CountDownLatch import kotlin.coroutines.Continuation import kotlin.coroutines.resume -class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { +class CryptoTestHelper(private val testHelper: CommonTestHelper) { private val messagesFromAlice: List = listOf("0 - Hello I'm Alice!", "4 - Go!") private val messagesFromBob: List = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.") @@ -64,27 +61,33 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { * @return alice session */ fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) - val roomId = mTestHelper.runBlockingTest { + val roomId = testHelper.runBlockingTest { aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) } - if (encryptedRoom) { - val room = aliceSession.getRoom(roomId)!! - - mTestHelper.runBlockingTest { + testHelper.waitWithLatch { latch -> + val room = aliceSession.getRoom(roomId)!! room.enableEncryption() + val roomSummaryLive = room.getRoomSummaryLive() + val roomSummaryObserver = object : Observer> { + override fun onChanged(roomSummary: Optional) { + if (roomSummary.getOrNull()?.isEncrypted.orFalse()) { + roomSummaryLive.removeObserver(this) + latch.countDown() + } + } + } + roomSummaryLive.observeForever(roomSummaryObserver) } } - return CryptoTestData(roomId, listOf(aliceSession)) } /** * @return alice and bob sessions */ - @Suppress("EXPERIMENTAL_API_USAGE") fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData { val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom) val aliceSession = cryptoTestData.firstSession @@ -92,54 +95,37 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { val aliceRoom = aliceSession.getRoom(aliceRoomId)!! - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) - val lock1 = CountDownLatch(1) - - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { - bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) - } - - val newRoomObserver = object : Observer> { - override fun onChanged(t: List?) { - if (t?.isNotEmpty() == true) { - lock1.countDown() - bobRoomSummariesLive.removeObserver(this) + testHelper.waitWithLatch { latch -> + val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) + val newRoomObserver = object : Observer> { + override fun onChanged(t: List?) { + if (t?.isNotEmpty() == true) { + bobRoomSummariesLive.removeObserver(this) + latch.countDown() + } } } - } - - GlobalScope.launch(Dispatchers.Main) { bobRoomSummariesLive.observeForever(newRoomObserver) - } - - mTestHelper.runBlockingTest { aliceRoom.invite(bobSession.myUserId) } - mTestHelper.await(lock1) - - val lock = CountDownLatch(1) - - val roomJoinedObserver = object : Observer> { - override fun onChanged(t: List?) { - if (bobSession.getRoom(aliceRoomId) - ?.getRoomMember(aliceSession.myUserId) - ?.membership == Membership.JOIN) { - lock.countDown() - bobRoomSummariesLive.removeObserver(this) + testHelper.waitWithLatch { latch -> + val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) + val roomJoinedObserver = object : Observer> { + override fun onChanged(t: List?) { + if (bobSession.getRoom(aliceRoomId) + ?.getRoomMember(bobSession.myUserId) + ?.membership == Membership.JOIN) { + bobRoomSummariesLive.removeObserver(this) + latch.countDown() + } } } - } - - GlobalScope.launch(Dispatchers.Main) { bobRoomSummariesLive.observeForever(roomJoinedObserver) + bobSession.joinRoom(aliceRoomId) } - - mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) } - - mTestHelper.await(lock) - // Ensure bob can send messages to the room // val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! // assertNotNull(roomFromBobPOV.powerLevels) @@ -171,13 +157,13 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { * @Return Sam session */ fun createSamAccountAndInviteToTheRoom(room: Room): Session { - val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) + val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { room.invite(samSession.myUserId, null) } - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { samSession.joinRoom(room.roomId, null, emptyList()) } @@ -194,23 +180,20 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { val bobSession = cryptoTestData.secondSession!! bobSession.cryptoService().setWarnOnUnknownDevices(false) - aliceSession.cryptoService().setWarnOnUnknownDevices(false) val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! // Alice sends a message - mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1) -// roomFromAlicePOV.sendTextMessage(messagesFromAlice[0]) + testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1) // Bob send 3 messages - mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1) - mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1) - mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1) + testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1) + testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1) + testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1) // Alice sends a message - mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1) - + testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1) return cryptoTestData } @@ -256,60 +239,44 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { ) } - @Suppress("EXPERIMENTAL_API_USAGE") fun createDM(alice: Session, bob: Session): String { - val roomId = mTestHelper.runBlockingTest { - alice.createDirectRoom(bob.myUserId) - } - - mTestHelper.waitWithLatch { latch -> - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { - bob.getRoomSummariesLive(roomSummaryQueryParams { }) - } - + var roomId: String = "" + testHelper.waitWithLatch { latch -> + roomId = alice.createDirectRoom(bob.myUserId) + val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { }) val newRoomObserver = object : Observer> { override fun onChanged(t: List?) { val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1 if (indexOfFirst != -1) { - latch.countDown() bobRoomSummariesLive.removeObserver(this) + latch.countDown() } } } - - GlobalScope.launch(Dispatchers.Main) { - bobRoomSummariesLive.observeForever(newRoomObserver) - } + bobRoomSummariesLive.observeForever(newRoomObserver) } - mTestHelper.waitWithLatch { latch -> - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { - bob.getRoomSummariesLive(roomSummaryQueryParams { }) - } - + testHelper.waitWithLatch { latch -> + val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { }) val newRoomObserver = object : Observer> { override fun onChanged(t: List?) { if (bob.getRoom(roomId) ?.getRoomMember(bob.myUserId) ?.membership == Membership.JOIN) { - latch.countDown() bobRoomSummariesLive.removeObserver(this) + latch.countDown() } } } - - GlobalScope.launch(Dispatchers.Main) { - bobRoomSummariesLive.observeForever(newRoomObserver) - } - - mTestHelper.runBlockingTest { bob.joinRoom(roomId) } + bobRoomSummariesLive.observeForever(newRoomObserver) + bob.joinRoom(roomId) } return roomId } fun initializeCrossSigning(session: Session) { - mTestHelper.doSync { + testHelper.doSync { session.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -346,8 +313,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { var bobPovTx: IncomingSasVerificationTransaction? = null // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") if (bobPovTx?.state == VerificationTxState.OnStarted) { @@ -359,16 +326,16 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { } } - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}") alicePovTx?.state == VerificationTxState.ShortCodeReady } } // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") if (bobPovTx?.state == VerificationTxState.OnStarted) { @@ -383,38 +350,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { bobPovTx!!.userHasVerifiedShortCode() alicePovTx!!.userHasVerifiedShortCode() - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) } } - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) } } } fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) aliceSession.cryptoService().setWarnOnUnknownDevices(false) - val roomId = mTestHelper.runBlockingTest { + val roomId = testHelper.runBlockingTest { aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) } val room = aliceSession.getRoom(roomId)!! - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { room.enableEncryption() } val sessions = mutableListOf(aliceSession) for (index in 1 until numberOfMembers) { - val session = mTestHelper.createAccount("User_$index", defaultSessionParams) - mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } + val session = testHelper.createAccount("User_$index", defaultSessionParams) + testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } println("TEST -> " + session.myUserId + " invited") - mTestHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) } + testHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) } println("TEST -> " + session.myUserId + " joined") sessions.add(session) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt similarity index 53% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt index 68e1953eff..c1596c281c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,18 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.worker +package org.matrix.android.sdk.common -import android.content.Context -import androidx.work.ListenableWorker -import androidx.work.WorkerParameters +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver -interface DelegateWorkerFactory { +/** + * Force foreground for testing + */ +internal class TestBackgroundDetectionObserver : BackgroundDetectionObserver { - fun create(context: Context, params: WorkerParameters): ListenableWorker + override val isInBackground: Boolean = false + + override fun register(listener: BackgroundDetectionObserver.Listener) = Unit + + override fun unregister(listener: BackgroundDetectionObserver.Listener) = Unit } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt similarity index 80% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt index 8b9b6efa11..e92232a7c5 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.api +package org.matrix.android.sdk.common import android.content.Context import android.os.Handler @@ -24,27 +24,27 @@ import androidx.work.Configuration import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.BuildConfig +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.api.network.ApiInterceptorListener import org.matrix.android.sdk.api.network.ApiPath import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.common.DaggerTestMatrixComponent import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import org.matrix.olm.OlmManager import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject /** - * This is the main entry point to the matrix sdk. - * To get the singleton instance, use getInstance static method. + * This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent. */ -class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) { +internal class TestMatrix constructor(context: Context, matrixConfiguration: MatrixConfiguration) { @Inject internal lateinit var legacySessionImporter: LegacySessionImporter @Inject internal lateinit var authenticationService: AuthenticationService @@ -55,15 +55,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo @Inject internal lateinit var sessionManager: SessionManager @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var apiInterceptor: ApiInterceptor + @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory private val uiHandler = Handler(Looper.getMainLooper()) init { Monarchy.init(context) DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this) - if (context.applicationContext !is Configuration.Provider) { - WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) - } + val configuration = Configuration.Builder() + .setExecutor(Executors.newCachedThreadPool()) + .setWorkerFactory(matrixWorkerFactory) + .build() + WorkManager.initialize(context, configuration) uiHandler.post { ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) } @@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo companion object { - private lateinit var instance: Matrix + private lateinit var instance: TestMatrix private val isInit = AtomicBoolean(false) fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) { if (isInit.compareAndSet(false, true)) { - instance = Matrix(context.applicationContext, matrixConfiguration) + instance = TestMatrix(context.applicationContext, matrixConfiguration) } } - fun getInstance(context: Context): Matrix { + fun getInstance(context: Context): TestMatrix { if (isInit.compareAndSet(false, true)) { val appContext = context.applicationContext if (appContext is MatrixConfiguration.Provider) { val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration() - instance = Matrix(appContext, matrixConfiguration) + instance = TestMatrix(appContext, matrixConfiguration) } else { throw IllegalStateException("Matrix is not initialized properly." + " You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.") diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt index 1d05e655af..d0f0e23152 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt @@ -34,12 +34,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule NetworkModule::class, AuthModule::class, RawModule::class, - SystemModule::class, - TestNetworkModule::class + SystemModule::class ]) @MatrixScope internal interface TestMatrixComponent : MatrixComponent { + fun inject(matrix: TestMatrix) + @Component.Factory interface Factory { fun create(@BindsInstance context: Context, diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt index 3e4d5fe08e..d82bac3700 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt @@ -18,10 +18,39 @@ package org.matrix.android.sdk.common import dagger.Binds import dagger.Module +import dagger.Provides import org.matrix.android.sdk.internal.di.MatrixComponent +import org.matrix.android.sdk.internal.di.MatrixScope +import org.matrix.android.sdk.internal.session.MockHttpInterceptor +import org.matrix.android.sdk.internal.session.TestInterceptor +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver @Module internal abstract class TestModule { @Binds abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent + + @Module + companion object { + + val interceptors = ArrayList() + + fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId } + + @Provides + @JvmStatic + @MockHttpInterceptor + fun providesTestInterceptor(): TestInterceptor? { + return MockOkHttpInterceptor().also { + interceptors.add(it) + } + } + + @Provides + @JvmStatic + @MatrixScope + fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver { + return TestBackgroundDetectionObserver() + } + } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt deleted file mode 100644 index 4cd92ca272..0000000000 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * 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 org.matrix.android.sdk.common - -import dagger.Module -import dagger.Provides -import org.matrix.android.sdk.internal.session.MockHttpInterceptor -import org.matrix.android.sdk.internal.session.TestInterceptor - -@Module -internal object TestNetworkModule { - - val interceptors = ArrayList() - - fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId } - - @Provides - @JvmStatic - @MockHttpInterceptor - fun providesTestInterceptor(): TestInterceptor? { - return MockOkHttpInterceptor().also { - interceptors.add(it) - } - } -} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt index 825fba570a..d0f63227f5 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt @@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent @FixMethodOrder(MethodSorters.JVM) class PreShareKeysTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun ensure_outbound_session_happy_path() { - val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) val e2eRoomID = testData.roomId val aliceSession = testData.firstSession val bobSession = testData.secondSession!! @@ -58,12 +58,12 @@ class PreShareKeysTest : InstrumentedTest { Log.d("#Test", "Room Key Received from alice $preShareCount") // Force presharing of new outbound key - mTestHelper.doSync { + testHelper.doSync { aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it) } - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val newGossipCount = bobSession.cryptoService().getGossipingEvents().count { it.senderId == aliceSession.myUserId && it.getClearType() == EventType.ROOM_KEY @@ -88,16 +88,16 @@ class PreShareKeysTest : InstrumentedTest { assertEquals("The session received by bob should match what alice sent", 0, sharedIndex) // Just send a real message as test - val sentEvent = mTestHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first() + val sentEvent = testHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first() assertEquals(megolmSessionId, sentEvent.root.content.toModel()?.sessionId, "Unexpected megolm session") - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE } } - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt index cf31294e2f..458eae6ab2 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt @@ -62,8 +62,8 @@ import kotlin.coroutines.resume class UnwedgingTest : InstrumentedTest { private lateinit var messagesReceivedByBob: List - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Before fun init() { @@ -85,7 +85,7 @@ class UnwedgingTest : InstrumentedTest { */ @Test fun testUnwedging() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId @@ -133,7 +133,7 @@ class UnwedgingTest : InstrumentedTest { roomFromAlicePOV.sendTextMessage("First message") // Wait for the message to be received by Bob - mTestHelper.await(latch) + testHelper.await(latch) bobTimeline.removeListener(bobEventsListener) messagesReceivedByBob.size shouldBe 1 @@ -161,7 +161,7 @@ class UnwedgingTest : InstrumentedTest { roomFromAlicePOV.sendTextMessage("Second message") // Wait for the message to be received by Bob - mTestHelper.await(latch) + testHelper.await(latch) bobTimeline.removeListener(bobEventsListener) messagesReceivedByBob.size shouldBe 2 @@ -179,7 +179,7 @@ class UnwedgingTest : InstrumentedTest { aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId) // Wait for the message to be received by Bob - mTestHelper.waitWithLatch { + testHelper.waitWithLatch { bobEventsListener = createEventListener(it, 3) bobTimeline.addListener(bobEventsListener) messagesReceivedByBob = emptyList() @@ -201,11 +201,11 @@ class UnwedgingTest : InstrumentedTest { Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType()) Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType()) // Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged - mTestHelper.await(bobFinalLatch) + testHelper.await(bobFinalLatch) bobTimeline.removeListener(bobHasThreeDecryptedEventsListener) // It's a trick to force key request on fail to decrypt - mTestHelper.doSync { + testHelper.doSync { bobSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -222,8 +222,8 @@ class UnwedgingTest : InstrumentedTest { } // Wait until we received back the key - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { // we should get back the key and be able to decrypt val result = tryOrNull { bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "") @@ -235,7 +235,7 @@ class UnwedgingTest : InstrumentedTest { bobTimeline.dispose() - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt index 44af87bcbe..d9cc7a8ac0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt @@ -45,14 +45,14 @@ import kotlin.coroutines.resume @FixMethodOrder(MethodSorters.NAME_ASCENDING) class XSigningTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_InitializeAndStoreKeys() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - mTestHelper.doSync { + testHelper.doSync { aliceSession.cryptoService().crossSigningService() .initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { @@ -79,12 +79,12 @@ class XSigningTest : InstrumentedTest { assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified()) - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_CrossSigningCheckBobSeesTheKeys() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -98,21 +98,21 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - mTestHelper.doSync { + testHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(aliceAuthParams) } }, it) } - mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(bobAuthParams) } }, it) } // Check that alice can see bob keys - mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } + testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey()) @@ -124,13 +124,13 @@ class XSigningTest : InstrumentedTest { assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted()) - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) } @Test fun test_CrossSigningTestAliceTrustBobNewDevice() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -144,12 +144,12 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + testHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(aliceAuthParams) } }, it) } - mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(bobAuthParams) } @@ -157,21 +157,21 @@ class XSigningTest : InstrumentedTest { // Check that alice can see bob keys val bobUserId = bobSession.myUserId - mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } + testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false) - mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } + testHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } // Now bobs logs in on a new device and verifies it // We will want to test that in alice POV, this new device would be trusted by cross signing - val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true)) + val bobSession2 = testHelper.logIntoAccount(bobUserId, SessionTestParams(true)) val bobSecondDeviceId = bobSession2.sessionParams.deviceId!! // Check that bob first session sees the new login - val data = mTestHelper.doSync> { + val data = testHelper.doSync> { bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } @@ -183,12 +183,12 @@ class XSigningTest : InstrumentedTest { assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice) // Manually mark it as trusted from first session - mTestHelper.doSync { + testHelper.doSync { bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it) } // Now alice should cross trust bob's second device - val data2 = mTestHelper.doSync> { + val data2 = testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } @@ -200,8 +200,8 @@ class XSigningTest : InstrumentedTest { val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) - mTestHelper.signOutAndClose(bobSession2) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession2) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt index da5e90abdd..189fc405eb 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt @@ -40,8 +40,9 @@ import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EncryptionTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_EncryptionEvent() { @@ -69,7 +70,7 @@ class EncryptionTest : InstrumentedTest { } private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false) + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false) val aliceSession = cryptoTestData.firstSession val room = aliceSession.getRoom(cryptoTestData.roomId)!! @@ -101,12 +102,12 @@ class EncryptionTest : InstrumentedTest { timeline.addListener(timelineListener) action.invoke(room) - - mTestHelper.await(latch) + testHelper.await(latch) timeline.dispose() - - room.isEncrypted() shouldBe roomShouldBeEncrypted - - cryptoTestData.cleanUp(mTestHelper) + testHelper.waitWithLatch { + room.isEncrypted() shouldBe roomShouldBeEncrypted + it.countDown() + } + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt index 40659cef11..975d481628 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt @@ -44,7 +44,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.common.CommonTestHelper -import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.SessionTestParams import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.internal.crypto.GossipingRequestState @@ -55,7 +54,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent -import java.util.concurrent.CountDownLatch import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -63,15 +61,14 @@ import kotlin.coroutines.resume @FixMethodOrder(MethodSorters.JVM) class KeyShareTests : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val commonTestHelper = CommonTestHelper(context()) @Test fun test_DoNotSelfShareIfNotTrusted() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) // Create an encrypted room and add a message - val roomId = mTestHelper.runBlockingTest { + val roomId = commonTestHelper.runBlockingTest { aliceSession.createRoom( CreateRoomParams().apply { visibility = RoomDirectoryVisibility.PRIVATE @@ -83,11 +80,11 @@ class KeyShareTests : InstrumentedTest { assertNotNull(room) Thread.sleep(4_000) assertTrue(room?.isEncrypted() == true) - val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId + val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId // Open a new sessionx - val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true)) + val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true)) val roomSecondSessionPOV = aliceSession2.getRoom(roomId) @@ -105,25 +102,24 @@ class KeyShareTests : InstrumentedTest { // Try to request aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root) - val waitLatch = CountDownLatch(1) val eventMegolmSessionId = receivedEvent.root.content.toModel()?.sessionId var outGoingRequestId: String? = null - mTestHelper.retryPeriodicallyWithLatch(waitLatch) { - aliceSession2.cryptoService().getOutgoingRoomKeyRequests() - .filter { req -> - // filter out request that was known before - !outgoingRequestsBefore.any { req.requestId == it.requestId } - } - .let { - val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId } - outGoingRequestId = outgoing?.requestId - outgoing != null - } + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + aliceSession2.cryptoService().getOutgoingRoomKeyRequests() + .filter { req -> + // filter out request that was known before + !outgoingRequestsBefore.any { req.requestId == it.requestId } + } + .let { + val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId } + outGoingRequestId = outgoing?.requestId + outgoing != null + } + } } - mTestHelper.await(waitLatch) - Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId") val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests() @@ -134,8 +130,8 @@ class KeyShareTests : InstrumentedTest { // The first session should see an incoming request // the request should be refused, because the device is not trusted - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { // DEBUG LOGS aliceSession.cryptoService().getIncomingRoomKeyRequests().let { Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)") @@ -164,8 +160,8 @@ class KeyShareTests : InstrumentedTest { // Re request aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root) - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { aliceSession.cryptoService().getIncomingRoomKeyRequests().let { Log.v("TEST", "Incoming request Session 1") Log.v("TEST", "=========================") @@ -180,8 +176,8 @@ class KeyShareTests : InstrumentedTest { } Thread.sleep(6_000) - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let { it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED } } @@ -194,15 +190,15 @@ class KeyShareTests : InstrumentedTest { fail("should have been able to decrypt") } - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(aliceSession2) + commonTestHelper.signOutAndClose(aliceSession) + commonTestHelper.signOutAndClose(aliceSession2) } @Test fun test_ShareSSSSSecret() { - val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - mTestHelper.doSync { + commonTestHelper.doSync { aliceSession1.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -218,25 +214,25 @@ class KeyShareTests : InstrumentedTest { } // Also bootstrap keybackup on first session - val creationInfo = mTestHelper.doSync { + val creationInfo = commonTestHelper.doSync { aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) } - val version = mTestHelper.doSync { + val version = commonTestHelper.doSync { aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it) } // Save it for gossiping aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) - val aliceSession2 = mTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true)) + val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true)) val aliceVerificationService1 = aliceSession1.cryptoService().verificationService() val aliceVerificationService2 = aliceSession2.cryptoService().verificationService() // force keys download - mTestHelper.doSync> { + commonTestHelper.doSync> { aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it) } - mTestHelper.doSync> { + commonTestHelper.doSync> { aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it) } @@ -276,8 +272,8 @@ class KeyShareTests : InstrumentedTest { aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId ?: "", txId) - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true } } @@ -290,31 +286,31 @@ class KeyShareTests : InstrumentedTest { // SSK and USK private keys should have been shared - mTestHelper.waitWithLatch(60_000) { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch(60_000) { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}") aliceSession2.cryptoService().crossSigningService().canCrossSign() } } // Test that key backup key has been shared to - mTestHelper.waitWithLatch(60_000) { latch -> + commonTestHelper.waitWithLatch(60_000) { latch -> val keysBackupService = aliceSession2.cryptoService().keysBackupService() - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.retryPeriodicallyWithLatch(latch) { Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}") keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey } } - mTestHelper.signOutAndClose(aliceSession1) - mTestHelper.signOutAndClose(aliceSession2) + commonTestHelper.signOutAndClose(aliceSession1) + commonTestHelper.signOutAndClose(aliceSession2) } @Test fun test_ImproperKeyShareBug() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - mTestHelper.doSync { + commonTestHelper.doSync { aliceSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -331,7 +327,7 @@ class KeyShareTests : InstrumentedTest { } // Create an encrypted room and send a couple of messages - val roomId = mTestHelper.runBlockingTest { + val roomId = commonTestHelper.runBlockingTest { aliceSession.createRoom( CreateRoomParams().apply { visibility = RoomDirectoryVisibility.PRIVATE @@ -343,12 +339,12 @@ class KeyShareTests : InstrumentedTest { assertNotNull(roomAlicePov) Thread.sleep(1_000) assertTrue(roomAlicePov?.isEncrypted() == true) - val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId + val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId // Create bob session - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) - mTestHelper.doSync { + val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) + commonTestHelper.doSync { bobSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -365,11 +361,11 @@ class KeyShareTests : InstrumentedTest { } // Let alice invite bob - mTestHelper.runBlockingTest { + commonTestHelper.runBlockingTest { roomAlicePov.invite(bobSession.myUserId, null) } - mTestHelper.runBlockingTest { + commonTestHelper.runBlockingTest { bobSession.joinRoom(roomAlicePov.roomId, null, emptyList()) } @@ -377,7 +373,7 @@ class KeyShareTests : InstrumentedTest { aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId) // and now resend a new message to reset index to 0 - mTestHelper.sendTextMessage(roomAlicePov, "After", 1) + commonTestHelper.sendTextMessage(roomAlicePov, "After", 1) val roomRoomBobPov = aliceSession.getRoom(roomId) val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt index c939952dc9..c835c2d40b 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt @@ -41,8 +41,8 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode @FixMethodOrder(MethodSorters.JVM) class WithHeldTests : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_WithHeldUnverifiedReason() { @@ -50,19 +50,19 @@ class WithHeldTests : InstrumentedTest { // ARRANGE // ============================= - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) // Initialize cross signing on both - mCryptoTestHelper.initializeCrossSigning(aliceSession) - mCryptoTestHelper.initializeCrossSigning(bobSession) + cryptoTestHelper.initializeCrossSigning(aliceSession) + cryptoTestHelper.initializeCrossSigning(bobSession) - val roomId = mCryptoTestHelper.createDM(aliceSession, bobSession) - mCryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId) + val roomId = cryptoTestHelper.createDM(aliceSession, bobSession) + cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId) val roomAlicePOV = aliceSession.getRoom(roomId)!! - val bobUnverifiedSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) + val bobUnverifiedSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) // ============================= // ACT @@ -71,11 +71,11 @@ class WithHeldTests : InstrumentedTest { // Alice decide to not send to unverified sessions aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true) - val timelineEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first() + val timelineEvent = testHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first() // await for bob unverified session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null } } @@ -101,10 +101,10 @@ class WithHeldTests : InstrumentedTest { // enable back sending to unverified aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false) - val secondEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first() + val secondEvent = testHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first() - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId) // wait until it's decrypted ev?.root?.getClearType() == EventType.MESSAGE @@ -123,17 +123,17 @@ class WithHeldTests : InstrumentedTest { Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage) } - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) - mTestHelper.signOutAndClose(bobUnverifiedSession) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobUnverifiedSession) } @Test fun test_WithHeldNoOlm() { - val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = testData.firstSession val bobSession = testData.secondSession!! - val aliceInterceptor = mTestHelper.getTestInterceptor(aliceSession) + val aliceInterceptor = testHelper.getTestInterceptor(aliceSession) // Simulate no OTK aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule( @@ -147,11 +147,11 @@ class WithHeldTests : InstrumentedTest { val roomAlicePov = aliceSession.getRoom(testData.roomId)!! - val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId + val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId // await for bob session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null } } @@ -177,14 +177,14 @@ class WithHeldTests : InstrumentedTest { // Add a new device for bob aliceInterceptor.clearRules() - val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true)) + val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true)) // send a second message - val secondMessageId = mTestHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId + val secondMessageId = testHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId // Check that the // await for bob SecondSession session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null } } @@ -194,27 +194,27 @@ class WithHeldTests : InstrumentedTest { Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2) aliceInterceptor.clearRules() - testData.cleanUp(mTestHelper) - mTestHelper.signOutAndClose(bobSecondSession) + testData.cleanUp(testHelper) + testHelper.signOutAndClose(bobSecondSession) } @Test fun test_WithHeldKeyRequest() { - val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = testData.firstSession val bobSession = testData.secondSession!! val roomAlicePov = aliceSession.getRoom(testData.roomId)!! - val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId + val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession) // Create a new session for bob - val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) + val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) // initialize to force request keys if missing - mCryptoTestHelper.initializeCrossSigning(bobSecondSession) + cryptoTestHelper.initializeCrossSigning(bobSecondSession) // Trust bob second device from Alice POV aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback()) @@ -223,8 +223,8 @@ class WithHeldTests : InstrumentedTest { var sessionId: String? = null // Check that the // await for bob SecondSession session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also { // try to decrypt and force key request tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") } @@ -235,8 +235,8 @@ class WithHeldTests : InstrumentedTest { } // Check that bob second session requested the key - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!) wc?.code == WithHeldCode.UNAUTHORISED } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index 0785dba8b9..2a07b74115 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -22,7 +22,6 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue -import org.junit.Assert.fail import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -43,7 +42,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreat import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult -import java.util.ArrayList import java.util.Collections import java.util.concurrent.CountDownLatch @@ -51,9 +49,9 @@ import java.util.concurrent.CountDownLatch @FixMethodOrder(MethodSorters.JVM) class KeysBackupTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) - private val mKeysBackupTestHelper = KeysBackupTestHelper(mTestHelper, mCryptoTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) + private val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper) /** * - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys @@ -62,7 +60,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun roomKeysTest_testBackupStore_ok() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() // From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store @@ -92,7 +90,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)) assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -100,7 +98,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun prepareKeysBackupVersionTest() { - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) assertNotNull(bobSession.cryptoService().keysBackupService()) @@ -110,7 +108,7 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val megolmBackupCreationInfo = mTestHelper.doSync { + val megolmBackupCreationInfo = testHelper.doSync { keysBackup.prepareKeysBackupVersion(null, null, it) } @@ -120,7 +118,7 @@ class KeysBackupTest : InstrumentedTest { assertNotNull(megolmBackupCreationInfo.recoveryKey) stateObserver.stopAndCheckStates(null) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession) } /** @@ -128,7 +126,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun createKeysBackupVersionTest() { - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) val keysBackup = bobSession.cryptoService().keysBackupService() @@ -136,14 +134,14 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val megolmBackupCreationInfo = mTestHelper.doSync { + val megolmBackupCreationInfo = testHelper.doSync { keysBackup.prepareKeysBackupVersion(null, null, it) } assertFalse(keysBackup.isEnabled) // Create the version - mTestHelper.doSync { + testHelper.doSync { keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) } @@ -151,7 +149,7 @@ class KeysBackupTest : InstrumentedTest { assertTrue(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession) } /** @@ -160,8 +158,9 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun backupAfterCreateKeysBackupVersionTest() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + keysBackupTestHelper.waitForKeybackUpBatching() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val latch = CountDownLatch(1) @@ -171,9 +170,9 @@ class KeysBackupTest : InstrumentedTest { val stateObserver = StateObserver(keysBackup, latch, 5) - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) - mTestHelper.await(latch) + testHelper.await(latch) val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false) val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true) @@ -191,7 +190,7 @@ class KeysBackupTest : InstrumentedTest { KeysBackupState.ReadyToBackUp ) ) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -199,13 +198,13 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun backupAllGroupSessionsTest() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val stateObserver = StateObserver(keysBackup) - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Check that backupAllGroupSessions returns valid data val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false) @@ -214,7 +213,7 @@ class KeysBackupTest : InstrumentedTest { var lastBackedUpKeysProgress = 0 - mTestHelper.doSync { + testHelper.doSync { keysBackup.backupAllGroupSessions(object : ProgressListener { override fun onProgress(progress: Int, total: Int) { assertEquals(nbOfKeys, total) @@ -230,7 +229,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -243,7 +242,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testEncryptAndDecryptKeysBackupData() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService @@ -252,7 +251,7 @@ class KeysBackupTest : InstrumentedTest { // - Pick a megolm key val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0] - val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo + val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo // - Check encryptGroupSession() returns stg val keyBackupData = keysBackup.encryptGroupSession(session) @@ -270,10 +269,10 @@ class KeysBackupTest : InstrumentedTest { decryption!!) assertNotNull(sessionData) // - Compare the decrypted megolm key with the original one - mKeysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData) + keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -284,10 +283,10 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun restoreKeysBackupTest() { - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) // - Restore the e2e backup from the homeserver - val importRoomKeysResult = mTestHelper.doSync { + val importRoomKeysResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, null, @@ -297,9 +296,9 @@ class KeysBackupTest : InstrumentedTest { ) } - mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) + keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -369,7 +368,7 @@ class KeysBackupTest : InstrumentedTest { fun trustKeyBackupVersionTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -379,7 +378,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device - mTestHelper.doSync { + testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion( testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, true, @@ -388,21 +387,21 @@ class KeysBackupTest : InstrumentedTest { } // Wait for backup state to be ReadyToBackUp - mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) + keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) // - Backup must be enabled on the new device, on the same version assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) } @@ -411,7 +410,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -428,7 +427,7 @@ class KeysBackupTest : InstrumentedTest { fun trustKeyBackupVersionWithRecoveryKeyTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -438,7 +437,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device with the recovery key - mTestHelper.doSync { + testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey( testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, @@ -447,21 +446,21 @@ class KeysBackupTest : InstrumentedTest { } // Wait for backup state to be ReadyToBackUp - mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) + keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) // - Backup must be enabled on the new device, on the same version assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) } @@ -470,7 +469,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -485,7 +484,7 @@ class KeysBackupTest : InstrumentedTest { fun trustKeyBackupVersionWithWrongRecoveryKeyTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -501,7 +500,7 @@ class KeysBackupTest : InstrumentedTest { "Bad recovery key", TestMatrixCallback(latch, false) ) - mTestHelper.await(latch) + testHelper.await(latch) // - The new device must still see the previous backup as not trusted assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion) @@ -509,7 +508,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -528,7 +527,7 @@ class KeysBackupTest : InstrumentedTest { // - Do an e2e backup to the homeserver with a password // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -538,7 +537,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device with the password - mTestHelper.doSync { + testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase( testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, password, @@ -547,21 +546,21 @@ class KeysBackupTest : InstrumentedTest { } // Wait for backup state to be ReadyToBackUp - mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) + keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) // - Backup must be enabled on the new device, on the same version assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) } @@ -570,7 +569,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -588,7 +587,7 @@ class KeysBackupTest : InstrumentedTest { // - Do an e2e backup to the homeserver with a password // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -604,7 +603,7 @@ class KeysBackupTest : InstrumentedTest { badPassword, TestMatrixCallback(latch, false) ) - mTestHelper.await(latch) + testHelper.await(latch) // - The new device must still see the previous backup as not trusted assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion) @@ -612,7 +611,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -623,7 +622,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun restoreKeysBackupWithAWrongRecoveryKeyTest() { - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) // - Try to restore the e2e backup with a wrong recovery key val latch2 = CountDownLatch(1) @@ -640,12 +639,12 @@ class KeysBackupTest : InstrumentedTest { } } ) - mTestHelper.await(latch2) + testHelper.await(latch2) // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -658,12 +657,12 @@ class KeysBackupTest : InstrumentedTest { fun testBackupWithPassword() { val password = "password" - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) // - Restore the e2e backup with the password val steps = ArrayList() - val importRoomKeysResult = mTestHelper.doSync { + val importRoomKeysResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, password, null, @@ -698,9 +697,9 @@ class KeysBackupTest : InstrumentedTest { assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress) assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress) - mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) + keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -714,7 +713,7 @@ class KeysBackupTest : InstrumentedTest { val password = "password" val wrongPassword = "passw0rd" - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) // - Try to restore the e2e backup with a wrong password val latch2 = CountDownLatch(1) @@ -731,12 +730,12 @@ class KeysBackupTest : InstrumentedTest { } } ) - mTestHelper.await(latch2) + testHelper.await(latch2) // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -749,10 +748,10 @@ class KeysBackupTest : InstrumentedTest { fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() { val password = "password" - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) // - Restore the e2e backup with the recovery key. - val importRoomKeysResult = mTestHelper.doSync { + val importRoomKeysResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, null, @@ -762,9 +761,9 @@ class KeysBackupTest : InstrumentedTest { ) } - mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) + keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -775,7 +774,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() { - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) // - Try to restore the e2e backup with a password val latch2 = CountDownLatch(1) @@ -792,12 +791,12 @@ class KeysBackupTest : InstrumentedTest { } } ) - mTestHelper.await(latch2) + testHelper.await(latch2) // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -807,22 +806,22 @@ class KeysBackupTest : InstrumentedTest { @Test fun testIsKeysBackupTrusted() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val stateObserver = StateObserver(keysBackup) // - Do an e2e backup to the homeserver - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Get key backup version from the homeserver - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { keysBackup.getCurrentVersion(it) } // - Check the returned KeyBackupVersion is trusted - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { keysBackup.getKeysBackupTrust(keysVersionResult!!, it) } @@ -837,7 +836,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -849,9 +848,8 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() { - fail("This test still fail. To investigate") // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -859,15 +857,15 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) assertTrue(keysBackup.isEnabled) // - Restart alice session // - Log Alice on a new device - val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) + val aliceSession2 = testHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) val keysBackup2 = aliceSession2.cryptoService().keysBackupService() @@ -891,13 +889,13 @@ class KeysBackupTest : InstrumentedTest { } } }) - mTestHelper.await(latch) + testHelper.await(latch) assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion) stateObserver.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null) - mTestHelper.signOutAndClose(aliceSession2) + testHelper.signOutAndClose(aliceSession2) } /** @@ -911,7 +909,7 @@ class KeysBackupTest : InstrumentedTest { @Test fun testBackupWhenAnotherBackupWasCreated() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -939,15 +937,15 @@ class KeysBackupTest : InstrumentedTest { }) // - Make alice back up her keys to her homeserver - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) assertTrue(keysBackup.isEnabled) - mTestHelper.await(latch0) + testHelper.await(latch0) // - Create a new backup with fake data on the homeserver, directly using the rest client - val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo() - mTestHelper.doSync { + val megolmBackupCreationInfo = cryptoTestHelper.createFakeMegolmBackupCreationInfo() + testHelper.doSync { (keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it) } @@ -957,14 +955,14 @@ class KeysBackupTest : InstrumentedTest { // - Make alice back up all her keys again val latch2 = CountDownLatch(1) keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false)) - mTestHelper.await(latch2) + testHelper.await(latch2) // -> That must fail and her backup state must be WrongBackUpVersion assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state) assertFalse(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -982,17 +980,17 @@ class KeysBackupTest : InstrumentedTest { @Test fun testBackupAfterVerifyingADevice() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val stateObserver = StateObserver(keysBackup) // - Make alice back up her keys to her homeserver - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Wait for keys backup to finish by asking again to backup keys. - mTestHelper.doSync { + testHelper.doSync { keysBackup.backupAllGroupSessions(null, it) } @@ -1001,14 +999,14 @@ class KeysBackupTest : InstrumentedTest { val aliceUserId = cryptoTestData.firstSession.myUserId // - Log Alice on a new device - val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) + val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) // - Post a message to have a new megolm session aliceSession2.cryptoService().setWarnOnUnknownDevices(false) val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!! - mTestHelper.sendTextMessage(room2, "New key", 1) + testHelper.sendTextMessage(room2, "New key", 1) // - Try to backup all in aliceSession2, it must fail val keysBackup2 = aliceSession2.cryptoService().keysBackupService() @@ -1025,7 +1023,7 @@ class KeysBackupTest : InstrumentedTest { super.onSuccess(data) } }) - mTestHelper.await(latch2) + testHelper.await(latch2) assertFalse(isSuccessful) @@ -1049,12 +1047,12 @@ class KeysBackupTest : InstrumentedTest { } } }) - mTestHelper.await(latch4) + testHelper.await(latch4) // -> It must use the same backup version assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion) - mTestHelper.doSync { + testHelper.doSync { aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it) } @@ -1063,8 +1061,8 @@ class KeysBackupTest : InstrumentedTest { stateObserver.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null) - mTestHelper.signOutAndClose(aliceSession2) - cryptoTestData.cleanUp(mTestHelper) + testHelper.signOutAndClose(aliceSession2) + cryptoTestData.cleanUp(testHelper) } /** @@ -1074,7 +1072,7 @@ class KeysBackupTest : InstrumentedTest { @Test fun deleteKeysBackupTest() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -1082,17 +1080,17 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) assertTrue(keysBackup.isEnabled) // Delete the backup - mTestHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) } + testHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) } // Backup is now disabled assertFalse(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt index a625ffc0e9..592b798bcc 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt @@ -32,8 +32,12 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import java.util.concurrent.CountDownLatch class KeysBackupTestHelper( - private val mTestHelper: CommonTestHelper, - private val mCryptoTestHelper: CryptoTestHelper) { + private val testHelper: CommonTestHelper, + private val cryptoTestHelper: CryptoTestHelper) { + + fun waitForKeybackUpBatching() { + Thread.sleep(400) + } /** * Common initial condition @@ -43,7 +47,9 @@ class KeysBackupTestHelper( * @param password optional password */ fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + + waitForKeybackUpBatching() val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -57,7 +63,7 @@ class KeysBackupTestHelper( var lastProgress = 0 var lastTotal = 0 - mTestHelper.doSync { + testHelper.doSync { keysBackup.backupAllGroupSessions(object : ProgressListener { override fun onProgress(progress: Int, total: Int) { lastProgress = progress @@ -72,7 +78,7 @@ class KeysBackupTestHelper( val aliceUserId = cryptoTestData.firstSession.myUserId // - Log Alice on a new device - val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) + val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) // Test check: aliceSession2 has no keys at login Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false)) @@ -92,7 +98,7 @@ class KeysBackupTestHelper( password: String? = null): PrepareKeysBackupDataResult { val stateObserver = StateObserver(keysBackup) - val megolmBackupCreationInfo = mTestHelper.doSync { + val megolmBackupCreationInfo = testHelper.doSync { keysBackup.prepareKeysBackupVersion(password, null, it) } @@ -101,7 +107,7 @@ class KeysBackupTestHelper( Assert.assertFalse(keysBackup.isEnabled) // Create the version - val keysVersion = mTestHelper.doSync { + val keysVersion = testHelper.doSync { keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) } @@ -136,7 +142,7 @@ class KeysBackupTestHelper( } }) - mTestHelper.await(latch) + testHelper.await(latch) } fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt index b343d7334a..43f8dc0762 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt @@ -18,10 +18,6 @@ package org.matrix.android.sdk.internal.crypto.ssss import androidx.lifecycle.Observer import androidx.test.ext.junit.runners.AndroidJUnit4 -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -36,6 +32,7 @@ import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent import org.matrix.android.sdk.api.session.securestorage.KeySigner import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec import org.matrix.android.sdk.api.session.securestorage.SecretStorageKeyContent +import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageError import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo import org.matrix.android.sdk.api.util.Optional @@ -45,13 +42,12 @@ import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2 import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService -import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) class QuadSTests : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) + private val testHelper = CommonTestHelper(context()) private val emptyKeySigner = object : KeySigner { override fun sign(canonicalJson: String): Map>? { @@ -60,35 +56,29 @@ class QuadSTests : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun test_Generate4SKey() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val quadS = aliceSession.sharedSecretStorageService val TEST_KEY_ID = "my.test.Key" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner) } - // Assert Account data is updated - val accountDataLock = CountDownLatch(1) var accountData: UserAccountDataEvent? = null - - val liveAccountData = runBlocking(Dispatchers.Main) { - aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") - } - val accountDataObserver = Observer?> { t -> - if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") { - accountData = t.getOrNull() - accountDataLock.countDown() + // Assert Account data is updated + testHelper.waitWithLatch { + val liveAccountData = aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") + val accountDataObserver = Observer?> { t -> + if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") { + accountData = t.getOrNull() + } + it.countDown() } + liveAccountData.observeForever(accountDataObserver) } - GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) } - - mTestHelper.await(accountDataLock) - assertNotNull("Key should be stored in account data", accountData) val parsed = SecretStorageKeyContent.fromJson(accountData!!.content) assertNotNull("Key Content cannot be parsed", parsed) @@ -96,36 +86,29 @@ class QuadSTests : InstrumentedTest { assertEquals("Unexpected key name", "Test Key", parsed.name) assertNull("Key was not generated from passphrase", parsed.passphrase) - // Set as default key - GlobalScope.launch { - quadS.setDefaultKey(TEST_KEY_ID) - } - var defaultKeyAccountData: UserAccountDataEvent? = null - val defaultDataLock = CountDownLatch(1) - - val liveDefAccountData = runBlocking(Dispatchers.Main) { - aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID) - } - val accountDefDataObserver = Observer?> { t -> - if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) { - defaultKeyAccountData = t.getOrNull()!! - defaultDataLock.countDown() + // Set as default key + testHelper.waitWithLatch { latch -> + quadS.setDefaultKey(TEST_KEY_ID) + val liveDefAccountData = + aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID) + val accountDefDataObserver = Observer?> { t -> + if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) { + defaultKeyAccountData = t.getOrNull()!! + latch.countDown() + } } + liveDefAccountData.observeForever(accountDefDataObserver) } - GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) } - - mTestHelper.await(defaultDataLock) - assertNotNull(defaultKeyAccountData?.content) assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key")) - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_StoreSecret() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId = "My.Key" val info = generatedSecret(aliceSession, keyId, true) @@ -133,7 +116,7 @@ class QuadSTests : InstrumentedTest { // Store a secret val clearSecret = "42".toByteArray().toBase64NoPadding() - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.storeSecret( "secret.of.life", clearSecret, @@ -154,7 +137,7 @@ class QuadSTests : InstrumentedTest { // Try to decrypt?? - val decryptedSecret = mTestHelper.runBlockingTest { + val decryptedSecret = testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret( "secret.of.life", null, // default key @@ -163,32 +146,32 @@ class QuadSTests : InstrumentedTest { } assertEquals("Secret mismatch", clearSecret, decryptedSecret) - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_SetDefaultLocalEcho() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val quadS = aliceSession.sharedSecretStorageService val TEST_KEY_ID = "my.test.Key" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner) } // Test that we don't need to wait for an account data sync to access directly the keyid from DB - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.setDefaultKey(TEST_KEY_ID) } - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_StoreSecretWithMultipleKey() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId1 = "Key.1" val key1Info = generatedSecret(aliceSession, keyId1, true) val keyId2 = "Key2" @@ -196,7 +179,7 @@ class QuadSTests : InstrumentedTest { val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.storeSecret( "my.secret", mySecretText.toByteArray().toBase64NoPadding(), @@ -216,33 +199,33 @@ class QuadSTests : InstrumentedTest { assertNotNull(encryptedContent?.get(keyId2)) // Assert that can decrypt with both keys - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret("my.secret", keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!! ) } - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret("my.secret", keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!! ) } - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_GetSecretWithBadPassphrase() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId1 = "Key.1" val passphrase = "The good pass phrase" val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true) val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.storeSecret( "my.secret", mySecretText.toByteArray().toBase64NoPadding(), @@ -250,19 +233,23 @@ class QuadSTests : InstrumentedTest { ) } - mTestHelper.runBlockingTest { - aliceSession.sharedSecretStorageService.getSecret("my.secret", - keyId1, - RawBytesKeySpec.fromPassphrase( - "A bad passphrase", - key1Info.content?.passphrase?.salt ?: "", - key1Info.content?.passphrase?.iterations ?: 0, - null) - ) + testHelper.runBlockingTest { + try { + aliceSession.sharedSecretStorageService.getSecret("my.secret", + keyId1, + RawBytesKeySpec.fromPassphrase( + "A bad passphrase", + key1Info.content?.passphrase?.salt ?: "", + key1Info.content?.passphrase?.iterations ?: 0, + null) + ) + } catch (throwable: Throwable) { + assert(throwable is SharedSecretStorageError.BadMac) + } } // Now try with correct key - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret("my.secret", keyId1, RawBytesKeySpec.fromPassphrase( @@ -273,42 +260,36 @@ class QuadSTests : InstrumentedTest { ) } - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } - @Suppress("EXPERIMENTAL_API_USAGE") private fun assertAccountData(session: Session, type: String): UserAccountDataEvent { - val accountDataLock = CountDownLatch(1) var accountData: UserAccountDataEvent? = null - - val liveAccountData = runBlocking(Dispatchers.Main) { - session.accountDataService().getLiveUserAccountDataEvent(type) - } - val accountDataObserver = Observer?> { t -> - if (t?.getOrNull()?.type == type) { - accountData = t.getOrNull() - accountDataLock.countDown() + testHelper.waitWithLatch { + val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type) + val accountDataObserver = Observer?> { t -> + if (t?.getOrNull()?.type == type) { + accountData = t.getOrNull() + it.countDown() + } } + liveAccountData.observeForever(accountDataObserver) } - GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) } - mTestHelper.await(accountDataLock) - assertNotNull("Account Data type:$type should be found", accountData) - return accountData!! } private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { val quadS = session.sharedSecretStorageService - val creationInfo = mTestHelper.runBlockingTest { + val creationInfo = testHelper.runBlockingTest { quadS.generateKey(keyId, null, keyId, emptyKeySigner) } assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") if (asDefault) { - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.setDefaultKey(keyId) } assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID) @@ -320,7 +301,7 @@ class QuadSTests : InstrumentedTest { private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { val quadS = session.sharedSecretStorageService - val creationInfo = mTestHelper.runBlockingTest { + val creationInfo = testHelper.runBlockingTest { quadS.generateKeyWithPassphrase( keyId, keyId, @@ -331,7 +312,7 @@ class QuadSTests : InstrumentedTest { assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") if (asDefault) { - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.setDefaultKey(keyId) } assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index e0d49b3f5e..c914da6f71 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -53,12 +53,12 @@ import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SASTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_aliceStartThenAliceCancel() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -83,7 +83,7 @@ class SASTest : InstrumentedTest { val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!) assertNotNull("Alice should have a started transaction", aliceKeyTx) - mTestHelper.await(bobTxCreatedLatch) + testHelper.await(bobTxCreatedLatch) bobVerificationService.removeListener(bobListener) val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID) @@ -116,7 +116,7 @@ class SASTest : InstrumentedTest { bobVerificationService.addListener(bobListener2) aliceSasTx.cancel(CancelCode.User) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled) assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled) @@ -133,13 +133,13 @@ class SASTest : InstrumentedTest { assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)) assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID)) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_key_agreement_protocols_must_include_curve25519() { fail("Not passing for the moment") - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val bobSession = cryptoTestData.secondSession!! @@ -186,17 +186,17 @@ class SASTest : InstrumentedTest { fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_key_agreement_macs_Must_include_hmac_sha256() { fail("Not passing for the moment") - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val bobSession = cryptoTestData.secondSession!! @@ -223,18 +223,18 @@ class SASTest : InstrumentedTest { fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) val cancelReq = canceledToDeviceEvent!!.content.toModel()!! assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_key_agreement_short_code_include_decimal() { fail("Not passing for the moment") - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val bobSession = cryptoTestData.secondSession!! @@ -261,12 +261,12 @@ class SASTest : InstrumentedTest { fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) val cancelReq = canceledToDeviceEvent!!.content.toModel()!! assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } private fun fakeBobStart(bobSession: Session, @@ -303,7 +303,7 @@ class SASTest : InstrumentedTest { // If a device has two verifications in progress with the same device, then it should cancel both verifications. @Test fun test_aliceStartTwoRequests() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -332,10 +332,10 @@ class SASTest : InstrumentedTest { aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceCreatedLatch) - mTestHelper.await(aliceCancelledLatch) + testHelper.await(aliceCreatedLatch) + testHelper.await(aliceCancelledLatch) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -343,7 +343,7 @@ class SASTest : InstrumentedTest { */ @Test fun test_aliceAndBobAgreement() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -383,7 +383,7 @@ class SASTest : InstrumentedTest { val bobUserId = bobSession.myUserId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceAcceptedLatch) + testHelper.await(aliceAcceptedLatch) assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false) @@ -397,12 +397,12 @@ class SASTest : InstrumentedTest { assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it)) } - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_aliceAndBobSASCode() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -444,8 +444,8 @@ class SASTest : InstrumentedTest { val bobUserId = bobSession.myUserId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceSASLatch) - mTestHelper.await(bobSASLatch) + testHelper.await(aliceSASLatch) + testHelper.await(bobSASLatch) val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction @@ -453,12 +453,12 @@ class SASTest : InstrumentedTest { assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL), bobTx.getShortCodeRepresentation(SasMode.DECIMAL)) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_happyPath() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -520,8 +520,8 @@ class SASTest : InstrumentedTest { val bobUserId = bobSession.myUserId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceSASLatch) - mTestHelper.await(bobSASLatch) + testHelper.await(aliceSASLatch) + testHelper.await(bobSASLatch) // Assert that devices are verified val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId) @@ -532,12 +532,12 @@ class SASTest : InstrumentedTest { assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_ConcurrentStart() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -553,8 +553,8 @@ class SASTest : InstrumentedTest { var requestID: String? = null - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() requestID = prAlicePOV?.transactionId Log.v("TEST", "== alicePOV is $prAlicePOV") @@ -564,8 +564,8 @@ class SASTest : InstrumentedTest { Log.v("TEST", "== requestID is $requestID") - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() Log.v("TEST", "== prBobPOV is $prBobPOV") prBobPOV?.transactionId == requestID @@ -579,8 +579,8 @@ class SASTest : InstrumentedTest { ) // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() Log.v("TEST", "== prAlicePOV is $prAlicePOV") prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null @@ -606,22 +606,22 @@ class SASTest : InstrumentedTest { var alicePovTx: SasVerificationTransaction? var bobPovTx: SasVerificationTransaction? - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction Log.v("TEST", "== alicePovTx is $alicePovTx") alicePovTx?.state == VerificationTxState.ShortCodeReady } } // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction Log.v("TEST", "== bobPovTx is $bobPovTx") bobPovTx?.state == VerificationTxState.ShortCodeReady } } - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt index 397f7f9441..36306aa383 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt @@ -40,8 +40,8 @@ import kotlin.coroutines.resume @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) class VerificationTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) data class ExpectedResult( val sasIsSupported: Boolean = false, @@ -155,12 +155,12 @@ class VerificationTest : InstrumentedTest { bobSupportedMethods: List, expectedResultForAlice: ExpectedResult, expectedResultForBob: ExpectedResult) { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession!! - mTestHelper.doSync { callback -> + testHelper.doSync { callback -> aliceSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -176,7 +176,7 @@ class VerificationTest : InstrumentedTest { }, callback) } - mTestHelper.doSync { callback -> + testHelper.doSync { callback -> bobSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -234,7 +234,7 @@ class VerificationTest : InstrumentedTest { val bobUserId = bobSession.myUserId // Step 1: Alice starts a verification request aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId) - mTestHelper.await(latch) + testHelper.await(latch) aliceReadyPendingVerificationRequest!!.let { pr -> pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported @@ -248,6 +248,6 @@ class VerificationTest : InstrumentedTest { pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode } - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt index 1adf31be5f..8a4429db45 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt @@ -45,7 +45,7 @@ object RoomDataHelper { content: Content? = null, prevContent: Content? = null, sender: String = FAKE_TEST_SENDER, - stateKey: String = FAKE_TEST_SENDER + stateKey: String? = null ): Event { return Event( type = type, @@ -64,6 +64,6 @@ object RoomDataHelper { private fun createFakeRoomMemberEvent(): Event { val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() - return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember) + return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember, stateKey = FAKE_TEST_SENDER) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt index dfa6ec10ae..bc9722c922 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt @@ -65,14 +65,8 @@ class TimelineForwardPaginationTest : InstrumentedTest { message, numberOfMessagesToSend) - // Alice clear the cache - commonTestHelper.runBlockingTest { - aliceSession.clearCache() - } - - // And restarts the sync - aliceSession.startSync(true) - + // Alice clear the cache and restart the sync + commonTestHelper.clearCacheAndSync(aliceSession) val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30)) aliceTimeline.start() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt index 1baf490dfc..45e4b53c77 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -24,14 +24,10 @@ import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.message.MessageContent -import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestData import org.matrix.android.sdk.common.CryptoTestHelper -import java.util.concurrent.CountDownLatch @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) @@ -84,24 +80,12 @@ class SearchMessagesTest : InstrumentedTest { val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! - val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10)) - aliceTimeline.start() - - val lock = CountDownLatch(1) - - val eventListener = commonTestHelper.createEventListener(lock) { snapshot -> - snapshot.count { it.root.content.toModel()?.body?.startsWith(MESSAGE).orFalse() } == 2 - } - - aliceTimeline.addListener(eventListener) commonTestHelper.sendTextMessage( roomFromAlicePOV, MESSAGE, 2) - commonTestHelper.await(lock) - val data = commonTestHelper.runBlockingTest { block.invoke(cryptoTestData) } @@ -114,7 +98,6 @@ class SearchMessagesTest : InstrumentedTest { }.orFalse() ) - aliceTimeline.removeAllListeners() cryptoTestData.cleanUp(commonTestHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt index 5911414c25..d7be19295c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt @@ -16,9 +16,7 @@ package org.matrix.android.sdk.session.space -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -50,18 +48,15 @@ class SpaceCreationTest : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun createSimplePublicSpace() { val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true)) val roomName = "My Space" val topic = "A public space for test" var spaceId: String = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(roomName, topic, null, true) - // wait a bit to let the summary update it self :/ - it.countDown() - } + spaceId = session.spaceService().createSpace(roomName, topic, null, true) + // wait a bit to let the summary update it self :/ + it.countDown() } val syncedSpace = session.spaceService().getSpace(spaceId) @@ -134,7 +129,6 @@ class SpaceCreationTest : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testSimplePublicSpaceWithChildren() { val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true)) val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true)) @@ -148,50 +142,40 @@ class SpaceCreationTest : InstrumentedTest { // create a room var firstChild: String? = null commonTestHelper.waitWithLatch { - GlobalScope.launch { - firstChild = aliceSession.createRoom(CreateRoomParams().apply { - this.name = "FirstRoom" - this.topic = "Description of first room" - this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT - }) - it.countDown() - } + firstChild = aliceSession.createRoom(CreateRoomParams().apply { + this.name = "FirstRoom" + this.topic = "Description of first room" + this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT + }) + it.countDown() } commonTestHelper.waitWithLatch { - GlobalScope.launch { - syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true) - it.countDown() - } + syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true) + it.countDown() } var secondChild: String? = null commonTestHelper.waitWithLatch { - GlobalScope.launch { - secondChild = aliceSession.createRoom(CreateRoomParams().apply { - this.name = "SecondRoom" - this.topic = "Description of second room" - this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT - }) - it.countDown() - } + secondChild = aliceSession.createRoom(CreateRoomParams().apply { + this.name = "SecondRoom" + this.topic = "Description of second room" + this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT + }) + it.countDown() } commonTestHelper.waitWithLatch { - GlobalScope.launch { - syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true) - it.countDown() - } + syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true) + it.countDown() } // Try to join from bob, it's a public space no need to invite var joinResult: JoinSpaceResult? = null commonTestHelper.waitWithLatch { - GlobalScope.launch { - joinResult = bobSession.spaceService().joinSpace(spaceId) - // wait a bit to let the summary update it self :/ - it.countDown() - } + joinResult = bobSession.spaceService().joinSpace(spaceId) + // wait a bit to let the summary update it self :/ + it.countDown() } assertEquals(JoinSpaceResult.Success, joinResult) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 436daf001b..1c38edbbd9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -18,9 +18,6 @@ package org.matrix.android.sdk.session.space import android.util.Log import androidx.lifecycle.Observer -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -56,43 +53,34 @@ class SpaceHierarchyTest : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun createCanonicalChildRelation() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val spaceName = "My Space" val topic = "A public space for test" - var spaceId: String = "" + var spaceId = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(spaceName, topic, null, true) - it.countDown() - } + spaceId = session.spaceService().createSpace(spaceName, topic, null, true) + it.countDown() } val syncedSpace = session.spaceService().getSpace(spaceId) - var roomId: String = "" + var roomId = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - roomId = session.createRoom(CreateRoomParams().apply { name = "General" }) - it.countDown() - } + roomId = session.createRoom(CreateRoomParams().apply { name = "General" }) + it.countDown() } val viaServers = listOf(session.sessionParams.homeServerHost ?: "") commonTestHelper.waitWithLatch { - GlobalScope.launch { - syncedSpace!!.addChildren(roomId, viaServers, null, true) - it.countDown() - } + syncedSpace!!.addChildren(roomId, viaServers, null, true) + it.countDown() } commonTestHelper.waitWithLatch { - GlobalScope.launch { - session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers) - it.countDown() - } + session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers) + it.countDown() } Thread.sleep(9000) @@ -181,7 +169,6 @@ class SpaceHierarchyTest : InstrumentedTest { // } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testFilteringBySpace() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -205,29 +192,23 @@ class SpaceHierarchyTest : InstrumentedTest { val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId) val viaServers = listOf(session.sessionParams.homeServerHost ?: "") commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) - session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) - it.countDown() - } + spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) + session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) + it.countDown() } // Create orphan rooms var orphan1 = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" }) - it.countDown() - } + orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" }) + it.countDown() } var orphan2 = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" }) - it.countDown() - } + orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" }) + it.countDown() } val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) }) @@ -250,11 +231,9 @@ class SpaceHierarchyTest : InstrumentedTest { // Add a non canonical child and check that it does not appear as orphan commonTestHelper.waitWithLatch { - GlobalScope.launch { - val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" }) - spaceA!!.addChildren(a3, viaServers, null, false) - it.countDown() - } + val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" }) + spaceA!!.addChildren(a3, viaServers, null, false) + it.countDown() } Thread.sleep(2_000) @@ -265,7 +244,6 @@ class SpaceHierarchyTest : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testBreakCycle() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -283,20 +261,16 @@ class SpaceHierarchyTest : InstrumentedTest { val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId) val viaServers = listOf(session.sessionParams.homeServerHost ?: "") commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) - session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) - it.countDown() - } + spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) + session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) + it.countDown() } // add back A as subspace of C commonTestHelper.waitWithLatch { - GlobalScope.launch { - val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId) - spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true) - it.countDown() - } + val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId) + spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true) + it.countDown() } Thread.sleep(1000) @@ -313,7 +287,6 @@ class SpaceHierarchyTest : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testLiveFlatChildren() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -336,12 +309,14 @@ class SpaceHierarchyTest : InstrumentedTest { session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers) } - val flatAChildren = runBlocking(Dispatchers.Main) { - session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) - } + val spaceCInfo = createPublicSpace(session, "SpaceC", listOf( + Triple("C1", true /*auto-join*/, true/*canonical*/), + Triple("C2", true, true) + )) commonTestHelper.waitWithLatch { latch -> + val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) val childObserver = object : Observer> { override fun onChanged(children: List?) { // Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}") @@ -354,20 +329,13 @@ class SpaceHierarchyTest : InstrumentedTest { } } - val spaceCInfo = createPublicSpace(session, "SpaceC", listOf( - Triple("C1", true /*auto-join*/, true/*canonical*/), - Triple("C2", true, true) - )) - // add C as subspace of B - runBlocking { - val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId) - spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) - } + val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId) + spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) // C1 and C2 should be in flatten child of A now - GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) } + flatAChildren.observeForever(childObserver) } // Test part one of the rooms @@ -376,7 +344,7 @@ class SpaceHierarchyTest : InstrumentedTest { val bRoom = session.getRoom(bRoomId) commonTestHelper.waitWithLatch { latch -> - + val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) val childObserver = object : Observer> { override fun onChanged(children: List?) { System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}") @@ -389,13 +357,10 @@ class SpaceHierarchyTest : InstrumentedTest { } // part from b room - runBlocking { - bRoom!!.leave(null) - } + bRoom!!.leave(null) // The room should have disapear from flat children - GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) } + flatAChildren.observeForever(childObserver) } - commonTestHelper.signOutAndClose(session) } @@ -404,94 +369,66 @@ class SpaceHierarchyTest : InstrumentedTest { val roomIds: List ) - @Suppress("EXPERIMENTAL_API_USAGE") private fun createPublicSpace(session: Session, spaceName: String, childInfo: List> /** Name, auto-join, canonical*/ ): TestSpaceCreationResult { var spaceId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true) - it.countDown() + var roomIds: List = emptyList() + commonTestHelper.waitWithLatch { latch -> + spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true) + val syncedSpace = session.spaceService().getSpace(spaceId) + val viaServers = listOf(session.sessionParams.homeServerHost ?: "") + + roomIds = childInfo.map { entry -> + session.createRoom(CreateRoomParams().apply { name = entry.first }) } - } - - val syncedSpace = session.spaceService().getSpace(spaceId) - val viaServers = listOf(session.sessionParams.homeServerHost ?: "") - - val roomIds = - childInfo.map { entry -> - var roomId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - roomId = session.createRoom(CreateRoomParams().apply { name = entry.first }) - it.countDown() - } - } - roomId - } - - roomIds.forEachIndexed { index, roomId -> - runBlocking { + roomIds.forEachIndexed { index, roomId -> syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) val canonical = childInfo[index].third if (canonical != null) { session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) } } + latch.countDown() } return TestSpaceCreationResult(spaceId, roomIds) } - @Suppress("EXPERIMENTAL_API_USAGE") private fun createPrivateSpace(session: Session, spaceName: String, childInfo: List> /** Name, auto-join, canonical*/ ): TestSpaceCreationResult { var spaceId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false) - it.countDown() - } - } - - val syncedSpace = session.spaceService().getSpace(spaceId) - val viaServers = listOf(session.sessionParams.homeServerHost ?: "") - - val roomIds = - childInfo.map { entry -> - var roomId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - val homeServerCapabilities = session - .getHomeServerCapabilities() - roomId = session.createRoom(CreateRoomParams().apply { - name = entry.first - this.featurePreset = RestrictedRoomPreset( - homeServerCapabilities, - listOf( - RoomJoinRulesAllowEntry.restrictedToRoom(spaceId) - ) - ) - }) - it.countDown() - } + var roomIds: List = emptyList() + commonTestHelper.waitWithLatch { latch -> + spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false) + val syncedSpace = session.spaceService().getSpace(spaceId) + val viaServers = listOf(session.sessionParams.homeServerHost ?: "") + roomIds = + childInfo.map { entry -> + val homeServerCapabilities = session + .getHomeServerCapabilities() + session.createRoom(CreateRoomParams().apply { + name = entry.first + this.featurePreset = RestrictedRoomPreset( + homeServerCapabilities, + listOf( + RoomJoinRulesAllowEntry.restrictedToRoom(spaceId) + ) + ) + }) } - roomId - } - - roomIds.forEachIndexed { index, roomId -> - runBlocking { + roomIds.forEachIndexed { index, roomId -> syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) val canonical = childInfo[index].third if (canonical != null) { session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) } } + latch.countDown() } return TestSpaceCreationResult(spaceId, roomIds) } @@ -559,11 +496,9 @@ class SpaceHierarchyTest : InstrumentedTest { var bobRoomId = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) - bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) - it.countDown() - } + bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) + bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) + it.countDown() } commonTestHelper.runBlockingTest { @@ -577,10 +512,8 @@ class SpaceHierarchyTest : InstrumentedTest { } commonTestHelper.waitWithLatch { - GlobalScope.launch { - bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) - it.countDown() - } + bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) + it.countDown() } commonTestHelper.waitWithLatch { latch -> @@ -600,19 +533,17 @@ class SpaceHierarchyTest : InstrumentedTest { // Let's now try to make alice admin of the room commonTestHelper.waitWithLatch { - GlobalScope.launch { - val room = bobSession.getRoom(bobRoomId)!! - val currentPLContent = room - .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) - ?.let { it.content.toModel() } + val room = bobSession.getRoom(bobRoomId)!! + val currentPLContent = room + .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + ?.let { it.content.toModel() } - val newPowerLevelsContent = currentPLContent - ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) - ?.toContent() + val newPowerLevelsContent = currentPLContent + ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) + ?.toContent() - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) - it.countDown() - } + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) + it.countDown() } commonTestHelper.waitWithLatch { latch -> @@ -627,10 +558,8 @@ class SpaceHierarchyTest : InstrumentedTest { } commonTestHelper.waitWithLatch { - GlobalScope.launch { - aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) - it.countDown() - } + aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) + it.countDown() } commonTestHelper.waitWithLatch { latch -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt index 8a4526a5e1..901ba75d16 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt @@ -20,6 +20,7 @@ import android.content.Context import androidx.lifecycle.ProcessLifecycleOwner import androidx.work.Configuration import androidx.work.WorkManager +import androidx.work.WorkerFactory import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.api.auth.AuthenticationService @@ -33,6 +34,7 @@ import org.matrix.android.sdk.internal.di.DaggerMatrixComponent import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import org.matrix.olm.OlmManager import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicBoolean @@ -53,12 +55,17 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo @Inject internal lateinit var sessionManager: SessionManager @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var apiInterceptor: ApiInterceptor + @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory init { Monarchy.init(context) DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this) if (context.applicationContext !is Configuration.Provider) { - WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) + val configuration = Configuration.Builder() + .setExecutor(Executors.newCachedThreadPool()) + .setWorkerFactory(matrixWorkerFactory) + .build() + WorkManager.initialize(context, configuration) } ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) } @@ -77,6 +84,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo return legacySessionImporter } + fun workerFactory(): WorkerFactory = matrixWorkerFactory + fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) { apiInterceptor.addListener(path, listener) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt index c11d00278b..3a5f8e7668 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -34,9 +35,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject -internal class CancelGossipRequestWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class CancelGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt index b2ba189b65..3129ccae3b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest @@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import timber.log.Timber import javax.inject.Inject -internal class SendGossipRequestWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt index b96943e4ae..ff206a3c96 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap @@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import timber.log.Timber import javax.inject.Inject -internal class SendGossipWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendGossipWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt index 3326d3707a..5cd647ff6f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt @@ -25,6 +25,7 @@ import io.realm.kotlin.where import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity @@ -50,9 +51,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import timber.log.Timber import javax.inject.Inject -internal class UpdateTrustWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt index 481ce85f70..a763c05e07 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt @@ -20,6 +20,7 @@ import androidx.work.Data import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.shouldBeRetried +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker @@ -33,9 +34,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class SendVerificationMessageWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendVerificationMessageWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt index 81a067f2c0..d9a4f1bde1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt @@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.session.TestInterceptor import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.system.SystemModule +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import org.matrix.olm.OlmManager import java.io.File @@ -86,6 +87,8 @@ internal interface MatrixComponent { fun sessionManager(): SessionManager + fun matrixWorkerFactory(): MatrixWorkerFactory + fun inject(matrix: Matrix) @Component.Factory diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt index 210eadeff7..b136041f79 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt @@ -20,6 +20,8 @@ import dagger.Module import dagger.Provides import org.matrix.android.sdk.internal.session.MockHttpInterceptor import org.matrix.android.sdk.internal.session.TestInterceptor +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.util.DefaultBackgroundDetectionObserver @Module internal object NoOpTestModule { @@ -28,4 +30,11 @@ internal object NoOpTestModule { @JvmStatic @MockHttpInterceptor fun providesTestInterceptor(): TestInterceptor? = null + + @Provides + @JvmStatic + @MatrixScope + fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver { + return DefaultBackgroundDetectionObserver() + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt index bafffdf852..7d004bc5c0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt @@ -17,24 +17,38 @@ package org.matrix.android.sdk.internal.di import android.content.Context +import androidx.lifecycle.Observer import androidx.work.Constraints import androidx.work.ListenableWorker import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo import androidx.work.WorkManager import androidx.work.WorkRequest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import java.util.concurrent.TimeUnit import javax.inject.Inject +@SessionScope internal class WorkManagerProvider @Inject constructor( context: Context, - @SessionId private val sessionId: String + @SessionId private val sessionId: String, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val sessionScope: CoroutineScope ) { private val tag = MATRIX_SDK_TAG_PREFIX + sessionId val workManager = WorkManager.getInstance(context) + init { + checkIfWorkerFactoryIsSetup() + } + /** * Create a OneTimeWorkRequestBuilder, with the Matrix SDK tag */ @@ -60,6 +74,27 @@ internal class WorkManagerProvider @Inject constructor( } } + private fun checkIfWorkerFactoryIsSetup() { + sessionScope.launch(coroutineDispatchers.main) { + val checkWorkerRequest = OneTimeWorkRequestBuilder().build() + workManager.enqueue(checkWorkerRequest) + val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id) + val observer = object : Observer { + override fun onChanged(workInfo: WorkInfo) { + if (workInfo.state.isFinished) { + checkWorkerLiveState.removeObserver(this) + if (workInfo.state == WorkInfo.State.FAILED) { + throw RuntimeException("MatrixWorkerFactory is not being set on your worker configuration.\n" + + "Makes sure to add it to a DelegatingWorkerFactory if you have your own factory or use it directly.\n" + + "You can grab the instance through the Matrix class.") + } + } + } + } + checkWorkerLiveState.observeForever(observer) + } + } + companion object { private const val MATRIX_SDK_TAG_PREFIX = "MatrixSDK-" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index 7f35c91010..52dee0ee55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.util.MimeTypes +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import org.matrix.android.sdk.internal.database.mapper.ContentMapper @@ -63,8 +64,8 @@ private data class NewAttachmentAttributes( * Possible previous worker: None * Possible next worker : Always [MultipleEventSendingDispatcherWorker] */ -internal class UploadContentWorker(val context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class UploadContentWorker(val context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt index 338f43bdbb..716859f195 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.group import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams @@ -28,8 +29,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class GetGroupDataWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt index 4df42b2cfb..ce29efaaac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt @@ -19,13 +19,14 @@ import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject -internal class AddPusherWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class AddPusherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt index 16a9eba363..f44c255f1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt @@ -21,6 +21,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.content.UploadContentWorker @@ -38,8 +39,8 @@ import javax.inject.Inject * Possible previous worker: Always [UploadContentWorker] * Possible next worker : None, but it will post new work to send events, encrypted or not */ -internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt index b4436bfcbf..c03d1fa81e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt @@ -19,6 +19,7 @@ import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.SessionComponent @@ -32,8 +33,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class RedactEventWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class RedactEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index 8b7fe4b907..7f24688ece 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -23,6 +23,7 @@ import io.realm.RealmConfiguration import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.tasks.SendEventTask import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.SessionComponent @@ -38,9 +39,8 @@ import javax.inject.Inject * Possible previous worker: [EncryptEventWorker] or first worker * Possible next worker : None */ -internal class SendEventWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt index 35e561a106..869a4d425a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt @@ -25,9 +25,12 @@ import javax.inject.Inject internal class SyncTokenStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) { fun getLastToken(): String? { - return Realm.getInstance(monarchy.realmConfiguration).use { + val token = Realm.getInstance(monarchy.realmConfiguration).use { + // Makes sure realm is up-to-date as it's used for querying internally on non looper thread. + it.refresh() it.where(SyncEntity::class.java).findFirst()?.nextBatch } + return token } fun saveToken(realm: Realm, token: String?) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt index 41bb1a44a6..763cd55714 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt @@ -21,8 +21,8 @@ import androidx.work.ExistingWorkPolicy import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.isTokenError +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.di.WorkManagerProvider -import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.sync.SyncPresence import org.matrix.android.sdk.internal.session.sync.SyncTask @@ -41,9 +41,8 @@ private const val DEFAULT_DELAY_TIMEOUT = 30_000L * Possible previous worker: None * Possible next worker : None */ -internal class SyncWorker(context: Context, - workerParameters: WorkerParameters -) : SessionSafeCoroutineWorker(context, workerParameters, Params::class.java) { +internal class SyncWorker(context: Context, workerParameters: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, workerParameters, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( @@ -56,7 +55,6 @@ internal class SyncWorker(context: Context, @Inject lateinit var syncTask: SyncTask @Inject lateinit var taskExecutor: TaskExecutor - @Inject lateinit var networkConnectivityChecker: NetworkConnectivityChecker @Inject lateinit var workManagerProvider: WorkManagerProvider override fun injectWith(injector: SessionComponent) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt index 3e977b31fb..9c8b36a3ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt @@ -18,26 +18,32 @@ package org.matrix.android.sdk.internal.util import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import org.matrix.android.sdk.internal.di.MatrixScope import timber.log.Timber -import javax.inject.Inject -/** - * To be attached to ProcessLifecycleOwner lifecycle - */ -@MatrixScope -internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecycleObserver { +interface BackgroundDetectionObserver : DefaultLifecycleObserver { + val isInBackground: Boolean - var isInBackground: Boolean = true + fun register(listener: Listener) + fun unregister(listener: Listener) + + interface Listener { + fun onMoveToForeground() + fun onMoveToBackground() + } +} + +internal class DefaultBackgroundDetectionObserver : BackgroundDetectionObserver { + + override var isInBackground: Boolean = true private set - private val listeners = LinkedHashSet() + private val listeners = LinkedHashSet() - fun register(listener: Listener) { + override fun register(listener: BackgroundDetectionObserver.Listener) { listeners.add(listener) } - fun unregister(listener: Listener) { + override fun unregister(listener: BackgroundDetectionObserver.Listener) { listeners.remove(listener) } @@ -52,9 +58,4 @@ internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecy isInBackground = true listeners.forEach { it.onMoveToBackground() } } - - interface Listener { - fun onMoveToForeground() - fun onMoveToBackground() - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt index b58cab99b5..0b451e9c34 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt @@ -17,16 +17,33 @@ package org.matrix.android.sdk.internal.worker import android.content.Context +import androidx.work.CoroutineWorker import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters +import org.matrix.android.sdk.internal.SessionManager +import org.matrix.android.sdk.internal.crypto.CancelGossipRequestWorker +import org.matrix.android.sdk.internal.crypto.SendGossipRequestWorker +import org.matrix.android.sdk.internal.crypto.SendGossipWorker +import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker +import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker +import org.matrix.android.sdk.internal.di.MatrixScope +import org.matrix.android.sdk.internal.session.content.UploadContentWorker +import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker +import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker +import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker +import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker +import org.matrix.android.sdk.internal.session.room.send.SendEventWorker +import org.matrix.android.sdk.internal.session.sync.job.SyncWorker import timber.log.Timber import javax.inject.Inject -import javax.inject.Provider -class MatrixWorkerFactory @Inject constructor( - private val workerFactories: Map, @JvmSuppressWildcards Provider> -) : WorkerFactory() { +/** + * This factory is responsible of creating Workers by giving the session manager. + * This is not the cleanest way but getting SessionComponent is dependant of args type. + */ +@MatrixScope +internal class MatrixWorkerFactory @Inject constructor(private val sessionManager: SessionManager) : WorkerFactory() { override fun createWorker( appContext: Context, @@ -34,11 +51,61 @@ class MatrixWorkerFactory @Inject constructor( workerParameters: WorkerParameters ): ListenableWorker? { Timber.d("MatrixWorkerFactory.createWorker for $workerClassName") + return when (workerClassName) { + CheckFactoryWorker::class.java.name -> + CheckFactoryWorker(appContext, workerParameters, true) + AddPusherWorker::class.java.name -> + AddPusherWorker(appContext, workerParameters, sessionManager) + CancelGossipRequestWorker::class.java.name -> + CancelGossipRequestWorker(appContext, workerParameters, sessionManager) + GetGroupDataWorker::class.java.name -> + GetGroupDataWorker(appContext, workerParameters, sessionManager) + MultipleEventSendingDispatcherWorker::class.java.name -> + MultipleEventSendingDispatcherWorker(appContext, workerParameters, sessionManager) + RedactEventWorker::class.java.name -> + RedactEventWorker(appContext, workerParameters, sessionManager) + SendEventWorker::class.java.name -> + SendEventWorker(appContext, workerParameters, sessionManager) + SendGossipRequestWorker::class.java.name -> + SendGossipRequestWorker(appContext, workerParameters, sessionManager) + SendGossipWorker::class.java.name -> + SendGossipWorker(appContext, workerParameters, sessionManager) + SendVerificationMessageWorker::class.java.name -> + SendVerificationMessageWorker(appContext, workerParameters, sessionManager) + SyncWorker::class.java.name -> + SyncWorker(appContext, workerParameters, sessionManager) + UpdateTrustWorker::class.java.name -> + UpdateTrustWorker(appContext, workerParameters, sessionManager) + UploadContentWorker::class.java.name -> + UploadContentWorker(appContext, workerParameters, sessionManager) + else -> { + Timber.w("No worker defined on MatrixWorkerFactory for $workerClassName will delegate to default.") + // Return null to delegate to the default WorkerFactory. + null + } + } + } - val foundEntry = - workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) } - val factoryProvider = foundEntry?.value - ?: throw IllegalArgumentException("unknown worker class name: $workerClassName") - return factoryProvider.get().create(appContext, workerParameters) + /** + * This worker is launched by the factory with the isCreatedByMatrixWorkerFactory flag to true. + * If the MatrixWorkerFactory is not set up, it will default to the other constructor and it will throw + */ + class CheckFactoryWorker(context: Context, + workerParameters: WorkerParameters, + private val isCreatedByMatrixWorkerFactory: Boolean) : + CoroutineWorker(context, workerParameters) { + + // Called by WorkManager if there is no MatrixWorkerFactory + constructor(context: Context, workerParameters: WorkerParameters) : this(context, + workerParameters, + isCreatedByMatrixWorkerFactory = false) + + override suspend fun doWork(): Result { + return if (!isCreatedByMatrixWorkerFactory) { + Result.failure() + } else { + Result.success() + } + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt index d4179e2272..334c4580e9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt @@ -22,6 +22,7 @@ import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.session.SessionComponent import timber.log.Timber @@ -33,6 +34,7 @@ import timber.log.Timber internal abstract class SessionSafeCoroutineWorker( context: Context, workerParameters: WorkerParameters, + private val sessionManager: SessionManager, private val paramClass: Class ) : CoroutineWorker(context, workerParameters) { @@ -48,7 +50,7 @@ internal abstract class SessionSafeCoroutineWorker( .also { Timber.e("Unable to parse work parameters") } return try { - val sessionComponent = getSessionComponent(params.sessionId) + val sessionComponent = sessionManager.getSessionComponent(params.sessionId) ?: return buildErrorResult(params, "No session") // Make sure to inject before handling error as you may need some dependencies to process them. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/Worker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/Worker.kt deleted file mode 100644 index 24035355f7..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/Worker.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * 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 org.matrix.android.sdk.internal.worker - -import androidx.work.ListenableWorker -import org.matrix.android.sdk.api.Matrix -import org.matrix.android.sdk.internal.session.SessionComponent - -internal fun ListenableWorker.getSessionComponent(sessionId: String): SessionComponent? { - return Matrix.getInstance(applicationContext).sessionManager.getSessionComponent(sessionId) -} diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 46f155a8d9..512f579785 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -220,6 +220,7 @@ class VectorApplication : override fun getWorkManagerConfiguration(): WorkConfiguration { return WorkConfiguration.Builder() + .setWorkerFactory(Matrix.getInstance(this.appContext).workerFactory()) .setExecutor(Executors.newCachedThreadPool()) .build() }