Merge pull request #4568 from vector-im/feature/fga/fix_sdk_integration_tests

Feature/fga/fix sdk integration tests
This commit is contained in:
Benoit Marty 2021-12-03 17:11:33 +01:00 committed by GitHub
commit e60d053f84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 906 additions and 934 deletions

1
changelog.d/4546.bugfix Normal file
View file

@ -0,0 +1 @@
Fix lots of integration tests by introducing TestMatrix class and MatrixWorkerFactory.

View file

@ -20,9 +20,10 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.test.internal.runner.junit4.statement.UiThreadStatement 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.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -30,7 +31,6 @@ import kotlinx.coroutines.withTimeout
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue 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.MatrixCallback
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig 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.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.sync.SyncState
import java.util.ArrayList import timber.log.Timber
import java.util.UUID import java.util.UUID
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -56,13 +56,14 @@ import java.util.concurrent.TimeUnit
*/ */
class CommonTestHelper(context: Context) { 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 { init {
UiThreadStatement.runOnUiThread { UiThreadStatement.runOnUiThread {
Matrix.initialize( TestMatrix.initialize(
context, context,
MatrixConfiguration( MatrixConfiguration(
applicationFlavor = "TestFlavor", 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 { fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {
@ -95,33 +96,47 @@ class CommonTestHelper(context: Context) {
* *
* @param session the session to sync * @param session the session to sync
*/ */
@Suppress("EXPERIMENTAL_API_USAGE") fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) {
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) {
val lock = CountDownLatch(1) val lock = CountDownLatch(1)
coroutineScope.launch {
val job = GlobalScope.launch(Dispatchers.Main) { session.startSync(true)
session.open() val syncLiveData = session.getSyncStateLive()
} val syncObserver = object : Observer<SyncState> {
runBlocking { job.join() } override fun onChanged(t: SyncState?) {
if (session.hasAlreadySynced()) {
session.startSync(true) lock.countDown()
syncLiveData.removeObserver(this)
val syncLiveData = runBlocking(Dispatchers.Main) { }
session.getSyncStateLive()
}
val syncObserver = object : Observer<SyncState> {
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) 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<SyncState> {
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 * 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 * @param nbOfMessages the number of time the message will be sent
*/ */
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> { fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List<TimelineEvent> {
val timeline = room.createTimeline(null, TimelineSettings(10))
val sentEvents = ArrayList<TimelineEvent>(nbOfMessages) val sentEvents = ArrayList<TimelineEvent>(nbOfMessages)
val latch = CountDownLatch(1) val timeline = room.createTimeline(null, TimelineSettings(10))
val timelineListener = object : Timeline.Listener { timeline.start()
override fun onTimelineFailure(throwable: Throwable) { waitWithLatch(timeout + 1_000L * nbOfMessages) { latch ->
} val timelineListener = object : Timeline.Listener {
override fun onTimelineFailure(throwable: Throwable) {
}
override fun onNewTimelineEvents(eventIds: List<String>) { override fun onNewTimelineEvents(eventIds: List<String>) {
// noop // noop
} }
override fun onTimelineUpdated(snapshot: List<TimelineEvent>) { override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
val newMessages = snapshot val newMessages = snapshot
.filter { it.root.sendState == SendState.SYNCED } .filter { it.root.sendState == SendState.SYNCED }
.filter { it.root.getClearType() == EventType.MESSAGE } .filter { it.root.getClearType() == EventType.MESSAGE }
.filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true } .filter { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(message) == true }
if (newMessages.size == nbOfMessages) { Timber.v("New synced message size: ${newMessages.size}")
sentEvents.addAll(newMessages) if (newMessages.size == nbOfMessages) {
// Remove listener now, if not at the next update sendEvents could change sentEvents.addAll(newMessages)
timeline.removeListener(this) // Remove listener now, if not at the next update sendEvents could change
latch.countDown() 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() timeline.dispose()
// Check that all events has been created // Check that all events has been created
assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong()) assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong())
return sentEvents 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 ***************************************************************************** // PRIVATE METHODS *****************************************************************************
/** /**
@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) {
assertTrue(registrationResult is RegistrationResult.Success) assertTrue(registrationResult is RegistrationResult.Success)
val session = (registrationResult as RegistrationResult.Success).session val session = (registrationResult as RegistrationResult.Success).session
session.open()
if (sessionTestParams.withInitialSync) { if (sessionTestParams.withInitialSync) {
syncSession(session, 60_000) syncSession(session, 60_000)
} }
return session return session
} }
@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) {
.getLoginWizard() .getLoginWizard()
.login(userName, password, "myDevice") .login(userName, password, "myDevice")
} }
session.open()
if (sessionTestParams.withInitialSync) { if (sessionTestParams.withInitialSync) {
syncSession(session) syncSession(session)
} }
@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) {
assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)) assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
} }
@Suppress("EXPERIMENTAL_API_USAGE") suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { while (true) {
GlobalScope.launch { delay(1000)
while (true) { if (condition()) {
delay(1000) latch.countDown()
if (condition()) { return
latch.countDown()
return@launch
}
} }
} }
} }
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) val latch = CountDownLatch(1)
block(latch) coroutineScope.launch(dispatcher) {
block(latch)
}
await(latch, timeout) await(latch, timeout)
} }

View file

@ -19,10 +19,6 @@ package org.matrix.android.sdk.common
import android.os.SystemClock import android.os.SystemClock
import android.util.Log import android.util.Log
import androidx.lifecycle.Observer 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.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull 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.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse 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.Session
import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction 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.RoomSummary
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams 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.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
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP 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.MegolmBackupAuthData
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
import java.util.UUID import java.util.UUID
import java.util.concurrent.CountDownLatch
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { class CryptoTestHelper(private val testHelper: CommonTestHelper) {
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!") private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.") private val messagesFromBob: List<String> = 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 * @return alice session
*/ */
fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData { 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" }) aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
} }
if (encryptedRoom) { if (encryptedRoom) {
val room = aliceSession.getRoom(roomId)!! testHelper.waitWithLatch { latch ->
val room = aliceSession.getRoom(roomId)!!
mTestHelper.runBlockingTest {
room.enableEncryption() room.enableEncryption()
val roomSummaryLive = room.getRoomSummaryLive()
val roomSummaryObserver = object : Observer<Optional<RoomSummary>> {
override fun onChanged(roomSummary: Optional<RoomSummary>) {
if (roomSummary.getOrNull()?.isEncrypted.orFalse()) {
roomSummaryLive.removeObserver(this)
latch.countDown()
}
}
}
roomSummaryLive.observeForever(roomSummaryObserver)
} }
} }
return CryptoTestData(roomId, listOf(aliceSession)) return CryptoTestData(roomId, listOf(aliceSession))
} }
/** /**
* @return alice and bob sessions * @return alice and bob sessions
*/ */
@Suppress("EXPERIMENTAL_API_USAGE")
fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData { fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData {
val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom) val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom)
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
@ -92,54 +95,37 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
val aliceRoom = aliceSession.getRoom(aliceRoomId)!! 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) testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { val newRoomObserver = object : Observer<List<RoomSummary>> {
bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) override fun onChanged(t: List<RoomSummary>?) {
} if (t?.isNotEmpty() == true) {
bobRoomSummariesLive.removeObserver(this)
val newRoomObserver = object : Observer<List<RoomSummary>> { latch.countDown()
override fun onChanged(t: List<RoomSummary>?) { }
if (t?.isNotEmpty() == true) {
lock1.countDown()
bobRoomSummariesLive.removeObserver(this)
} }
} }
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver) bobRoomSummariesLive.observeForever(newRoomObserver)
}
mTestHelper.runBlockingTest {
aliceRoom.invite(bobSession.myUserId) aliceRoom.invite(bobSession.myUserId)
} }
mTestHelper.await(lock1) testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { })
val lock = CountDownLatch(1) val roomJoinedObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) {
val roomJoinedObserver = object : Observer<List<RoomSummary>> { if (bobSession.getRoom(aliceRoomId)
override fun onChanged(t: List<RoomSummary>?) { ?.getRoomMember(bobSession.myUserId)
if (bobSession.getRoom(aliceRoomId) ?.membership == Membership.JOIN) {
?.getRoomMember(aliceSession.myUserId) bobRoomSummariesLive.removeObserver(this)
?.membership == Membership.JOIN) { latch.countDown()
lock.countDown() }
bobRoomSummariesLive.removeObserver(this)
} }
} }
}
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(roomJoinedObserver) bobRoomSummariesLive.observeForever(roomJoinedObserver)
bobSession.joinRoom(aliceRoomId)
} }
mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) }
mTestHelper.await(lock)
// Ensure bob can send messages to the room // Ensure bob can send messages to the room
// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! // val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
// assertNotNull(roomFromBobPOV.powerLevels) // assertNotNull(roomFromBobPOV.powerLevels)
@ -171,13 +157,13 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
* @Return Sam session * @Return Sam session
*/ */
fun createSamAccountAndInviteToTheRoom(room: Room): 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) room.invite(samSession.myUserId, null)
} }
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
samSession.joinRoom(room.roomId, null, emptyList()) samSession.joinRoom(room.roomId, null, emptyList())
} }
@ -194,23 +180,20 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
val bobSession = cryptoTestData.secondSession!! val bobSession = cryptoTestData.secondSession!!
bobSession.cryptoService().setWarnOnUnknownDevices(false) bobSession.cryptoService().setWarnOnUnknownDevices(false)
aliceSession.cryptoService().setWarnOnUnknownDevices(false) aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!!
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!!
// Alice sends a message // Alice sends a message
mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1) testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1)
// roomFromAlicePOV.sendTextMessage(messagesFromAlice[0])
// Bob send 3 messages // Bob send 3 messages
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1) testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1)
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1) testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1)
mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1) testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1)
// Alice sends a message // Alice sends a message
mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1) testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1)
return cryptoTestData return cryptoTestData
} }
@ -256,60 +239,44 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
) )
} }
@Suppress("EXPERIMENTAL_API_USAGE")
fun createDM(alice: Session, bob: Session): String { fun createDM(alice: Session, bob: Session): String {
val roomId = mTestHelper.runBlockingTest { var roomId: String = ""
alice.createDirectRoom(bob.myUserId) testHelper.waitWithLatch { latch ->
} roomId = alice.createDirectRoom(bob.myUserId)
val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
mTestHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) {
bob.getRoomSummariesLive(roomSummaryQueryParams { })
}
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1 val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1
if (indexOfFirst != -1) { if (indexOfFirst != -1) {
latch.countDown()
bobRoomSummariesLive.removeObserver(this) bobRoomSummariesLive.removeObserver(this)
latch.countDown()
} }
} }
} }
bobRoomSummariesLive.observeForever(newRoomObserver)
GlobalScope.launch(Dispatchers.Main) {
bobRoomSummariesLive.observeForever(newRoomObserver)
}
} }
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { })
bob.getRoomSummariesLive(roomSummaryQueryParams { })
}
val newRoomObserver = object : Observer<List<RoomSummary>> { val newRoomObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(t: List<RoomSummary>?) { override fun onChanged(t: List<RoomSummary>?) {
if (bob.getRoom(roomId) if (bob.getRoom(roomId)
?.getRoomMember(bob.myUserId) ?.getRoomMember(bob.myUserId)
?.membership == Membership.JOIN) { ?.membership == Membership.JOIN) {
latch.countDown()
bobRoomSummariesLive.removeObserver(this) bobRoomSummariesLive.removeObserver(this)
latch.countDown()
} }
} }
} }
bobRoomSummariesLive.observeForever(newRoomObserver)
GlobalScope.launch(Dispatchers.Main) { bob.joinRoom(roomId)
bobRoomSummariesLive.observeForever(newRoomObserver)
}
mTestHelper.runBlockingTest { bob.joinRoom(roomId) }
} }
return roomId return roomId
} }
fun initializeCrossSigning(session: Session) { fun initializeCrossSigning(session: Session) {
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
session.cryptoService().crossSigningService() session.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -346,8 +313,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
var bobPovTx: IncomingSasVerificationTransaction? = null var bobPovTx: IncomingSasVerificationTransaction? = null
// wait for alice to get the ready // wait for alice to get the ready
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
if (bobPovTx?.state == VerificationTxState.OnStarted) { if (bobPovTx?.state == VerificationTxState.OnStarted) {
@ -359,16 +326,16 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
} }
} }
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction
Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}") Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}")
alicePovTx?.state == VerificationTxState.ShortCodeReady alicePovTx?.state == VerificationTxState.ShortCodeReady
} }
} }
// wait for alice to get the ready // wait for alice to get the ready
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction
Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}")
if (bobPovTx?.state == VerificationTxState.OnStarted) { if (bobPovTx?.state == VerificationTxState.OnStarted) {
@ -383,38 +350,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) {
bobPovTx!!.userHasVerifiedShortCode() bobPovTx!!.userHasVerifiedShortCode()
alicePovTx!!.userHasVerifiedShortCode() alicePovTx!!.userHasVerifiedShortCode()
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
} }
} }
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
} }
} }
} }
fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData { 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) aliceSession.cryptoService().setWarnOnUnknownDevices(false)
val roomId = mTestHelper.runBlockingTest { val roomId = testHelper.runBlockingTest {
aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" })
} }
val room = aliceSession.getRoom(roomId)!! val room = aliceSession.getRoom(roomId)!!
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
room.enableEncryption() room.enableEncryption()
} }
val sessions = mutableListOf(aliceSession) val sessions = mutableListOf(aliceSession)
for (index in 1 until numberOfMembers) { for (index in 1 until numberOfMembers) {
val session = mTestHelper.createAccount("User_$index", defaultSessionParams) val session = testHelper.createAccount("User_$index", defaultSessionParams)
mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) }
println("TEST -> " + session.myUserId + " invited") 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") println("TEST -> " + session.myUserId + " joined")
sessions.add(session) sessions.add(session)
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,13 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.internal.worker package org.matrix.android.sdk.common
import android.content.Context import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import androidx.work.ListenableWorker
import androidx.work.WorkerParameters
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
} }

View file

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.matrix.android.sdk.api package org.matrix.android.sdk.common
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
@ -24,27 +24,27 @@ import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig 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.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.api.legacy.LegacySessionImporter
import org.matrix.android.sdk.api.network.ApiInterceptorListener import org.matrix.android.sdk.api.network.ApiInterceptorListener
import org.matrix.android.sdk.api.network.ApiPath import org.matrix.android.sdk.api.network.ApiPath
import org.matrix.android.sdk.api.raw.RawService 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.SessionManager
import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.ApiInterceptor
import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.network.UserAgentHolder
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject import javax.inject.Inject
/** /**
* This is the main entry point to the matrix sdk. * This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent.
* To get the singleton instance, use getInstance static method.
*/ */
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 legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService @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 sessionManager: SessionManager
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
@Inject internal lateinit var apiInterceptor: ApiInterceptor @Inject internal lateinit var apiInterceptor: ApiInterceptor
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
init { init {
Monarchy.init(context) Monarchy.init(context)
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this) DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) { val configuration = Configuration.Builder()
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) .setExecutor(Executors.newCachedThreadPool())
} .setWorkerFactory(matrixWorkerFactory)
.build()
WorkManager.initialize(context, configuration)
uiHandler.post { uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
} }
@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
companion object { companion object {
private lateinit var instance: Matrix private lateinit var instance: TestMatrix
private val isInit = AtomicBoolean(false) private val isInit = AtomicBoolean(false)
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) { fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
if (isInit.compareAndSet(false, true)) { 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)) { if (isInit.compareAndSet(false, true)) {
val appContext = context.applicationContext val appContext = context.applicationContext
if (appContext is MatrixConfiguration.Provider) { if (appContext is MatrixConfiguration.Provider) {
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration() val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
instance = Matrix(appContext, matrixConfiguration) instance = TestMatrix(appContext, matrixConfiguration)
} else { } else {
throw IllegalStateException("Matrix is not initialized properly." + throw IllegalStateException("Matrix is not initialized properly." +
" You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.") " You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")

View file

@ -34,12 +34,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
NetworkModule::class, NetworkModule::class,
AuthModule::class, AuthModule::class,
RawModule::class, RawModule::class,
SystemModule::class, SystemModule::class
TestNetworkModule::class
]) ])
@MatrixScope @MatrixScope
internal interface TestMatrixComponent : MatrixComponent { internal interface TestMatrixComponent : MatrixComponent {
fun inject(matrix: TestMatrix)
@Component.Factory @Component.Factory
interface Factory { interface Factory {
fun create(@BindsInstance context: Context, fun create(@BindsInstance context: Context,

View file

@ -18,10 +18,39 @@ package org.matrix.android.sdk.common
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import org.matrix.android.sdk.internal.di.MatrixComponent 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 @Module
internal abstract class TestModule { internal abstract class TestModule {
@Binds @Binds
abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent
@Module
companion object {
val interceptors = ArrayList<TestInterceptor>()
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()
}
}
} }

View file

@ -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<TestInterceptor>()
fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId }
@Provides
@JvmStatic
@MockHttpInterceptor
fun providesTestInterceptor(): TestInterceptor? {
return MockOkHttpInterceptor().also {
interceptors.add(it)
}
}
}

View file

@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class PreShareKeysTest : InstrumentedTest { class PreShareKeysTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun ensure_outbound_session_happy_path() { fun ensure_outbound_session_happy_path() {
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
val e2eRoomID = testData.roomId val e2eRoomID = testData.roomId
val aliceSession = testData.firstSession val aliceSession = testData.firstSession
val bobSession = testData.secondSession!! val bobSession = testData.secondSession!!
@ -58,12 +58,12 @@ class PreShareKeysTest : InstrumentedTest {
Log.d("#Test", "Room Key Received from alice $preShareCount") Log.d("#Test", "Room Key Received from alice $preShareCount")
// Force presharing of new outbound key // Force presharing of new outbound key
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it) aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it)
} }
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
val newGossipCount = bobSession.cryptoService().getGossipingEvents().count { val newGossipCount = bobSession.cryptoService().getGossipingEvents().count {
it.senderId == aliceSession.myUserId && it.senderId == aliceSession.myUserId &&
it.getClearType() == EventType.ROOM_KEY 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) assertEquals("The session received by bob should match what alice sent", 0, sharedIndex)
// Just send a real message as test // 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<EncryptedEventContent>()?.sessionId, "Unexpected megolm session") assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
} }
} }
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
} }
} }

View file

@ -62,8 +62,8 @@ import kotlin.coroutines.resume
class UnwedgingTest : InstrumentedTest { class UnwedgingTest : InstrumentedTest {
private lateinit var messagesReceivedByBob: List<TimelineEvent> private lateinit var messagesReceivedByBob: List<TimelineEvent>
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Before @Before
fun init() { fun init() {
@ -85,7 +85,7 @@ class UnwedgingTest : InstrumentedTest {
*/ */
@Test @Test
fun testUnwedging() { fun testUnwedging() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId val aliceRoomId = cryptoTestData.roomId
@ -133,7 +133,7 @@ class UnwedgingTest : InstrumentedTest {
roomFromAlicePOV.sendTextMessage("First message") roomFromAlicePOV.sendTextMessage("First message")
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
mTestHelper.await(latch) testHelper.await(latch)
bobTimeline.removeListener(bobEventsListener) bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 1 messagesReceivedByBob.size shouldBe 1
@ -161,7 +161,7 @@ class UnwedgingTest : InstrumentedTest {
roomFromAlicePOV.sendTextMessage("Second message") roomFromAlicePOV.sendTextMessage("Second message")
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
mTestHelper.await(latch) testHelper.await(latch)
bobTimeline.removeListener(bobEventsListener) bobTimeline.removeListener(bobEventsListener)
messagesReceivedByBob.size shouldBe 2 messagesReceivedByBob.size shouldBe 2
@ -179,7 +179,7 @@ class UnwedgingTest : InstrumentedTest {
aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId) aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId)
// Wait for the message to be received by Bob // Wait for the message to be received by Bob
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
bobEventsListener = createEventListener(it, 3) bobEventsListener = createEventListener(it, 3)
bobTimeline.addListener(bobEventsListener) bobTimeline.addListener(bobEventsListener)
messagesReceivedByBob = emptyList() messagesReceivedByBob = emptyList()
@ -201,11 +201,11 @@ class UnwedgingTest : InstrumentedTest {
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType()) Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType())
Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].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 // 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) bobTimeline.removeListener(bobHasThreeDecryptedEventsListener)
// It's a trick to force key request on fail to decrypt // It's a trick to force key request on fail to decrypt
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService() bobSession.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -222,8 +222,8 @@ class UnwedgingTest : InstrumentedTest {
} }
// Wait until we received back the key // Wait until we received back the key
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
// we should get back the key and be able to decrypt // we should get back the key and be able to decrypt
val result = tryOrNull { val result = tryOrNull {
bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "") bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "")
@ -235,7 +235,7 @@ class UnwedgingTest : InstrumentedTest {
bobTimeline.dispose() bobTimeline.dispose()
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener { private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {

View file

@ -45,14 +45,14 @@ import kotlin.coroutines.resume
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
class XSigningTest : InstrumentedTest { class XSigningTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun test_InitializeAndStoreKeys() { fun test_InitializeAndStoreKeys() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService() aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning(object : UserInteractiveAuthInterceptor { .initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
@ -79,12 +79,12 @@ class XSigningTest : InstrumentedTest {
assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified()) assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified())
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
} }
@Test @Test
fun test_CrossSigningCheckBobSeesTheKeys() { fun test_CrossSigningCheckBobSeesTheKeys() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -98,21 +98,21 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD password = TestConstants.PASSWORD
) )
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams) promise.resume(aliceAuthParams)
} }
}, it) }, it)
} }
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(bobAuthParams) promise.resume(bobAuthParams)
} }
}, it) } }, it) }
// Check that alice can see bob keys // Check that alice can see bob keys
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey()) 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()) assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
} }
@Test @Test
fun test_CrossSigningTestAliceTrustBobNewDevice() { fun test_CrossSigningTestAliceTrustBobNewDevice() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -144,12 +144,12 @@ class XSigningTest : InstrumentedTest {
password = TestConstants.PASSWORD password = TestConstants.PASSWORD
) )
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { testHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(aliceAuthParams) promise.resume(aliceAuthParams)
} }
}, it) } }, it) }
mTestHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { testHelper.doSync<Unit> { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
promise.resume(bobAuthParams) promise.resume(bobAuthParams)
} }
@ -157,21 +157,21 @@ class XSigningTest : InstrumentedTest {
// Check that alice can see bob keys // Check that alice can see bob keys
val bobUserId = bobSession.myUserId val bobUserId = bobSession.myUserId
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) }
val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId)
assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false) assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false)
mTestHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } testHelper.doSync<Unit> { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) }
// Now bobs logs in on a new device and verifies 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 // 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!! val bobSecondDeviceId = bobSession2.sessionParams.deviceId!!
// Check that bob first session sees the new login // Check that bob first session sees the new login
val data = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { val data = testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) 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) assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice)
// Manually mark it as trusted from first session // Manually mark it as trusted from first session
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it) bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it)
} }
// Now alice should cross trust bob's second device // Now alice should cross trust bob's second device
val data2 = mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { val data2 = testHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it)
} }
@ -200,8 +200,8 @@ class XSigningTest : InstrumentedTest {
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
mTestHelper.signOutAndClose(bobSession2) testHelper.signOutAndClose(bobSession2)
} }
} }

View file

@ -40,8 +40,9 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EncryptionTest : InstrumentedTest { class EncryptionTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val testHelper = CommonTestHelper(context())
private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun test_EncryptionEvent() { fun test_EncryptionEvent() {
@ -69,7 +70,7 @@ class EncryptionTest : InstrumentedTest {
} }
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) { 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 aliceSession = cryptoTestData.firstSession
val room = aliceSession.getRoom(cryptoTestData.roomId)!! val room = aliceSession.getRoom(cryptoTestData.roomId)!!
@ -101,12 +102,12 @@ class EncryptionTest : InstrumentedTest {
timeline.addListener(timelineListener) timeline.addListener(timelineListener)
action.invoke(room) action.invoke(room)
testHelper.await(latch)
mTestHelper.await(latch)
timeline.dispose() timeline.dispose()
testHelper.waitWithLatch {
room.isEncrypted() shouldBe roomShouldBeEncrypted room.isEncrypted() shouldBe roomShouldBeEncrypted
it.countDown()
cryptoTestData.cleanUp(mTestHelper) }
cryptoTestData.cleanUp(testHelper)
} }
} }

View file

@ -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.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.common.CommonTestHelper 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.SessionTestParams
import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.common.TestConstants
import org.matrix.android.sdk.internal.crypto.GossipingRequestState 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.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import java.util.concurrent.CountDownLatch
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
@ -63,15 +61,14 @@ import kotlin.coroutines.resume
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class KeyShareTests : InstrumentedTest { class KeyShareTests : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val commonTestHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
@Test @Test
fun test_DoNotSelfShareIfNotTrusted() { 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 // Create an encrypted room and add a message
val roomId = mTestHelper.runBlockingTest { val roomId = commonTestHelper.runBlockingTest {
aliceSession.createRoom( aliceSession.createRoom(
CreateRoomParams().apply { CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE visibility = RoomDirectoryVisibility.PRIVATE
@ -83,11 +80,11 @@ class KeyShareTests : InstrumentedTest {
assertNotNull(room) assertNotNull(room)
Thread.sleep(4_000) Thread.sleep(4_000)
assertTrue(room?.isEncrypted() == true) 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 // 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) val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
@ -105,25 +102,24 @@ class KeyShareTests : InstrumentedTest {
// Try to request // Try to request
aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root) aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root)
val waitLatch = CountDownLatch(1)
val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId val eventMegolmSessionId = receivedEvent.root.content.toModel<EncryptedEventContent>()?.sessionId
var outGoingRequestId: String? = null var outGoingRequestId: String? = null
mTestHelper.retryPeriodicallyWithLatch(waitLatch) { commonTestHelper.waitWithLatch { latch ->
aliceSession2.cryptoService().getOutgoingRoomKeyRequests() commonTestHelper.retryPeriodicallyWithLatch(latch) {
.filter { req -> aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
// filter out request that was known before .filter { req ->
!outgoingRequestsBefore.any { req.requestId == it.requestId } // filter out request that was known before
} !outgoingRequestsBefore.any { req.requestId == it.requestId }
.let { }
val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId } .let {
outGoingRequestId = outgoing?.requestId val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId }
outgoing != null outGoingRequestId = outgoing?.requestId
} outgoing != null
}
}
} }
mTestHelper.await(waitLatch)
Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId") Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId")
val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests() val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
@ -134,8 +130,8 @@ class KeyShareTests : InstrumentedTest {
// The first session should see an incoming request // The first session should see an incoming request
// the request should be refused, because the device is not trusted // the request should be refused, because the device is not trusted
mTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
// DEBUG LOGS // DEBUG LOGS
aliceSession.cryptoService().getIncomingRoomKeyRequests().let { aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)") Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)")
@ -164,8 +160,8 @@ class KeyShareTests : InstrumentedTest {
// Re request // Re request
aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root) aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root)
mTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession.cryptoService().getIncomingRoomKeyRequests().let { aliceSession.cryptoService().getIncomingRoomKeyRequests().let {
Log.v("TEST", "Incoming request Session 1") Log.v("TEST", "Incoming request Session 1")
Log.v("TEST", "=========================") Log.v("TEST", "=========================")
@ -180,8 +176,8 @@ class KeyShareTests : InstrumentedTest {
} }
Thread.sleep(6_000) Thread.sleep(6_000)
mTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let { aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let {
it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED } it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED }
} }
@ -194,15 +190,15 @@ class KeyShareTests : InstrumentedTest {
fail("should have been able to decrypt") fail("should have been able to decrypt")
} }
mTestHelper.signOutAndClose(aliceSession) commonTestHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(aliceSession2) commonTestHelper.signOutAndClose(aliceSession2)
} }
@Test @Test
fun test_ShareSSSSSecret() { fun test_ShareSSSSSecret() {
val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> { commonTestHelper.doSync<Unit> {
aliceSession1.cryptoService().crossSigningService() aliceSession1.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -218,25 +214,25 @@ class KeyShareTests : InstrumentedTest {
} }
// Also bootstrap keybackup on first session // Also bootstrap keybackup on first session
val creationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> { val creationInfo = commonTestHelper.doSync<MegolmBackupCreationInfo> {
aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
} }
val version = mTestHelper.doSync<KeysVersion> { val version = commonTestHelper.doSync<KeysVersion> {
aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it) aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
} }
// Save it for gossiping // Save it for gossiping
aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) 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 aliceVerificationService1 = aliceSession1.cryptoService().verificationService()
val aliceVerificationService2 = aliceSession2.cryptoService().verificationService() val aliceVerificationService2 = aliceSession2.cryptoService().verificationService()
// force keys download // force keys download
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { commonTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it) aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it)
} }
mTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> { commonTestHelper.doSync<MXUsersDevicesMap<CryptoDeviceInfo>> {
aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it) aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it)
} }
@ -276,8 +272,8 @@ class KeyShareTests : InstrumentedTest {
aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId
?: "", txId) ?: "", txId)
mTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true 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 // SSK and USK private keys should have been shared
mTestHelper.waitWithLatch(60_000) { latch -> commonTestHelper.waitWithLatch(60_000) { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}") Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}")
aliceSession2.cryptoService().crossSigningService().canCrossSign() aliceSession2.cryptoService().crossSigningService().canCrossSign()
} }
} }
// Test that key backup key has been shared to // Test that key backup key has been shared to
mTestHelper.waitWithLatch(60_000) { latch -> commonTestHelper.waitWithLatch(60_000) { latch ->
val keysBackupService = aliceSession2.cryptoService().keysBackupService() val keysBackupService = aliceSession2.cryptoService().keysBackupService()
mTestHelper.retryPeriodicallyWithLatch(latch) { commonTestHelper.retryPeriodicallyWithLatch(latch) {
Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}") Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}")
keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey
} }
} }
mTestHelper.signOutAndClose(aliceSession1) commonTestHelper.signOutAndClose(aliceSession1)
mTestHelper.signOutAndClose(aliceSession2) commonTestHelper.signOutAndClose(aliceSession2)
} }
@Test @Test
fun test_ImproperKeyShareBug() { fun test_ImproperKeyShareBug() {
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
mTestHelper.doSync<Unit> { commonTestHelper.doSync<Unit> {
aliceSession.cryptoService().crossSigningService() aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -331,7 +327,7 @@ class KeyShareTests : InstrumentedTest {
} }
// Create an encrypted room and send a couple of messages // Create an encrypted room and send a couple of messages
val roomId = mTestHelper.runBlockingTest { val roomId = commonTestHelper.runBlockingTest {
aliceSession.createRoom( aliceSession.createRoom(
CreateRoomParams().apply { CreateRoomParams().apply {
visibility = RoomDirectoryVisibility.PRIVATE visibility = RoomDirectoryVisibility.PRIVATE
@ -343,12 +339,12 @@ class KeyShareTests : InstrumentedTest {
assertNotNull(roomAlicePov) assertNotNull(roomAlicePov)
Thread.sleep(1_000) Thread.sleep(1_000)
assertTrue(roomAlicePov?.isEncrypted() == true) 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 // Create bob session
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
mTestHelper.doSync<Unit> { commonTestHelper.doSync<Unit> {
bobSession.cryptoService().crossSigningService() bobSession.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -365,11 +361,11 @@ class KeyShareTests : InstrumentedTest {
} }
// Let alice invite bob // Let alice invite bob
mTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
roomAlicePov.invite(bobSession.myUserId, null) roomAlicePov.invite(bobSession.myUserId, null)
} }
mTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList()) bobSession.joinRoom(roomAlicePov.roomId, null, emptyList())
} }
@ -377,7 +373,7 @@ class KeyShareTests : InstrumentedTest {
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId) aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
// and now resend a new message to reset index to 0 // 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 roomRoomBobPov = aliceSession.getRoom(roomId)
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId) val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)

View file

@ -41,8 +41,8 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class WithHeldTests : InstrumentedTest { class WithHeldTests : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun test_WithHeldUnverifiedReason() { fun test_WithHeldUnverifiedReason() {
@ -50,19 +50,19 @@ class WithHeldTests : InstrumentedTest {
// ARRANGE // ARRANGE
// ============================= // =============================
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) val bobSession = testHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
// Initialize cross signing on both // Initialize cross signing on both
mCryptoTestHelper.initializeCrossSigning(aliceSession) cryptoTestHelper.initializeCrossSigning(aliceSession)
mCryptoTestHelper.initializeCrossSigning(bobSession) cryptoTestHelper.initializeCrossSigning(bobSession)
val roomId = mCryptoTestHelper.createDM(aliceSession, bobSession) val roomId = cryptoTestHelper.createDM(aliceSession, bobSession)
mCryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId) cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId)
val roomAlicePOV = aliceSession.getRoom(roomId)!! val roomAlicePOV = aliceSession.getRoom(roomId)!!
val bobUnverifiedSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) val bobUnverifiedSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true))
// ============================= // =============================
// ACT // ACT
@ -71,11 +71,11 @@ class WithHeldTests : InstrumentedTest {
// Alice decide to not send to unverified sessions // Alice decide to not send to unverified sessions
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true) 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 // await for bob unverified session to get the message
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null
} }
} }
@ -101,10 +101,10 @@ class WithHeldTests : InstrumentedTest {
// enable back sending to unverified // enable back sending to unverified
aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false) 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 -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId) val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId)
// wait until it's decrypted // wait until it's decrypted
ev?.root?.getClearType() == EventType.MESSAGE ev?.root?.getClearType() == EventType.MESSAGE
@ -123,17 +123,17 @@ class WithHeldTests : InstrumentedTest {
Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage) Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
} }
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
mTestHelper.signOutAndClose(bobUnverifiedSession) testHelper.signOutAndClose(bobUnverifiedSession)
} }
@Test @Test
fun test_WithHeldNoOlm() { fun test_WithHeldNoOlm() {
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession val aliceSession = testData.firstSession
val bobSession = testData.secondSession!! val bobSession = testData.secondSession!!
val aliceInterceptor = mTestHelper.getTestInterceptor(aliceSession) val aliceInterceptor = testHelper.getTestInterceptor(aliceSession)
// Simulate no OTK // Simulate no OTK
aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule( aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule(
@ -147,11 +147,11 @@ class WithHeldTests : InstrumentedTest {
val roomAlicePov = aliceSession.getRoom(testData.roomId)!! 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 // await for bob session to get the message
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null
} }
} }
@ -177,14 +177,14 @@ class WithHeldTests : InstrumentedTest {
// Add a new device for bob // Add a new device for bob
aliceInterceptor.clearRules() aliceInterceptor.clearRules()
val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true)) val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true))
// send a second message // 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 // Check that the
// await for bob SecondSession session to get the message // await for bob SecondSession session to get the message
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null 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) Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
aliceInterceptor.clearRules() aliceInterceptor.clearRules()
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
mTestHelper.signOutAndClose(bobSecondSession) testHelper.signOutAndClose(bobSecondSession)
} }
@Test @Test
fun test_WithHeldKeyRequest() { fun test_WithHeldKeyRequest() {
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = testData.firstSession val aliceSession = testData.firstSession
val bobSession = testData.secondSession!! val bobSession = testData.secondSession!!
val roomAlicePov = aliceSession.getRoom(testData.roomId)!! 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 // 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 // initialize to force request keys if missing
mCryptoTestHelper.initializeCrossSigning(bobSecondSession) cryptoTestHelper.initializeCrossSigning(bobSecondSession)
// Trust bob second device from Alice POV // Trust bob second device from Alice POV
aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback()) aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback())
@ -223,8 +223,8 @@ class WithHeldTests : InstrumentedTest {
var sessionId: String? = null var sessionId: String? = null
// Check that the // Check that the
// await for bob SecondSession session to get the message // await for bob SecondSession session to get the message
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also { val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also {
// try to decrypt and force key request // try to decrypt and force key request
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") } tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
@ -235,8 +235,8 @@ class WithHeldTests : InstrumentedTest {
} }
// Check that bob second session requested the key // Check that bob second session requested the key
mTestHelper.waitWithLatch { latch -> testHelper.waitWithLatch { latch ->
mTestHelper.retryPeriodicallyWithLatch(latch) { testHelper.retryPeriodicallyWithLatch(latch) {
val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!) val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!)
wc?.code == WithHeldCode.UNAUTHORISED wc?.code == WithHeldCode.UNAUTHORISED
} }

View file

@ -22,7 +22,6 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder import org.junit.FixMethodOrder
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith 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.KeysVersion
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult
import java.util.ArrayList
import java.util.Collections import java.util.Collections
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@ -51,9 +49,9 @@ import java.util.concurrent.CountDownLatch
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class KeysBackupTest : InstrumentedTest { class KeysBackupTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
private val mKeysBackupTestHelper = KeysBackupTestHelper(mTestHelper, mCryptoTestHelper) private val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
/** /**
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys * - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
@ -62,7 +60,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun roomKeysTest_testBackupStore_ok() { fun roomKeysTest_testBackupStore_ok() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
// From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys // From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
@ -92,7 +90,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)) assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false))
assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)) assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true))
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -100,7 +98,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun prepareKeysBackupVersionTest() { fun prepareKeysBackupVersionTest() {
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
assertNotNull(bobSession.cryptoService().keysBackupService()) assertNotNull(bobSession.cryptoService().keysBackupService())
@ -110,7 +108,7 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> { val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
keysBackup.prepareKeysBackupVersion(null, null, it) keysBackup.prepareKeysBackupVersion(null, null, it)
} }
@ -120,7 +118,7 @@ class KeysBackupTest : InstrumentedTest {
assertNotNull(megolmBackupCreationInfo.recoveryKey) assertNotNull(megolmBackupCreationInfo.recoveryKey)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
} }
/** /**
@ -128,7 +126,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun createKeysBackupVersionTest() { 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() val keysBackup = bobSession.cryptoService().keysBackupService()
@ -136,14 +134,14 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> { val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
keysBackup.prepareKeysBackupVersion(null, null, it) keysBackup.prepareKeysBackupVersion(null, null, it)
} }
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
// Create the version // Create the version
mTestHelper.doSync<KeysVersion> { testHelper.doSync<KeysVersion> {
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
} }
@ -151,7 +149,7 @@ class KeysBackupTest : InstrumentedTest {
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
mTestHelper.signOutAndClose(bobSession) testHelper.signOutAndClose(bobSession)
} }
/** /**
@ -160,8 +158,9 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun backupAfterCreateKeysBackupVersionTest() { fun backupAfterCreateKeysBackupVersionTest() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
keysBackupTestHelper.waitForKeybackUpBatching()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
@ -171,9 +170,9 @@ class KeysBackupTest : InstrumentedTest {
val stateObserver = StateObserver(keysBackup, latch, 5) 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 nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true) val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)
@ -191,7 +190,7 @@ class KeysBackupTest : InstrumentedTest {
KeysBackupState.ReadyToBackUp KeysBackupState.ReadyToBackUp
) )
) )
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -199,13 +198,13 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun backupAllGroupSessionsTest() { fun backupAllGroupSessionsTest() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Check that backupAllGroupSessions returns valid data // Check that backupAllGroupSessions returns valid data
val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false) val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)
@ -214,7 +213,7 @@ class KeysBackupTest : InstrumentedTest {
var lastBackedUpKeysProgress = 0 var lastBackedUpKeysProgress = 0
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
keysBackup.backupAllGroupSessions(object : ProgressListener { keysBackup.backupAllGroupSessions(object : ProgressListener {
override fun onProgress(progress: Int, total: Int) { override fun onProgress(progress: Int, total: Int) {
assertEquals(nbOfKeys, total) assertEquals(nbOfKeys, total)
@ -230,7 +229,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys) assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -243,7 +242,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun testEncryptAndDecryptKeysBackupData() { fun testEncryptAndDecryptKeysBackupData() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService
@ -252,7 +251,7 @@ class KeysBackupTest : InstrumentedTest {
// - Pick a megolm key // - Pick a megolm key
val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0] val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0]
val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo
// - Check encryptGroupSession() returns stg // - Check encryptGroupSession() returns stg
val keyBackupData = keysBackup.encryptGroupSession(session) val keyBackupData = keysBackup.encryptGroupSession(session)
@ -270,10 +269,10 @@ class KeysBackupTest : InstrumentedTest {
decryption!!) decryption!!)
assertNotNull(sessionData) assertNotNull(sessionData)
// - Compare the decrypted megolm key with the original one // - Compare the decrypted megolm key with the original one
mKeysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData) keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -284,10 +283,10 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun restoreKeysBackupTest() { fun restoreKeysBackupTest() {
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Restore the e2e backup from the homeserver // - Restore the e2e backup from the homeserver
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> { val importRoomKeysResult = testHelper.doSync<ImportRoomKeysResult> {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
null, 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() { fun trustKeyBackupVersionTest() {
// - Do an e2e backup to the homeserver with a recovery key // - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device // - And log Alice on a new device
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@ -379,7 +378,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
// - Trust the backup from the new device // - Trust the backup from the new device
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion( testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
true, true,
@ -388,21 +387,21 @@ class KeysBackupTest : InstrumentedTest {
} }
// Wait for backup state to be ReadyToBackUp // 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 // - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
// - Retrieve the last version from the server // - Retrieve the last version from the server
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> { val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
} }
// - It must be the same // - It must be the same
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> { val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
} }
@ -411,7 +410,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(2, keysBackupVersionTrust.signatures.size) assertEquals(2, keysBackupVersionTrust.signatures.size)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
} }
/** /**
@ -428,7 +427,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionWithRecoveryKeyTest() { fun trustKeyBackupVersionWithRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key // - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device // - And log Alice on a new device
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@ -438,7 +437,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
// - Trust the backup from the new device with the recovery key // - Trust the backup from the new device with the recovery key
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey( testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
@ -447,21 +446,21 @@ class KeysBackupTest : InstrumentedTest {
} }
// Wait for backup state to be ReadyToBackUp // 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 // - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
// - Retrieve the last version from the server // - Retrieve the last version from the server
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> { val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
} }
// - It must be the same // - It must be the same
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> { val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
} }
@ -470,7 +469,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(2, keysBackupVersionTrust.signatures.size) assertEquals(2, keysBackupVersionTrust.signatures.size)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
} }
/** /**
@ -485,7 +484,7 @@ class KeysBackupTest : InstrumentedTest {
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() { fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
// - Do an e2e backup to the homeserver with a recovery key // - Do an e2e backup to the homeserver with a recovery key
// - And log Alice on a new device // - And log Alice on a new device
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@ -501,7 +500,7 @@ class KeysBackupTest : InstrumentedTest {
"Bad recovery key", "Bad recovery key",
TestMatrixCallback(latch, false) TestMatrixCallback(latch, false)
) )
mTestHelper.await(latch) testHelper.await(latch)
// - The new device must still see the previous backup as not trusted // - The new device must still see the previous backup as not trusted
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion) assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
@ -509,7 +508,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
stateObserver.stopAndCheckStates(null) 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 // - Do an e2e backup to the homeserver with a password
// - And log Alice on a new device // - And log Alice on a new device
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@ -538,7 +537,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
// - Trust the backup from the new device with the password // - Trust the backup from the new device with the password
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase( testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase(
testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
password, password,
@ -547,21 +546,21 @@ class KeysBackupTest : InstrumentedTest {
} }
// Wait for backup state to be ReadyToBackUp // 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 // - Backup must be enabled on the new device, on the same version
assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version)
assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled)
// - Retrieve the last version from the server // - Retrieve the last version from the server
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> { val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it)
} }
// - It must be the same // - It must be the same
assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version)
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> { val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it)
} }
@ -570,7 +569,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(2, keysBackupVersionTrust.signatures.size) assertEquals(2, keysBackupVersionTrust.signatures.size)
stateObserver.stopAndCheckStates(null) 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 // - Do an e2e backup to the homeserver with a password
// - And log Alice on a new device // - And log Alice on a new device
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService())
@ -604,7 +603,7 @@ class KeysBackupTest : InstrumentedTest {
badPassword, badPassword,
TestMatrixCallback(latch, false) TestMatrixCallback(latch, false)
) )
mTestHelper.await(latch) testHelper.await(latch)
// - The new device must still see the previous backup as not trusted // - The new device must still see the previous backup as not trusted
assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion) assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion)
@ -612,7 +611,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
} }
/** /**
@ -623,7 +622,7 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun restoreKeysBackupWithAWrongRecoveryKeyTest() { fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Try to restore the e2e backup with a wrong recovery key // - Try to restore the e2e backup with a wrong recovery key
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
@ -640,12 +639,12 @@ class KeysBackupTest : InstrumentedTest {
} }
} }
) )
mTestHelper.await(latch2) testHelper.await(latch2)
// onSuccess may not have been called // onSuccess may not have been called
assertNull(importRoomKeysResult) assertNull(importRoomKeysResult)
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
} }
/** /**
@ -658,12 +657,12 @@ class KeysBackupTest : InstrumentedTest {
fun testBackupWithPassword() { fun testBackupWithPassword() {
val password = "password" val password = "password"
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Restore the e2e backup with the password // - Restore the e2e backup with the password
val steps = ArrayList<StepProgressListener.Step>() val steps = ArrayList<StepProgressListener.Step>()
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> { val importRoomKeysResult = testHelper.doSync<ImportRoomKeysResult> {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
password, password,
null, null,
@ -698,9 +697,9 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress) assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress)
assertEquals(100, (steps[104] 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 password = "password"
val wrongPassword = "passw0rd" val wrongPassword = "passw0rd"
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Try to restore the e2e backup with a wrong password // - Try to restore the e2e backup with a wrong password
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
@ -731,12 +730,12 @@ class KeysBackupTest : InstrumentedTest {
} }
} }
) )
mTestHelper.await(latch2) testHelper.await(latch2)
// onSuccess may not have been called // onSuccess may not have been called
assertNull(importRoomKeysResult) assertNull(importRoomKeysResult)
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
} }
/** /**
@ -749,10 +748,10 @@ class KeysBackupTest : InstrumentedTest {
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() { fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
val password = "password" val password = "password"
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
// - Restore the e2e backup with the recovery key. // - Restore the e2e backup with the recovery key.
val importRoomKeysResult = mTestHelper.doSync<ImportRoomKeysResult> { val importRoomKeysResult = testHelper.doSync<ImportRoomKeysResult> {
testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!,
testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey,
null, 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 @Test
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() { fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
// - Try to restore the e2e backup with a password // - Try to restore the e2e backup with a password
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
@ -792,12 +791,12 @@ class KeysBackupTest : InstrumentedTest {
} }
} }
) )
mTestHelper.await(latch2) testHelper.await(latch2)
// onSuccess may not have been called // onSuccess may not have been called
assertNull(importRoomKeysResult) assertNull(importRoomKeysResult)
testData.cleanUp(mTestHelper) testData.cleanUp(testHelper)
} }
/** /**
@ -807,22 +806,22 @@ class KeysBackupTest : InstrumentedTest {
@Test @Test
fun testIsKeysBackupTrusted() { fun testIsKeysBackupTrusted() {
// - Create a backup version // - Create a backup version
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
// - Do an e2e backup to the homeserver // - Do an e2e backup to the homeserver
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
// Get key backup version from the homeserver // Get key backup version from the homeserver
val keysVersionResult = mTestHelper.doSync<KeysVersionResult?> { val keysVersionResult = testHelper.doSync<KeysVersionResult?> {
keysBackup.getCurrentVersion(it) keysBackup.getCurrentVersion(it)
} }
// - Check the returned KeyBackupVersion is trusted // - Check the returned KeyBackupVersion is trusted
val keysBackupVersionTrust = mTestHelper.doSync<KeysBackupVersionTrust> { val keysBackupVersionTrust = testHelper.doSync<KeysBackupVersionTrust> {
keysBackup.getKeysBackupTrust(keysVersionResult!!, it) keysBackup.getKeysBackupTrust(keysVersionResult!!, it)
} }
@ -837,7 +836,7 @@ class KeysBackupTest : InstrumentedTest {
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId) assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -849,9 +848,8 @@ class KeysBackupTest : InstrumentedTest {
*/ */
@Test @Test
fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() { fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() {
fail("This test still fail. To investigate")
// - Create a backup version // - Create a backup version
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@ -859,15 +857,15 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
// - Restart alice session // - Restart alice session
// - Log Alice on a new device // - 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() val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
@ -891,13 +889,13 @@ class KeysBackupTest : InstrumentedTest {
} }
} }
}) })
mTestHelper.await(latch) testHelper.await(latch)
assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion) assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
stateObserver2.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null)
mTestHelper.signOutAndClose(aliceSession2) testHelper.signOutAndClose(aliceSession2)
} }
/** /**
@ -911,7 +909,7 @@ class KeysBackupTest : InstrumentedTest {
@Test @Test
fun testBackupWhenAnotherBackupWasCreated() { fun testBackupWhenAnotherBackupWasCreated() {
// - Create a backup version // - Create a backup version
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@ -939,15 +937,15 @@ class KeysBackupTest : InstrumentedTest {
}) })
// - Make alice back up her keys to her homeserver // - Make alice back up her keys to her homeserver
mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
mTestHelper.await(latch0) testHelper.await(latch0)
// - Create a new backup with fake data on the homeserver, directly using the rest client // - Create a new backup with fake data on the homeserver, directly using the rest client
val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo() val megolmBackupCreationInfo = cryptoTestHelper.createFakeMegolmBackupCreationInfo()
mTestHelper.doSync<KeysVersion> { testHelper.doSync<KeysVersion> {
(keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it) (keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it)
} }
@ -957,14 +955,14 @@ class KeysBackupTest : InstrumentedTest {
// - Make alice back up all her keys again // - Make alice back up all her keys again
val latch2 = CountDownLatch(1) val latch2 = CountDownLatch(1)
keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false)) keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false))
mTestHelper.await(latch2) testHelper.await(latch2)
// -> That must fail and her backup state must be WrongBackUpVersion // -> That must fail and her backup state must be WrongBackUpVersion
assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state) assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state)
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -982,17 +980,17 @@ class KeysBackupTest : InstrumentedTest {
@Test @Test
fun testBackupAfterVerifyingADevice() { fun testBackupAfterVerifyingADevice() {
// - Create a backup version // - Create a backup version
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
// - Make alice back up her keys to her homeserver // - 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. // Wait for keys backup to finish by asking again to backup keys.
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
keysBackup.backupAllGroupSessions(null, it) keysBackup.backupAllGroupSessions(null, it)
} }
@ -1001,14 +999,14 @@ class KeysBackupTest : InstrumentedTest {
val aliceUserId = cryptoTestData.firstSession.myUserId val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device // - 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 // - Post a message to have a new megolm session
aliceSession2.cryptoService().setWarnOnUnknownDevices(false) aliceSession2.cryptoService().setWarnOnUnknownDevices(false)
val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!! 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 // - Try to backup all in aliceSession2, it must fail
val keysBackup2 = aliceSession2.cryptoService().keysBackupService() val keysBackup2 = aliceSession2.cryptoService().keysBackupService()
@ -1025,7 +1023,7 @@ class KeysBackupTest : InstrumentedTest {
super.onSuccess(data) super.onSuccess(data)
} }
}) })
mTestHelper.await(latch2) testHelper.await(latch2)
assertFalse(isSuccessful) assertFalse(isSuccessful)
@ -1049,12 +1047,12 @@ class KeysBackupTest : InstrumentedTest {
} }
} }
}) })
mTestHelper.await(latch4) testHelper.await(latch4)
// -> It must use the same backup version // -> It must use the same backup version
assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion) assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it) aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it)
} }
@ -1063,8 +1061,8 @@ class KeysBackupTest : InstrumentedTest {
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
stateObserver2.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null)
mTestHelper.signOutAndClose(aliceSession2) testHelper.signOutAndClose(aliceSession2)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -1074,7 +1072,7 @@ class KeysBackupTest : InstrumentedTest {
@Test @Test
fun deleteKeysBackupTest() { fun deleteKeysBackupTest() {
// - Create a backup version // - Create a backup version
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@ -1082,17 +1080,17 @@ class KeysBackupTest : InstrumentedTest {
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup)
assertTrue(keysBackup.isEnabled) assertTrue(keysBackup.isEnabled)
// Delete the backup // Delete the backup
mTestHelper.doSync<Unit> { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) } testHelper.doSync<Unit> { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) }
// Backup is now disabled // Backup is now disabled
assertFalse(keysBackup.isEnabled) assertFalse(keysBackup.isEnabled)
stateObserver.stopAndCheckStates(null) stateObserver.stopAndCheckStates(null)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
} }

View file

@ -32,8 +32,12 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
class KeysBackupTestHelper( class KeysBackupTestHelper(
private val mTestHelper: CommonTestHelper, private val testHelper: CommonTestHelper,
private val mCryptoTestHelper: CryptoTestHelper) { private val cryptoTestHelper: CryptoTestHelper) {
fun waitForKeybackUpBatching() {
Thread.sleep(400)
}
/** /**
* Common initial condition * Common initial condition
@ -43,7 +47,9 @@ class KeysBackupTestHelper(
* @param password optional password * @param password optional password
*/ */
fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData { fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
waitForKeybackUpBatching()
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
@ -57,7 +63,7 @@ class KeysBackupTestHelper(
var lastProgress = 0 var lastProgress = 0
var lastTotal = 0 var lastTotal = 0
mTestHelper.doSync<Unit> { testHelper.doSync<Unit> {
keysBackup.backupAllGroupSessions(object : ProgressListener { keysBackup.backupAllGroupSessions(object : ProgressListener {
override fun onProgress(progress: Int, total: Int) { override fun onProgress(progress: Int, total: Int) {
lastProgress = progress lastProgress = progress
@ -72,7 +78,7 @@ class KeysBackupTestHelper(
val aliceUserId = cryptoTestData.firstSession.myUserId val aliceUserId = cryptoTestData.firstSession.myUserId
// - Log Alice on a new device // - 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 // Test check: aliceSession2 has no keys at login
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false)) Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
@ -92,7 +98,7 @@ class KeysBackupTestHelper(
password: String? = null): PrepareKeysBackupDataResult { password: String? = null): PrepareKeysBackupDataResult {
val stateObserver = StateObserver(keysBackup) val stateObserver = StateObserver(keysBackup)
val megolmBackupCreationInfo = mTestHelper.doSync<MegolmBackupCreationInfo> { val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
keysBackup.prepareKeysBackupVersion(password, null, it) keysBackup.prepareKeysBackupVersion(password, null, it)
} }
@ -101,7 +107,7 @@ class KeysBackupTestHelper(
Assert.assertFalse(keysBackup.isEnabled) Assert.assertFalse(keysBackup.isEnabled)
// Create the version // Create the version
val keysVersion = mTestHelper.doSync<KeysVersion> { val keysVersion = testHelper.doSync<KeysVersion> {
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
} }
@ -136,7 +142,7 @@ class KeysBackupTestHelper(
} }
}) })
mTestHelper.await(latch) testHelper.await(latch)
} }
fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) { fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) {

View file

@ -18,10 +18,6 @@ package org.matrix.android.sdk.internal.crypto.ssss
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.test.ext.junit.runners.AndroidJUnit4 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.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull 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.KeySigner
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec 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.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.SharedSecretStorageService
import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo
import org.matrix.android.sdk.api.util.Optional 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.SSSS_ALGORITHM_AES_HMAC_SHA2
import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class QuadSTests : InstrumentedTest { class QuadSTests : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val emptyKeySigner = object : KeySigner { private val emptyKeySigner = object : KeySigner {
override fun sign(canonicalJson: String): Map<String, Map<String, String>>? { override fun sign(canonicalJson: String): Map<String, Map<String, String>>? {
@ -60,35 +56,29 @@ class QuadSTests : InstrumentedTest {
} }
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun test_Generate4SKey() { 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 quadS = aliceSession.sharedSecretStorageService
val TEST_KEY_ID = "my.test.Key" val TEST_KEY_ID = "my.test.Key"
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner) quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner)
} }
// Assert Account data is updated
val accountDataLock = CountDownLatch(1)
var accountData: UserAccountDataEvent? = null var accountData: UserAccountDataEvent? = null
// Assert Account data is updated
val liveAccountData = runBlocking(Dispatchers.Main) { testHelper.waitWithLatch {
aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") val liveAccountData = aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID")
} val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t -> if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") {
if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") { accountData = t.getOrNull()
accountData = t.getOrNull() }
accountDataLock.countDown() it.countDown()
} }
liveAccountData.observeForever(accountDataObserver)
} }
GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
mTestHelper.await(accountDataLock)
assertNotNull("Key should be stored in account data", accountData) assertNotNull("Key should be stored in account data", accountData)
val parsed = SecretStorageKeyContent.fromJson(accountData!!.content) val parsed = SecretStorageKeyContent.fromJson(accountData!!.content)
assertNotNull("Key Content cannot be parsed", parsed) assertNotNull("Key Content cannot be parsed", parsed)
@ -96,36 +86,29 @@ class QuadSTests : InstrumentedTest {
assertEquals("Unexpected key name", "Test Key", parsed.name) assertEquals("Unexpected key name", "Test Key", parsed.name)
assertNull("Key was not generated from passphrase", parsed.passphrase) assertNull("Key was not generated from passphrase", parsed.passphrase)
// Set as default key
GlobalScope.launch {
quadS.setDefaultKey(TEST_KEY_ID)
}
var defaultKeyAccountData: UserAccountDataEvent? = null var defaultKeyAccountData: UserAccountDataEvent? = null
val defaultDataLock = CountDownLatch(1) // Set as default key
testHelper.waitWithLatch { latch ->
val liveDefAccountData = runBlocking(Dispatchers.Main) { quadS.setDefaultKey(TEST_KEY_ID)
aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID) val liveDefAccountData =
} aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t -> val accountDefDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) { if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) {
defaultKeyAccountData = t.getOrNull()!! defaultKeyAccountData = t.getOrNull()!!
defaultDataLock.countDown() latch.countDown()
}
} }
liveDefAccountData.observeForever(accountDefDataObserver)
} }
GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) }
mTestHelper.await(defaultDataLock)
assertNotNull(defaultKeyAccountData?.content) assertNotNull(defaultKeyAccountData?.content)
assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key")) assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key"))
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
} }
@Test @Test
fun test_StoreSecret() { 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 keyId = "My.Key"
val info = generatedSecret(aliceSession, keyId, true) val info = generatedSecret(aliceSession, keyId, true)
@ -133,7 +116,7 @@ class QuadSTests : InstrumentedTest {
// Store a secret // Store a secret
val clearSecret = "42".toByteArray().toBase64NoPadding() val clearSecret = "42".toByteArray().toBase64NoPadding()
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret( aliceSession.sharedSecretStorageService.storeSecret(
"secret.of.life", "secret.of.life",
clearSecret, clearSecret,
@ -154,7 +137,7 @@ class QuadSTests : InstrumentedTest {
// Try to decrypt?? // Try to decrypt??
val decryptedSecret = mTestHelper.runBlockingTest { val decryptedSecret = testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret( aliceSession.sharedSecretStorageService.getSecret(
"secret.of.life", "secret.of.life",
null, // default key null, // default key
@ -163,32 +146,32 @@ class QuadSTests : InstrumentedTest {
} }
assertEquals("Secret mismatch", clearSecret, decryptedSecret) assertEquals("Secret mismatch", clearSecret, decryptedSecret)
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
} }
@Test @Test
fun test_SetDefaultLocalEcho() { 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 quadS = aliceSession.sharedSecretStorageService
val TEST_KEY_ID = "my.test.Key" val TEST_KEY_ID = "my.test.Key"
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner) 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 // 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) quadS.setDefaultKey(TEST_KEY_ID)
} }
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
} }
@Test @Test
fun test_StoreSecretWithMultipleKey() { 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 keyId1 = "Key.1"
val key1Info = generatedSecret(aliceSession, keyId1, true) val key1Info = generatedSecret(aliceSession, keyId1, true)
val keyId2 = "Key2" val keyId2 = "Key2"
@ -196,7 +179,7 @@ class QuadSTests : InstrumentedTest {
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret( aliceSession.sharedSecretStorageService.storeSecret(
"my.secret", "my.secret",
mySecretText.toByteArray().toBase64NoPadding(), mySecretText.toByteArray().toBase64NoPadding(),
@ -216,33 +199,33 @@ class QuadSTests : InstrumentedTest {
assertNotNull(encryptedContent?.get(keyId2)) assertNotNull(encryptedContent?.get(keyId2))
// Assert that can decrypt with both keys // Assert that can decrypt with both keys
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1, keyId1,
RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!! RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!!
) )
} }
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId2, keyId2,
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!! RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
) )
} }
mTestHelper.signOutAndClose(aliceSession) testHelper.signOutAndClose(aliceSession)
} }
@Test @Test
fun test_GetSecretWithBadPassphrase() { 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 keyId1 = "Key.1"
val passphrase = "The good pass phrase" val passphrase = "The good pass phrase"
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true) val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.storeSecret( aliceSession.sharedSecretStorageService.storeSecret(
"my.secret", "my.secret",
mySecretText.toByteArray().toBase64NoPadding(), mySecretText.toByteArray().toBase64NoPadding(),
@ -250,19 +233,23 @@ class QuadSTests : InstrumentedTest {
) )
} }
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", try {
keyId1, aliceSession.sharedSecretStorageService.getSecret("my.secret",
RawBytesKeySpec.fromPassphrase( keyId1,
"A bad passphrase", RawBytesKeySpec.fromPassphrase(
key1Info.content?.passphrase?.salt ?: "", "A bad passphrase",
key1Info.content?.passphrase?.iterations ?: 0, key1Info.content?.passphrase?.salt ?: "",
null) key1Info.content?.passphrase?.iterations ?: 0,
) null)
)
} catch (throwable: Throwable) {
assert(throwable is SharedSecretStorageError.BadMac)
}
} }
// Now try with correct key // Now try with correct key
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
aliceSession.sharedSecretStorageService.getSecret("my.secret", aliceSession.sharedSecretStorageService.getSecret("my.secret",
keyId1, keyId1,
RawBytesKeySpec.fromPassphrase( 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 { private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
val accountDataLock = CountDownLatch(1)
var accountData: UserAccountDataEvent? = null var accountData: UserAccountDataEvent? = null
testHelper.waitWithLatch {
val liveAccountData = runBlocking(Dispatchers.Main) { val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
session.accountDataService().getLiveUserAccountDataEvent(type) val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t ->
} if (t?.getOrNull()?.type == type) {
val accountDataObserver = Observer<Optional<UserAccountDataEvent>?> { t -> accountData = t.getOrNull()
if (t?.getOrNull()?.type == type) { it.countDown()
accountData = t.getOrNull() }
accountDataLock.countDown()
} }
liveAccountData.observeForever(accountDataObserver)
} }
GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) }
mTestHelper.await(accountDataLock)
assertNotNull("Account Data type:$type should be found", accountData) assertNotNull("Account Data type:$type should be found", accountData)
return accountData!! return accountData!!
} }
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService val quadS = session.sharedSecretStorageService
val creationInfo = mTestHelper.runBlockingTest { val creationInfo = testHelper.runBlockingTest {
quadS.generateKey(keyId, null, keyId, emptyKeySigner) quadS.generateKey(keyId, null, keyId, emptyKeySigner)
} }
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
if (asDefault) { if (asDefault) {
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
quadS.setDefaultKey(keyId) quadS.setDefaultKey(keyId)
} }
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID) 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 { private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
val quadS = session.sharedSecretStorageService val quadS = session.sharedSecretStorageService
val creationInfo = mTestHelper.runBlockingTest { val creationInfo = testHelper.runBlockingTest {
quadS.generateKeyWithPassphrase( quadS.generateKeyWithPassphrase(
keyId, keyId,
keyId, keyId,
@ -331,7 +312,7 @@ class QuadSTests : InstrumentedTest {
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
if (asDefault) { if (asDefault) {
mTestHelper.runBlockingTest { testHelper.runBlockingTest {
quadS.setDefaultKey(keyId) quadS.setDefaultKey(keyId)
} }
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID) assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)

View file

@ -53,12 +53,12 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SASTest : InstrumentedTest { class SASTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
@Test @Test
fun test_aliceStartThenAliceCancel() { fun test_aliceStartThenAliceCancel() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -83,7 +83,7 @@ class SASTest : InstrumentedTest {
val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!) val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!)
assertNotNull("Alice should have a started transaction", aliceKeyTx) assertNotNull("Alice should have a started transaction", aliceKeyTx)
mTestHelper.await(bobTxCreatedLatch) testHelper.await(bobTxCreatedLatch)
bobVerificationService.removeListener(bobListener) bobVerificationService.removeListener(bobListener)
val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID) val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)
@ -116,7 +116,7 @@ class SASTest : InstrumentedTest {
bobVerificationService.addListener(bobListener2) bobVerificationService.addListener(bobListener2)
aliceSasTx.cancel(CancelCode.User) 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 alice side", aliceSasTx.state is VerificationTxState.Cancelled)
assertTrue("Should be cancelled on bob side", bobSasTx.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(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID)) assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
@Test @Test
fun test_key_agreement_protocols_must_include_curve25519() { fun test_key_agreement_protocols_must_include_curve25519() {
fail("Not passing for the moment") fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!! val bobSession = cryptoTestData.secondSession!!
@ -186,17 +186,17 @@ class SASTest : InstrumentedTest {
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols) 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) assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
@Test @Test
fun test_key_agreement_macs_Must_include_hmac_sha256() { fun test_key_agreement_macs_Must_include_hmac_sha256() {
fail("Not passing for the moment") fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!! val bobSession = cryptoTestData.secondSession!!
@ -223,18 +223,18 @@ class SASTest : InstrumentedTest {
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac) fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac)
mTestHelper.await(cancelLatch) testHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!! val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
@Test @Test
fun test_key_agreement_short_code_include_decimal() { fun test_key_agreement_short_code_include_decimal() {
fail("Not passing for the moment") fail("Not passing for the moment")
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val bobSession = cryptoTestData.secondSession!! val bobSession = cryptoTestData.secondSession!!
@ -261,12 +261,12 @@ class SASTest : InstrumentedTest {
fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes) fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes)
mTestHelper.await(cancelLatch) testHelper.await(cancelLatch)
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!! val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) 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, 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. // If a device has two verifications in progress with the same device, then it should cancel both verifications.
@Test @Test
fun test_aliceStartTwoRequests() { fun test_aliceStartTwoRequests() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession 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)
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceCreatedLatch) testHelper.await(aliceCreatedLatch)
mTestHelper.await(aliceCancelledLatch) testHelper.await(aliceCancelledLatch)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
/** /**
@ -343,7 +343,7 @@ class SASTest : InstrumentedTest {
*/ */
@Test @Test
fun test_aliceAndBobAgreement() { fun test_aliceAndBobAgreement() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -383,7 +383,7 @@ class SASTest : InstrumentedTest {
val bobUserId = bobSession.myUserId val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceAcceptedLatch) testHelper.await(aliceAcceptedLatch)
assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false) 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)) assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
} }
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
@Test @Test
fun test_aliceAndBobSASCode() { fun test_aliceAndBobSASCode() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -444,8 +444,8 @@ class SASTest : InstrumentedTest {
val bobUserId = bobSession.myUserId val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceSASLatch) testHelper.await(aliceSASLatch)
mTestHelper.await(bobSASLatch) testHelper.await(bobSASLatch)
val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction
val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, 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), assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
bobTx.getShortCodeRepresentation(SasMode.DECIMAL)) bobTx.getShortCodeRepresentation(SasMode.DECIMAL))
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
@Test @Test
fun test_happyPath() { fun test_happyPath() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -520,8 +520,8 @@ class SASTest : InstrumentedTest {
val bobUserId = bobSession.myUserId val bobUserId = bobSession.myUserId
val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId
aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null)
mTestHelper.await(aliceSASLatch) testHelper.await(aliceSASLatch)
mTestHelper.await(bobSASLatch) testHelper.await(bobSASLatch)
// Assert that devices are verified // Assert that devices are verified
val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId) 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("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) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
@Test @Test
fun test_ConcurrentStart() { fun test_ConcurrentStart() {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession val bobSession = cryptoTestData.secondSession
@ -553,8 +553,8 @@ class SASTest : InstrumentedTest {
var requestID: String? = null var requestID: String? = null
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
requestID = prAlicePOV?.transactionId requestID = prAlicePOV?.transactionId
Log.v("TEST", "== alicePOV is $prAlicePOV") Log.v("TEST", "== alicePOV is $prAlicePOV")
@ -564,8 +564,8 @@ class SASTest : InstrumentedTest {
Log.v("TEST", "== requestID is $requestID") Log.v("TEST", "== requestID is $requestID")
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
Log.v("TEST", "== prBobPOV is $prBobPOV") Log.v("TEST", "== prBobPOV is $prBobPOV")
prBobPOV?.transactionId == requestID prBobPOV?.transactionId == requestID
@ -579,8 +579,8 @@ class SASTest : InstrumentedTest {
) )
// wait for alice to get the ready // wait for alice to get the ready
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
Log.v("TEST", "== prAlicePOV is $prAlicePOV") Log.v("TEST", "== prAlicePOV is $prAlicePOV")
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
@ -606,22 +606,22 @@ class SASTest : InstrumentedTest {
var alicePovTx: SasVerificationTransaction? var alicePovTx: SasVerificationTransaction?
var bobPovTx: SasVerificationTransaction? var bobPovTx: SasVerificationTransaction?
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction
Log.v("TEST", "== alicePovTx is $alicePovTx") Log.v("TEST", "== alicePovTx is $alicePovTx")
alicePovTx?.state == VerificationTxState.ShortCodeReady alicePovTx?.state == VerificationTxState.ShortCodeReady
} }
} }
// wait for alice to get the ready // wait for alice to get the ready
mTestHelper.waitWithLatch { testHelper.waitWithLatch {
mTestHelper.retryPeriodicallyWithLatch(it) { testHelper.retryPeriodicallyWithLatch(it) {
bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction
Log.v("TEST", "== bobPovTx is $bobPovTx") Log.v("TEST", "== bobPovTx is $bobPovTx")
bobPovTx?.state == VerificationTxState.ShortCodeReady bobPovTx?.state == VerificationTxState.ShortCodeReady
} }
} }
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
} }

View file

@ -40,8 +40,8 @@ import kotlin.coroutines.resume
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
class VerificationTest : InstrumentedTest { class VerificationTest : InstrumentedTest {
private val mTestHelper = CommonTestHelper(context()) private val testHelper = CommonTestHelper(context())
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) private val cryptoTestHelper = CryptoTestHelper(testHelper)
data class ExpectedResult( data class ExpectedResult(
val sasIsSupported: Boolean = false, val sasIsSupported: Boolean = false,
@ -155,12 +155,12 @@ class VerificationTest : InstrumentedTest {
bobSupportedMethods: List<VerificationMethod>, bobSupportedMethods: List<VerificationMethod>,
expectedResultForAlice: ExpectedResult, expectedResultForAlice: ExpectedResult,
expectedResultForBob: ExpectedResult) { expectedResultForBob: ExpectedResult) {
val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val bobSession = cryptoTestData.secondSession!! val bobSession = cryptoTestData.secondSession!!
mTestHelper.doSync<Unit> { callback -> testHelper.doSync<Unit> { callback ->
aliceSession.cryptoService().crossSigningService() aliceSession.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -176,7 +176,7 @@ class VerificationTest : InstrumentedTest {
}, callback) }, callback)
} }
mTestHelper.doSync<Unit> { callback -> testHelper.doSync<Unit> { callback ->
bobSession.cryptoService().crossSigningService() bobSession.cryptoService().crossSigningService()
.initializeCrossSigning( .initializeCrossSigning(
object : UserInteractiveAuthInterceptor { object : UserInteractiveAuthInterceptor {
@ -234,7 +234,7 @@ class VerificationTest : InstrumentedTest {
val bobUserId = bobSession.myUserId val bobUserId = bobSession.myUserId
// Step 1: Alice starts a verification request // Step 1: Alice starts a verification request
aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId) aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId)
mTestHelper.await(latch) testHelper.await(latch)
aliceReadyPendingVerificationRequest!!.let { pr -> aliceReadyPendingVerificationRequest!!.let { pr ->
pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported
@ -248,6 +248,6 @@ class VerificationTest : InstrumentedTest {
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
} }
cryptoTestData.cleanUp(mTestHelper) cryptoTestData.cleanUp(testHelper)
} }
} }

View file

@ -45,7 +45,7 @@ object RoomDataHelper {
content: Content? = null, content: Content? = null,
prevContent: Content? = null, prevContent: Content? = null,
sender: String = FAKE_TEST_SENDER, sender: String = FAKE_TEST_SENDER,
stateKey: String = FAKE_TEST_SENDER stateKey: String? = null
): Event { ): Event {
return Event( return Event(
type = type, type = type,
@ -64,6 +64,6 @@ object RoomDataHelper {
private fun createFakeRoomMemberEvent(): Event { private fun createFakeRoomMemberEvent(): Event {
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() 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)
} }
} }

View file

@ -65,14 +65,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
message, message,
numberOfMessagesToSend) numberOfMessagesToSend)
// Alice clear the cache // Alice clear the cache and restart the sync
commonTestHelper.runBlockingTest { commonTestHelper.clearCacheAndSync(aliceSession)
aliceSession.clearCache()
}
// And restarts the sync
aliceSession.startSync(true)
val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30)) val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30))
aliceTimeline.start() aliceTimeline.start()

View file

@ -24,14 +24,10 @@ import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.extensions.orFalse 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.api.session.search.SearchResult
import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestData import org.matrix.android.sdk.common.CryptoTestData
import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.CryptoTestHelper
import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class) @RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM) @FixMethodOrder(MethodSorters.JVM)
@ -84,24 +80,12 @@ class SearchMessagesTest : InstrumentedTest {
val aliceSession = cryptoTestData.firstSession val aliceSession = cryptoTestData.firstSession
val aliceRoomId = cryptoTestData.roomId val aliceRoomId = cryptoTestData.roomId
val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! 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<MessageContent>()?.body?.startsWith(MESSAGE).orFalse() } == 2
}
aliceTimeline.addListener(eventListener)
commonTestHelper.sendTextMessage( commonTestHelper.sendTextMessage(
roomFromAlicePOV, roomFromAlicePOV,
MESSAGE, MESSAGE,
2) 2)
commonTestHelper.await(lock)
val data = commonTestHelper.runBlockingTest { val data = commonTestHelper.runBlockingTest {
block.invoke(cryptoTestData) block.invoke(cryptoTestData)
} }
@ -114,7 +98,6 @@ class SearchMessagesTest : InstrumentedTest {
}.orFalse() }.orFalse()
) )
aliceTimeline.removeAllListeners()
cryptoTestData.cleanUp(commonTestHelper) cryptoTestData.cleanUp(commonTestHelper)
} }
} }

View file

@ -16,9 +16,7 @@
package org.matrix.android.sdk.session.space package org.matrix.android.sdk.session.space
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
@ -50,18 +48,15 @@ class SpaceCreationTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context()) private val commonTestHelper = CommonTestHelper(context())
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun createSimplePublicSpace() { fun createSimplePublicSpace() {
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true)) val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
val roomName = "My Space" val roomName = "My Space"
val topic = "A public space for test" val topic = "A public space for test"
var spaceId: String = "" var spaceId: String = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { spaceId = session.spaceService().createSpace(roomName, topic, null, true)
spaceId = session.spaceService().createSpace(roomName, topic, null, true) // wait a bit to let the summary update it self :/
// wait a bit to let the summary update it self :/ it.countDown()
it.countDown()
}
} }
val syncedSpace = session.spaceService().getSpace(spaceId) val syncedSpace = session.spaceService().getSpace(spaceId)
@ -134,7 +129,6 @@ class SpaceCreationTest : InstrumentedTest {
} }
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun testSimplePublicSpaceWithChildren() { fun testSimplePublicSpaceWithChildren() {
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true)) val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true)) val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
@ -148,50 +142,40 @@ class SpaceCreationTest : InstrumentedTest {
// create a room // create a room
var firstChild: String? = null var firstChild: String? = null
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { firstChild = aliceSession.createRoom(CreateRoomParams().apply {
firstChild = aliceSession.createRoom(CreateRoomParams().apply { this.name = "FirstRoom"
this.name = "FirstRoom" this.topic = "Description of first room"
this.topic = "Description of first room" this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT })
}) it.countDown()
it.countDown()
}
} }
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true)
syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true) it.countDown()
it.countDown()
}
} }
var secondChild: String? = null var secondChild: String? = null
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { secondChild = aliceSession.createRoom(CreateRoomParams().apply {
secondChild = aliceSession.createRoom(CreateRoomParams().apply { this.name = "SecondRoom"
this.name = "SecondRoom" this.topic = "Description of second room"
this.topic = "Description of second room" this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT })
}) it.countDown()
it.countDown()
}
} }
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true)
syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true) it.countDown()
it.countDown()
}
} }
// Try to join from bob, it's a public space no need to invite // Try to join from bob, it's a public space no need to invite
var joinResult: JoinSpaceResult? = null var joinResult: JoinSpaceResult? = null
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { joinResult = bobSession.spaceService().joinSpace(spaceId)
joinResult = bobSession.spaceService().joinSpace(spaceId) // wait a bit to let the summary update it self :/
// wait a bit to let the summary update it self :/ it.countDown()
it.countDown()
}
} }
assertEquals(JoinSpaceResult.Success, joinResult) assertEquals(JoinSpaceResult.Success, joinResult)

View file

@ -18,9 +18,6 @@ package org.matrix.android.sdk.session.space
import android.util.Log import android.util.Log
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
@ -56,43 +53,34 @@ class SpaceHierarchyTest : InstrumentedTest {
private val commonTestHelper = CommonTestHelper(context()) private val commonTestHelper = CommonTestHelper(context())
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun createCanonicalChildRelation() { fun createCanonicalChildRelation() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val session = commonTestHelper.createAccount("John", SessionTestParams(true))
val spaceName = "My Space" val spaceName = "My Space"
val topic = "A public space for test" val topic = "A public space for test"
var spaceId: String = "" var spaceId = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
spaceId = session.spaceService().createSpace(spaceName, topic, null, true) it.countDown()
it.countDown()
}
} }
val syncedSpace = session.spaceService().getSpace(spaceId) val syncedSpace = session.spaceService().getSpace(spaceId)
var roomId: String = "" var roomId = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { roomId = session.createRoom(CreateRoomParams().apply { name = "General" })
roomId = session.createRoom(CreateRoomParams().apply { name = "General" }) it.countDown()
it.countDown()
}
} }
val viaServers = listOf(session.sessionParams.homeServerHost ?: "") val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { syncedSpace!!.addChildren(roomId, viaServers, null, true)
syncedSpace!!.addChildren(roomId, viaServers, null, true) it.countDown()
it.countDown()
}
} }
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers) it.countDown()
it.countDown()
}
} }
Thread.sleep(9000) Thread.sleep(9000)
@ -181,7 +169,6 @@ class SpaceHierarchyTest : InstrumentedTest {
// } // }
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun testFilteringBySpace() { fun testFilteringBySpace() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@ -205,29 +192,23 @@ class SpaceHierarchyTest : InstrumentedTest {
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId) val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
val viaServers = listOf(session.sessionParams.homeServerHost ?: "") val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) it.countDown()
it.countDown()
}
} }
// Create orphan rooms // Create orphan rooms
var orphan1 = "" var orphan1 = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" })
orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" }) it.countDown()
it.countDown()
}
} }
var orphan2 = "" var orphan2 = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" })
orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" }) it.countDown()
it.countDown()
}
} }
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) }) 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 // Add a non canonical child and check that it does not appear as orphan
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" })
val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" }) spaceA!!.addChildren(a3, viaServers, null, false)
spaceA!!.addChildren(a3, viaServers, null, false) it.countDown()
it.countDown()
}
} }
Thread.sleep(2_000) Thread.sleep(2_000)
@ -265,7 +244,6 @@ class SpaceHierarchyTest : InstrumentedTest {
} }
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun testBreakCycle() { fun testBreakCycle() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@ -283,20 +261,16 @@ class SpaceHierarchyTest : InstrumentedTest {
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId) val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
val viaServers = listOf(session.sessionParams.homeServerHost ?: "") val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) it.countDown()
it.countDown()
}
} }
// add back A as subspace of C // add back A as subspace of C
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId) spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true) it.countDown()
it.countDown()
}
} }
Thread.sleep(1000) Thread.sleep(1000)
@ -313,7 +287,6 @@ class SpaceHierarchyTest : InstrumentedTest {
} }
@Test @Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun testLiveFlatChildren() { fun testLiveFlatChildren() {
val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val session = commonTestHelper.createAccount("John", SessionTestParams(true))
@ -336,12 +309,14 @@ class SpaceHierarchyTest : InstrumentedTest {
session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers) session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
} }
val flatAChildren = runBlocking(Dispatchers.Main) { val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) Triple("C1", true /*auto-join*/, true/*canonical*/),
} Triple("C2", true, true)
))
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
val childObserver = object : Observer<List<RoomSummary>> { val childObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(children: List<RoomSummary>?) { override fun onChanged(children: List<RoomSummary>?) {
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}") // 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 // add C as subspace of B
runBlocking { val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId) spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
}
// C1 and C2 should be in flatten child of A now // 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 // Test part one of the rooms
@ -376,7 +344,7 @@ class SpaceHierarchyTest : InstrumentedTest {
val bRoom = session.getRoom(bRoomId) val bRoom = session.getRoom(bRoomId)
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId)
val childObserver = object : Observer<List<RoomSummary>> { val childObserver = object : Observer<List<RoomSummary>> {
override fun onChanged(children: List<RoomSummary>?) { override fun onChanged(children: List<RoomSummary>?) {
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}") System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
@ -389,13 +357,10 @@ class SpaceHierarchyTest : InstrumentedTest {
} }
// part from b room // part from b room
runBlocking { bRoom!!.leave(null)
bRoom!!.leave(null)
}
// The room should have disapear from flat children // The room should have disapear from flat children
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) } flatAChildren.observeForever(childObserver)
} }
commonTestHelper.signOutAndClose(session) commonTestHelper.signOutAndClose(session)
} }
@ -404,94 +369,66 @@ class SpaceHierarchyTest : InstrumentedTest {
val roomIds: List<String> val roomIds: List<String>
) )
@Suppress("EXPERIMENTAL_API_USAGE")
private fun createPublicSpace(session: Session, private fun createPublicSpace(session: Session,
spaceName: String, spaceName: String,
childInfo: List<Triple<String, Boolean, Boolean?>> childInfo: List<Triple<String, Boolean, Boolean?>>
/** Name, auto-join, canonical*/ /** Name, auto-join, canonical*/
): TestSpaceCreationResult { ): TestSpaceCreationResult {
var spaceId = "" var spaceId = ""
commonTestHelper.waitWithLatch { var roomIds: List<String> = emptyList()
GlobalScope.launch { commonTestHelper.waitWithLatch { latch ->
spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true) spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
it.countDown() val syncedSpace = session.spaceService().getSpace(spaceId)
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
roomIds = childInfo.map { entry ->
session.createRoom(CreateRoomParams().apply { name = entry.first })
} }
} roomIds.forEachIndexed { index, roomId ->
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 {
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
val canonical = childInfo[index].third val canonical = childInfo[index].third
if (canonical != null) { if (canonical != null) {
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
} }
} }
latch.countDown()
} }
return TestSpaceCreationResult(spaceId, roomIds) return TestSpaceCreationResult(spaceId, roomIds)
} }
@Suppress("EXPERIMENTAL_API_USAGE")
private fun createPrivateSpace(session: Session, private fun createPrivateSpace(session: Session,
spaceName: String, spaceName: String,
childInfo: List<Triple<String, Boolean, Boolean?>> childInfo: List<Triple<String, Boolean, Boolean?>>
/** Name, auto-join, canonical*/ /** Name, auto-join, canonical*/
): TestSpaceCreationResult { ): TestSpaceCreationResult {
var spaceId = "" var spaceId = ""
commonTestHelper.waitWithLatch { var roomIds: List<String> = emptyList()
GlobalScope.launch { commonTestHelper.waitWithLatch { latch ->
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false) spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
it.countDown() val syncedSpace = session.spaceService().getSpace(spaceId)
} val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
} roomIds =
childInfo.map { entry ->
val syncedSpace = session.spaceService().getSpace(spaceId) val homeServerCapabilities = session
val viaServers = listOf(session.sessionParams.homeServerHost ?: "") .getHomeServerCapabilities()
session.createRoom(CreateRoomParams().apply {
val roomIds = name = entry.first
childInfo.map { entry -> this.featurePreset = RestrictedRoomPreset(
var roomId = "" homeServerCapabilities,
commonTestHelper.waitWithLatch { listOf(
GlobalScope.launch { RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
val homeServerCapabilities = session )
.getHomeServerCapabilities() )
roomId = session.createRoom(CreateRoomParams().apply { })
name = entry.first
this.featurePreset = RestrictedRoomPreset(
homeServerCapabilities,
listOf(
RoomJoinRulesAllowEntry.restrictedToRoom(spaceId)
)
)
})
it.countDown()
}
} }
roomId roomIds.forEachIndexed { index, roomId ->
}
roomIds.forEachIndexed { index, roomId ->
runBlocking {
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
val canonical = childInfo[index].third val canonical = childInfo[index].third
if (canonical != null) { if (canonical != null) {
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
} }
} }
latch.countDown()
} }
return TestSpaceCreationResult(spaceId, roomIds) return TestSpaceCreationResult(spaceId, roomIds)
} }
@ -559,11 +496,9 @@ class SpaceHierarchyTest : InstrumentedTest {
var bobRoomId = "" var bobRoomId = ""
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" })
bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId)
bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) it.countDown()
it.countDown()
}
} }
commonTestHelper.runBlockingTest { commonTestHelper.runBlockingTest {
@ -577,10 +512,8 @@ class SpaceHierarchyTest : InstrumentedTest {
} }
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) it.countDown()
it.countDown()
}
} }
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
@ -600,19 +533,17 @@ class SpaceHierarchyTest : InstrumentedTest {
// Let's now try to make alice admin of the room // Let's now try to make alice admin of the room
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { val room = bobSession.getRoom(bobRoomId)!!
val room = bobSession.getRoom(bobRoomId)!! val currentPLContent = room
val currentPLContent = room .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) ?.let { it.content.toModel<PowerLevelsContent>() }
?.let { it.content.toModel<PowerLevelsContent>() }
val newPowerLevelsContent = currentPLContent val newPowerLevelsContent = currentPLContent
?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value)
?.toContent() ?.toContent()
room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!)
it.countDown() it.countDown()
}
} }
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->
@ -627,10 +558,8 @@ class SpaceHierarchyTest : InstrumentedTest {
} }
commonTestHelper.waitWithLatch { commonTestHelper.waitWithLatch {
GlobalScope.launch { aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: ""))
aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) it.countDown()
it.countDown()
}
} }
commonTestHelper.waitWithLatch { latch -> commonTestHelper.waitWithLatch { latch ->

View file

@ -20,6 +20,7 @@ import android.content.Context
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerFactory
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.auth.AuthenticationService 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.ApiInterceptor
import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.network.UserAgentHolder
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean 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 sessionManager: SessionManager
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
@Inject internal lateinit var apiInterceptor: ApiInterceptor @Inject internal lateinit var apiInterceptor: ApiInterceptor
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
init { init {
Monarchy.init(context) Monarchy.init(context)
DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this) DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) { 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) ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
} }
@ -77,6 +84,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return legacySessionImporter return legacySessionImporter
} }
fun workerFactory(): WorkerFactory = matrixWorkerFactory
fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) { fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) {
apiInterceptor.addListener(path, listener) apiInterceptor.addListener(path, listener)
} }

View file

@ -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.Event
import org.matrix.android.sdk.api.session.events.model.EventType 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.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.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore 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 org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject import javax.inject.Inject
internal class CancelGossipRequestWorker(context: Context, internal class CancelGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
params: WorkerParameters) : SessionSafeCoroutineWorker<CancelGossipRequestWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<CancelGossipRequestWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -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.Event
import org.matrix.android.sdk.api.session.events.model.EventType 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.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.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class SendGossipRequestWorker(context: Context, internal class SendGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
params: WorkerParameters) : SessionSafeCoroutineWorker<SendGossipRequestWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<SendGossipRequestWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -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.Event
import org.matrix.android.sdk.api.session.events.model.EventType 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.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.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class SendGossipWorker(context: Context, internal class SendGossipWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
params: WorkerParameters) : SessionSafeCoroutineWorker<SendGossipWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<SendGossipWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -25,6 +25,7 @@ import io.realm.kotlin.where
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo 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.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class UpdateTrustWorker(context: Context, internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
params: WorkerParameters) : SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -20,6 +20,7 @@ import androidx.work.Data
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.shouldBeRetried 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.crypto.tasks.SendVerificationMessageTask
import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker
@ -33,9 +34,8 @@ import javax.inject.Inject
* Possible previous worker: None * Possible previous worker: None
* Possible next worker : None * Possible next worker : None
*/ */
internal class SendVerificationMessageWorker(context: Context, internal class SendVerificationMessageWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
params: WorkerParameters) : SessionSafeCoroutineWorker<SendVerificationMessageWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<SendVerificationMessageWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -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.task.TaskExecutor
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.system.SystemModule import org.matrix.android.sdk.internal.util.system.SystemModule
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import java.io.File import java.io.File
@ -86,6 +87,8 @@ internal interface MatrixComponent {
fun sessionManager(): SessionManager fun sessionManager(): SessionManager
fun matrixWorkerFactory(): MatrixWorkerFactory
fun inject(matrix: Matrix) fun inject(matrix: Matrix)
@Component.Factory @Component.Factory

View file

@ -20,6 +20,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import org.matrix.android.sdk.internal.session.MockHttpInterceptor import org.matrix.android.sdk.internal.session.MockHttpInterceptor
import org.matrix.android.sdk.internal.session.TestInterceptor 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 @Module
internal object NoOpTestModule { internal object NoOpTestModule {
@ -28,4 +30,11 @@ internal object NoOpTestModule {
@JvmStatic @JvmStatic
@MockHttpInterceptor @MockHttpInterceptor
fun providesTestInterceptor(): TestInterceptor? = null fun providesTestInterceptor(): TestInterceptor? = null
@Provides
@JvmStatic
@MatrixScope
fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver {
return DefaultBackgroundDetectionObserver()
}
} }

View file

@ -17,24 +17,38 @@
package org.matrix.android.sdk.internal.di package org.matrix.android.sdk.internal.di
import android.content.Context import android.content.Context
import androidx.lifecycle.Observer
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.ListenableWorker import androidx.work.ListenableWorker
import androidx.work.NetworkType import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkRequest 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 java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@SessionScope
internal class WorkManagerProvider @Inject constructor( internal class WorkManagerProvider @Inject constructor(
context: Context, 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 private val tag = MATRIX_SDK_TAG_PREFIX + sessionId
val workManager = WorkManager.getInstance(context) val workManager = WorkManager.getInstance(context)
init {
checkIfWorkerFactoryIsSetup()
}
/** /**
* Create a OneTimeWorkRequestBuilder, with the Matrix SDK tag * 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<MatrixWorkerFactory.CheckFactoryWorker>().build()
workManager.enqueue(checkWorkerRequest)
val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id)
val observer = object : Observer<WorkInfo> {
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 { companion object {
private const val MATRIX_SDK_TAG_PREFIX = "MatrixSDK-" private const val MATRIX_SDK_TAG_PREFIX = "MatrixSDK-"

View file

@ -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.MessageImageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.util.MimeTypes 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.attachments.MXEncryptedAttachments
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
@ -63,8 +64,8 @@ private data class NewAttachmentAttributes(
* Possible previous worker: None * Possible previous worker: None
* Possible next worker : Always [MultipleEventSendingDispatcherWorker] * Possible next worker : Always [MultipleEventSendingDispatcherWorker]
*/ */
internal class UploadContentWorker(val context: Context, params: WorkerParameters) : internal class UploadContentWorker(val context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<UploadContentWorker.Params>(context, params, Params::class.java) { SessionSafeCoroutineWorker<UploadContentWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.group
import android.content.Context import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass 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.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.SessionWorkerParams
@ -28,8 +29,8 @@ import javax.inject.Inject
* Possible previous worker: None * Possible previous worker: None
* Possible next worker : None * Possible next worker : None
*/ */
internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : internal class GetGroupDataWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<GetGroupDataWorker.Params>(context, params, Params::class.java) { SessionSafeCoroutineWorker<GetGroupDataWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -19,13 +19,14 @@ import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.Failure 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.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject import javax.inject.Inject
internal class AddPusherWorker(context: Context, params: WorkerParameters) : internal class AddPusherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<AddPusherWorker.Params>(context, params, Params::class.java) { SessionSafeCoroutineWorker<AddPusherWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -21,6 +21,7 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.room.send.SendState 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.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.session.content.UploadContentWorker import org.matrix.android.sdk.internal.session.content.UploadContentWorker
@ -38,8 +39,8 @@ import javax.inject.Inject
* Possible previous worker: Always [UploadContentWorker] * Possible previous worker: Always [UploadContentWorker]
* Possible next worker : None, but it will post new work to send events, encrypted or not * Possible next worker : None, but it will post new work to send events, encrypted or not
*/ */
internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters) : internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<MultipleEventSendingDispatcherWorker.Params>(context, params, Params::class.java) { SessionSafeCoroutineWorker<MultipleEventSendingDispatcherWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -19,6 +19,7 @@ import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.Failure 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.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.SessionComponent
@ -32,8 +33,8 @@ import javax.inject.Inject
* Possible previous worker: None * Possible previous worker: None
* Possible next worker : None * Possible next worker : None
*/ */
internal class RedactEventWorker(context: Context, params: WorkerParameters) : internal class RedactEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
SessionSafeCoroutineWorker<RedactEventWorker.Params>(context, params, Params::class.java) { SessionSafeCoroutineWorker<RedactEventWorker.Params>(context, params, sessionManager, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -23,6 +23,7 @@ import io.realm.RealmConfiguration
import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.send.SendState 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.crypto.tasks.SendEventTask
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.SessionComponent
@ -38,9 +39,8 @@ import javax.inject.Inject
* Possible previous worker: [EncryptEventWorker] or first worker * Possible previous worker: [EncryptEventWorker] or first worker
* Possible next worker : None * Possible next worker : None
*/ */
internal class SendEventWorker(context: Context, internal class SendEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
params: WorkerParameters) : SessionSafeCoroutineWorker<SendEventWorker.Params>(context, params, sessionManager, Params::class.java) {
SessionSafeCoroutineWorker<SendEventWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(

View file

@ -25,9 +25,12 @@ import javax.inject.Inject
internal class SyncTokenStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) { internal class SyncTokenStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) {
fun getLastToken(): String? { 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 it.where(SyncEntity::class.java).findFirst()?.nextBatch
} }
return token
} }
fun saveToken(realm: Realm, token: String?) { fun saveToken(realm: Realm, token: String?) {

View file

@ -21,8 +21,8 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.isTokenError 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.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.SessionComponent
import org.matrix.android.sdk.internal.session.sync.SyncPresence import org.matrix.android.sdk.internal.session.sync.SyncPresence
import org.matrix.android.sdk.internal.session.sync.SyncTask 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 previous worker: None
* Possible next worker : None * Possible next worker : None
*/ */
internal class SyncWorker(context: Context, internal class SyncWorker(context: Context, workerParameters: WorkerParameters, sessionManager: SessionManager) :
workerParameters: WorkerParameters SessionSafeCoroutineWorker<SyncWorker.Params>(context, workerParameters, sessionManager, Params::class.java) {
) : SessionSafeCoroutineWorker<SyncWorker.Params>(context, workerParameters, Params::class.java) {
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class Params( internal data class Params(
@ -56,7 +55,6 @@ internal class SyncWorker(context: Context,
@Inject lateinit var syncTask: SyncTask @Inject lateinit var syncTask: SyncTask
@Inject lateinit var taskExecutor: TaskExecutor @Inject lateinit var taskExecutor: TaskExecutor
@Inject lateinit var networkConnectivityChecker: NetworkConnectivityChecker
@Inject lateinit var workManagerProvider: WorkManagerProvider @Inject lateinit var workManagerProvider: WorkManagerProvider
override fun injectWith(injector: SessionComponent) { override fun injectWith(injector: SessionComponent) {

View file

@ -18,26 +18,32 @@ package org.matrix.android.sdk.internal.util
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import org.matrix.android.sdk.internal.di.MatrixScope
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject
/** interface BackgroundDetectionObserver : DefaultLifecycleObserver {
* To be attached to ProcessLifecycleOwner lifecycle val isInBackground: Boolean
*/
@MatrixScope
internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecycleObserver {
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 set
private val listeners = LinkedHashSet<Listener>() private val listeners = LinkedHashSet<BackgroundDetectionObserver.Listener>()
fun register(listener: Listener) { override fun register(listener: BackgroundDetectionObserver.Listener) {
listeners.add(listener) listeners.add(listener)
} }
fun unregister(listener: Listener) { override fun unregister(listener: BackgroundDetectionObserver.Listener) {
listeners.remove(listener) listeners.remove(listener)
} }
@ -52,9 +58,4 @@ internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecy
isInBackground = true isInBackground = true
listeners.forEach { it.onMoveToBackground() } listeners.forEach { it.onMoveToBackground() }
} }
interface Listener {
fun onMoveToForeground()
fun onMoveToBackground()
}
} }

View file

@ -17,16 +17,33 @@
package org.matrix.android.sdk.internal.worker package org.matrix.android.sdk.internal.worker
import android.content.Context import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.ListenableWorker import androidx.work.ListenableWorker
import androidx.work.WorkerFactory import androidx.work.WorkerFactory
import androidx.work.WorkerParameters 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider
class MatrixWorkerFactory @Inject constructor( /**
private val workerFactories: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<DelegateWorkerFactory>> * This factory is responsible of creating Workers by giving the session manager.
) : WorkerFactory() { * 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( override fun createWorker(
appContext: Context, appContext: Context,
@ -34,11 +51,61 @@ class MatrixWorkerFactory @Inject constructor(
workerParameters: WorkerParameters workerParameters: WorkerParameters
): ListenableWorker? { ): ListenableWorker? {
Timber.d("MatrixWorkerFactory.createWorker for $workerClassName") 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) } * This worker is launched by the factory with the isCreatedByMatrixWorkerFactory flag to true.
val factoryProvider = foundEntry?.value * If the MatrixWorkerFactory is not set up, it will default to the other constructor and it will throw
?: throw IllegalArgumentException("unknown worker class name: $workerClassName") */
return factoryProvider.get().create(appContext, workerParameters) 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()
}
}
} }
} }

View file

@ -22,6 +22,7 @@ import androidx.work.CoroutineWorker
import androidx.work.Data import androidx.work.Data
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass 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.session.SessionComponent
import timber.log.Timber import timber.log.Timber
@ -33,6 +34,7 @@ import timber.log.Timber
internal abstract class SessionSafeCoroutineWorker<PARAM : SessionWorkerParams>( internal abstract class SessionSafeCoroutineWorker<PARAM : SessionWorkerParams>(
context: Context, context: Context,
workerParameters: WorkerParameters, workerParameters: WorkerParameters,
private val sessionManager: SessionManager,
private val paramClass: Class<PARAM> private val paramClass: Class<PARAM>
) : CoroutineWorker(context, workerParameters) { ) : CoroutineWorker(context, workerParameters) {
@ -48,7 +50,7 @@ internal abstract class SessionSafeCoroutineWorker<PARAM : SessionWorkerParams>(
.also { Timber.e("Unable to parse work parameters") } .also { Timber.e("Unable to parse work parameters") }
return try { return try {
val sessionComponent = getSessionComponent(params.sessionId) val sessionComponent = sessionManager.getSessionComponent(params.sessionId)
?: return buildErrorResult(params, "No session") ?: return buildErrorResult(params, "No session")
// Make sure to inject before handling error as you may need some dependencies to process them. // Make sure to inject before handling error as you may need some dependencies to process them.

View file

@ -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)
}

View file

@ -220,6 +220,7 @@ class VectorApplication :
override fun getWorkManagerConfiguration(): WorkConfiguration { override fun getWorkManagerConfiguration(): WorkConfiguration {
return WorkConfiguration.Builder() return WorkConfiguration.Builder()
.setWorkerFactory(Matrix.getInstance(this.appContext).workerFactory())
.setExecutor(Executors.newCachedThreadPool()) .setExecutor(Executors.newCachedThreadPool())
.build() .build()
} }