diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt new file mode 100644 index 0000000000..9f8cbb2d44 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CommonTestHelper.kt @@ -0,0 +1,407 @@ +/* + * Copyright 2016 OpenMarket Ltd + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +// import android.content.Context +// import android.net.Uri +// import androidx.test.InstrumentationRegistry +// +// import org.junit.Assert +// +// import java.util.ArrayList +// import java.util.HashMap +// import java.util.UUID +// import java.util.concurrent.CountDownLatch +// import java.util.concurrent.TimeUnit +// +// import im.vector.matrix.android.api.Matrix +// import im.vector.matrix.android.api.auth.data.Credentials +// import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig +// import im.vector.matrix.android.api.session.Session +// import im.vector.matrix.android.internal.auth.registration.AuthParams +// import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse +// import im.vector.matrix.android.internal.auth.registration.RegistrationParams +// import io.realm.internal.android.JsonUtils +// +// +// /** +// * This class exposes methods to be used in common cases +// * Registration, login, Sync, Sending messages... +// */ +// class CommonTestHelper { +// +// @Throws(InterruptedException::class) +// fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session { +// return createAccount(userNamePrefix, TestConstants.PASSWORD, testParams) +// } +// +// @Throws(InterruptedException::class) +// fun logIntoAccount(userId: String, testParams: SessionTestParams): Session { +// return logIntoAccount(userId, TestConstants.PASSWORD, testParams) +// } +// +// /** +// * Create a Home server configuration, with Http connection allowed for test +// */ +// fun createHomeServerConfig(): HomeServerConnectionConfig { +// return HomeServerConnectionConfig.Builder() +// .withHomeServerUri(Uri.parse(TestConstants.TESTS_HOME_SERVER_URL)) +// .build() +// } +// +// /** +// * This methods init the event stream and check for initial sync +// * +// * @param session the session to sync +// * @param withCrypto true if crypto is enabled and should be checked +// */ +// @Throws(InterruptedException::class) +// fun syncSession(session: Session, withCrypto: Boolean) { +// val params = HashMap() +// val sizeOfLock = if (withCrypto) 2 else 1 +// val lock2 = CountDownLatch(sizeOfLock) +// session.getDataHandler().addListener(object : MXEventListener() { +// fun onInitialSyncComplete(toToken: String) { +// params["isInit"] = true +// lock2.countDown() +// } +// +// fun onCryptoSyncComplete() { +// params["onCryptoSyncComplete"] = true +// lock2.countDown() +// } +// }) +// session.getDataHandler().getStore().open() +// session.startEventStream(null) +// +// await(lock2) +// Assert.assertTrue(params.containsKey("isInit")) +// if (withCrypto) { +// Assert.assertTrue(params.containsKey("onCryptoSyncComplete")) +// } +// } +// +// /** +// * Sends text messages in a room +// * +// * @param room the room where to send the messages +// * @param message the message to send +// * @param nbOfMessages the number of time the message will be sent +// * @throws Exception +// */ +// @Throws(Exception::class) +// fun sendTextMessage(room: Room, message: String, nbOfMessages: Int): List { +// val sentEvents = ArrayList(nbOfMessages) +// val latch = CountDownLatch(nbOfMessages) +// val onEventSentListener = object : MXEventListener() { +// fun onEventSent(event: Event, prevEventId: String) { +// latch.countDown() +// } +// } +// room.addEventListener(onEventSentListener) +// for (i in 0 until nbOfMessages) { +// room.sendTextMessage(message + " #" + (i + 1), null, Message.FORMAT_MATRIX_HTML, object : RoomMediaMessage.EventCreationListener() { +// fun onEventCreated(roomMediaMessage: RoomMediaMessage) { +// val sentEvent = roomMediaMessage.getEvent() +// sentEvents.add(sentEvent) +// } +// +// fun onEventCreationFailed(roomMediaMessage: RoomMediaMessage, errorMessage: String) { +// +// } +// +// fun onEncryptionFailed(roomMediaMessage: RoomMediaMessage) { +// +// } +// }) +// } +// await(latch) +// room.removeEventListener(onEventSentListener) +// +// // Check that all events has been created +// Assert.assertEquals(nbOfMessages.toLong(), sentEvents.size.toLong()) +// +// return sentEvents +// } +// +// +// // PRIVATE METHODS ***************************************************************************** +// +// /** +// * Creates a unique account +// * +// * @param userNamePrefix the user name prefix +// * @param password the password +// * @param testParams test params about the session +// * @return the session associated with the newly created account +// */ +// @Throws(InterruptedException::class) +// private fun createAccount(userNamePrefix: String, +// password: String, +// testParams: SessionTestParams): Session { +// val context = InstrumentationRegistry.getContext() +// val session = createAccountAndSync( +// context, +// userNamePrefix + "_" + System.currentTimeMillis() + UUID.randomUUID(), +// password, +// testParams +// ) +// Assert.assertNotNull(session) +// return session +// } +// +// /** +// * Logs into an existing account +// * +// * @param userId the userId to log in +// * @param password the password to log in +// * @param testParams test params about the session +// * @return the session associated with the existing account +// */ +// @Throws(InterruptedException::class) +// private fun logIntoAccount(userId: String, +// password: String, +// testParams: SessionTestParams): Session { +// val context = InstrumentationRegistry.getContext() +// val session = logAccountAndSync(context, userId, password, testParams) +// Assert.assertNotNull(session) +// return session +// } +// +// /** +// * Create an account and a dedicated session +// * +// * @param context the context +// * @param userName the account username +// * @param password the password +// * @param sessionTestParams parameters for the test +// */ +// @Throws(InterruptedException::class) +// private fun createAccountAndSync(context: Context, +// userName: String, +// password: String, +// sessionTestParams: SessionTestParams): Session { +// val hs = createHomeServerConfig() +// +// val loginRestClient = LoginRestClient(hs) +// +// val params = HashMap() +// val registrationParams = RegistrationParams() +// +// var lock = CountDownLatch(1) +// +// // get the registration session id +// loginRestClient.register(registrationParams, object : TestMatrixCallback(lock, false) { +// override fun onFailure(failure: Throwable) { +// // detect if a parameter is expected +// var registrationFlowResponse: RegistrationFlowResponse? = null +// +// // when a response is not completed the server returns an error message +// if (null != failure.mStatus && failure.mStatus === 401) { +// try { +// registrationFlowResponse = JsonUtils.toRegistrationFlowResponse(e.mErrorBodyAsString) +// } catch (castExcept: Exception) { +// } +// +// } +// +// // check if the server response can be casted +// if (null != registrationFlowResponse) { +// params["session"] = registrationFlowResponse!!.session +// } +// +// super.onFailure(failure) +// } +// }) +// +// await(lock) +// +// val session = params["session"] as String? +// +// Assert.assertNotNull(session) +// +// registrationParams.username = userName +// registrationParams.password = password +// val authParams = AuthParams(LOGIN_FLOW_TYPE_DUMMY) +// authParams.session = session +// +// registrationParams.auth = authParams +// +// lock = CountDownLatch(1) +// loginRestClient.register(registrationParams, object : TestMatrixCallback(lock) { +// fun onSuccess(credentials: Credentials) { +// params["credentials"] = credentials +// super.onSuccess(credentials) +// } +// }) +// +// await(lock) +// +// val credentials = params["credentials"] as Credentials? +// +// Assert.assertNotNull(credentials) +// +// hs.setCredentials(credentials) +// +// val store = MXFileStore(hs, false, context) +// +// val dataHandler = MXDataHandler(store, credentials) +// dataHandler.setLazyLoadingEnabled(sessionTestParams.withLazyLoading) +// +// val Session = Session.Builder(hs, dataHandler, context) +// .withLegacyCryptoStore(sessionTestParams.withLegacyCryptoStore) +// .build() +// +// if (sessionTestParams.withCryptoEnabled) { +// Session.enableCryptoWhenStarting() +// } +// if (sessionTestParams.withInitialSync) { +// syncSession(Session, sessionTestParams.withCryptoEnabled) +// } +// return Session +// } +// +// /** +// * Start an account login +// * +// * @param context the context +// * @param userName the account username +// * @param password the password +// * @param sessionTestParams session test params +// */ +// @Throws(InterruptedException::class) +// private fun logAccountAndSync(context: Context, +// userName: String, +// password: String, +// sessionTestParams: SessionTestParams): Session { +// val hs = createHomeServerConfig(null) +// val loginRestClient = LoginRestClient(hs) +// val params = HashMap() +// val lock = CountDownLatch(1) +// +// // get the registration session id +// loginRestClient.loginWithUser(userName, password, object : TestMatrixCallback(lock) { +// fun onSuccess(credentials: Credentials) { +// params["credentials"] = credentials +// super.onSuccess(credentials) +// } +// }) +// +// await(lock) +// +// val credentials = params["credentials"] as Credentials? +// +// Assert.assertNotNull(credentials) +// +// hs.setCredentials(credentials) +// +// val store = MXFileStore(hs, false, context) +// +// val mxDataHandler = MXDataHandler(store, credentials) +// mxDataHandler.setLazyLoadingEnabled(sessionTestParams.withLazyLoading) +// +// val Session = Session.Builder(hs, mxDataHandler, context) +// .withLegacyCryptoStore(sessionTestParams.withLegacyCryptoStore) +// .build() +// +// if (sessionTestParams.withCryptoEnabled) { +// Session.enableCryptoWhenStarting() +// } +// if (sessionTestParams.withInitialSync) { +// syncSession(Session, sessionTestParams.withCryptoEnabled) +// } +// return Session +// } +// +// /** +// * Await for a latch and ensure the result is true +// * +// * @param latch +// * @throws InterruptedException +// */ +// @Throws(InterruptedException::class) +// fun await(latch: CountDownLatch) { +// Assert.assertTrue(latch.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)) +// } +// +// /** +// * Clear all provided sessions +// * +// * @param sessions the sessions to clear +// */ +// fun closeAllSessions(sessions: List) { +// for (session in sessions) { +// session.close() +// } +// } +// +// /** +// * Clone a session. +// * It simulate that the user launches again the application with the same Credentials, contrary to login which will create a new DeviceId +// * +// * @param from the session to clone +// * @return the duplicated session +// */ +// @Throws(InterruptedException::class) +// fun createNewSession(from: Session, sessionTestParams: SessionTestParams): Session { +// val context = InstrumentationRegistry.getContext() +// +// val credentials = from.sessionParams.credentials +// val hs = createHomeServerConfig(credentials) +// val store = MXFileStore(hs, false, context) +// val dataHandler = MXDataHandler(store, credentials) +// dataHandler.setLazyLoadingEnabled(sessionTestParams.withLazyLoading) +// store.setDataHandler(dataHandler) +// val session2 = Session.Builder(hs, dataHandler, context) +// .withLegacyCryptoStore(sessionTestParams.withLegacyCryptoStore) +// .build() +// +// val results = HashMap() +// +// val lock = CountDownLatch(1) +// val listener = object : MXStoreListener() { +// fun postProcess(accountId: String) { +// results["postProcess"] = "postProcess $accountId" +// } +// +// fun onStoreReady(accountId: String) { +// results["onStoreReady"] = "onStoreReady" +// lock.countDown() +// } +// +// fun onStoreCorrupted(accountId: String, description: String) { +// results["onStoreCorrupted"] = description +// lock.countDown() +// } +// +// fun onStoreOOM(accountId: String, description: String) { +// results["onStoreOOM"] = "onStoreOOM" +// lock.countDown() +// } +// } +// +// store.addMXStoreListener(listener) +// store.open() +// +// await(lock) +// +// Assert.assertTrue(results.toString(), results.containsKey("onStoreReady")) +// +// return session2 +// } +// } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt new file mode 100644 index 0000000000..8ad9f1ec6f --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestData.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +import im.vector.matrix.android.api.session.Session + +data class CryptoTestData(val firstSession: Session, + val roomId: String, + val secondSession: Session? = null, + val thirdSession: Session? = null) { + + fun close() { + firstSession.close() + secondSession?.close() + secondSession?.close() + } +} diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt new file mode 100644 index 0000000000..804e97857e --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/CryptoTestHelper.kt @@ -0,0 +1,358 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +// import android.os.SystemClock +// import android.text.TextUtils +// import im.vector.matrix.android.api.session.Session +// import im.vector.matrix.android.api.session.events.model.Event +// import im.vector.matrix.android.api.session.events.model.EventType +// import im.vector.matrix.android.api.session.events.model.toContent +// import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams +// import im.vector.matrix.android.api.session.room.model.message.MessageTextContent +// import im.vector.matrix.android.api.session.room.model.message.MessageType +// import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +// import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP +// import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupAuthData +// import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +// import org.junit.Assert.* +// import java.util.* +// import java.util.concurrent.CountDownLatch +// +// class CryptoTestHelper(val mTestHelper: CommonTestHelper) { +// +// val messagesFromAlice: List = Arrays.asList("0 - Hello I'm Alice!", "4 - Go!") +// val messagesFromBob: List = Arrays.asList("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.") +// +// // Set this value to false to test the new Realm store and to true to test legacy Filestore +// val USE_LEGACY_CRYPTO_STORE = false +// +// // Lazy loading is on by default now +// private val LAZY_LOADING_ENABLED = true +// +// val defaultSessionParams = SessionTestParams(true, false, LAZY_LOADING_ENABLED, USE_LEGACY_CRYPTO_STORE) +// val encryptedSessionParams = SessionTestParams(true, true, LAZY_LOADING_ENABLED, USE_LEGACY_CRYPTO_STORE) +// +// fun buildTextEvent(text: String, session: Session, roomId: String): Event { +// val message = MessageTextContent( +// MessageType.MSGTYPE_TEXT, +// text +// ) +// +// return Event( +// type = EventType.MESSAGE, +// content = message.toContent(), +// senderId = session.myUserId, +// roomId = roomId) +// } +// +// /** +// * @return alice session +// */ +// fun doE2ETestWithAliceInARoom(): CryptoTestData { +// val results = HashMap() +// val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) +// +// var roomId: String? = null +// val lock1 = CountDownLatch(1) +// +// aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }, object : TestMatrixCallback(lock1) { +// override fun onSuccess(data: String) { +// roomId = data +// super.onSuccess(data) +// } +// }) +// +// mTestHelper.await(lock1) +// assertNotNull(roomId) +// +// val room = aliceSession.getRoom(roomId!!) +// +// val lock2 = CountDownLatch(1) +// room.enableEncryptionWithAlgorithm(MXCRYPTO_ALGORITHM_MEGOLM, object : TestMatrixCallback(lock2) { +// override fun onSuccess(data: Void?) { +// results["enableEncryptionWithAlgorithm"] = "enableEncryptionWithAlgorithm" +// super.onSuccess(data) +// } +// }) +// mTestHelper.await(lock2) +// assertTrue(results.containsKey("enableEncryptionWithAlgorithm")) +// +// return CryptoTestData(aliceSession, roomId!!) +// } +// +// /** +// * @param cryptedBob +// * @return alice and bob sessions +// */ +// fun doE2ETestWithAliceAndBobInARoom(cryptedBob: Boolean = true): CryptoTestData { +// val statuses = HashMap() +// +// val cryptoTestData = doE2ETestWithAliceInARoom() +// val aliceSession = cryptoTestData.firstSession +// val aliceRoomId = cryptoTestData.roomId +// +// val room = aliceSession.getRoom(aliceRoomId)!! +// +// val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) +// +// val lock1 = CountDownLatch(2) +// +// val bobEventListener = object : MXEventListener() { +// override fun onNewRoom(roomId: String) { +// if (TextUtils.equals(roomId, aliceRoomId)) { +// if (!statuses.containsKey("onNewRoom")) { +// statuses["onNewRoom"] = "onNewRoom" +// lock1.countDown() +// } +// } +// } +// } +// +// bobSession.dataHandler.addListener(bobEventListener) +// +// room.invite(bobSession.myUserId, callback = object : TestMatrixCallback(lock1) { +// override fun onSuccess(data: Unit) { +// statuses["invite"] = "invite" +// super.onSuccess(data) +// } +// }) +// +// mTestHelper.await(lock1) +// +// assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom")) +// +// bobSession.dataHandler.removeListener(bobEventListener) +// +// val lock2 = CountDownLatch(2) +// +// bobSession.joinRoom(aliceRoomId, callback = TestMatrixCallback(lock2)) +// +// room.addEventListener(object : MXEventListener() { +// override fun onLiveEvent(event: Event, roomState: RoomState) { +// if (TextUtils.equals(event.getType(), Event.EVENT_TYPE_STATE_ROOM_MEMBER)) { +// val contentToConsider = event.contentAsJsonObject +// val member = JsonUtils.toRoomMember(contentToConsider) +// +// if (TextUtils.equals(member.membership, RoomMember.MEMBERSHIP_JOIN)) { +// statuses["AliceJoin"] = "AliceJoin" +// lock2.countDown() +// } +// } +// } +// }) +// +// mTestHelper.await(lock2) +// +// // Ensure bob can send messages to the room +// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! +// assertNotNull(roomFromBobPOV.state.powerLevels) +// assertTrue(roomFromBobPOV.state.powerLevels.maySendMessage(bobSession.myUserId)) +// +// assertTrue(statuses.toString() + "", statuses.containsKey("AliceJoin")) +// +// bobSession.dataHandler.removeListener(bobEventListener) +// +// return CryptoTestData(aliceSession, aliceRoomId, bobSession) +// } +// +// /** +// * @return Alice, Bob and Sam session +// */ +// fun doE2ETestWithAliceAndBobAndSamInARoom(): CryptoTestData { +// val statuses = HashMap() +// +// val cryptoTestData = doE2ETestWithAliceAndBobInARoom(true) +// val aliceSession = cryptoTestData.firstSession +// val aliceRoomId = cryptoTestData.roomId +// +// val room = aliceSession.getRoom(aliceRoomId)!! +// +// val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) +// +// val lock1 = CountDownLatch(2) +// +// val samEventListener = object : MXEventListener() { +// override fun onNewRoom(roomId: String) { +// if (TextUtils.equals(roomId, aliceRoomId)) { +// if (!statuses.containsKey("onNewRoom")) { +// statuses["onNewRoom"] = "onNewRoom" +// lock1.countDown() +// } +// } +// } +// } +// +// samSession.dataHandler.addListener(samEventListener) +// +// room.invite(aliceSession, samSession.myUserId, object : TestMatrixCallback(lock1) { +// override fun onSuccess(info: Void?) { +// statuses["invite"] = "invite" +// super.onSuccess(info) +// } +// }) +// +// mTestHelper.await(lock1) +// +// assertTrue(statuses.containsKey("invite") && statuses.containsKey("onNewRoom")) +// +// samSession.dataHandler.removeListener(samEventListener) +// +// val lock2 = CountDownLatch(1) +// +// samSession.joinRoom(aliceRoomId, object : TestMatrixCallback(lock2) { +// override fun onSuccess(info: String) { +// statuses["joinRoom"] = "joinRoom" +// super.onSuccess(info) +// } +// }) +// +// mTestHelper.await(lock2) +// assertTrue(statuses.containsKey("joinRoom")) +// +// // wait the initial sync +// SystemClock.sleep(1000) +// +// samSession.dataHandler.removeListener(samEventListener) +// +// return CryptoTestData(aliceSession, aliceRoomId, cryptoTestData.secondSession, samSession) +// } +// +// /** +// * @param cryptedBob +// * @return Alice and Bob sessions +// */ +// fun doE2ETestWithAliceAndBobInARoomWithEncryptedMessages(cryptedBob: Boolean): CryptoTestData { +// val cryptoTestData = doE2ETestWithAliceAndBobInARoom(cryptedBob) +// val aliceSession = cryptoTestData.firstSession +// val aliceRoomId = cryptoTestData.roomId +// val bobSession = cryptoTestData.secondSession!! +// +// bobSession.setWarnOnUnknownDevices(false) +// +// aliceSession.setWarnOnUnknownDevices(false) +// +// val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! +// val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! +// +// var messagesReceivedByBobCount = 0 +// var lock = CountDownLatch(3) +// +// val bobEventsListener = object : MXEventListener() { +// override fun onLiveEvent(event: Event, roomState: RoomState) { +// if (TextUtils.equals(event.getType(), Event.EVENT_TYPE_MESSAGE) && !TextUtils.equals(event.getSender(), bobSession.myUserId)) { +// messagesReceivedByBobCount++ +// lock.countDown() +// } +// } +// } +// +// roomFromBobPOV.addEventListener(bobEventsListener) +// +// val results = HashMap() +// +// bobSession.dataHandler.addListener(object : MXEventListener() { +// override fun onToDeviceEvent(event: Event) { +// results["onToDeviceEvent"] = event +// lock.countDown() +// } +// }) +// +// // Alice sends a message +// roomFromAlicePOV.sendEvent(buildTextEvent(messagesFromAlice[0], aliceSession, aliceRoomId), TestMatrixCallback(lock, true)) +// mTestHelper.await(lock) +// assertTrue(results.containsKey("onToDeviceEvent")) +// assertEquals(1, messagesReceivedByBobCount) +// +// // Bob send a message +// lock = CountDownLatch(1) +// roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob[0], bobSession, aliceRoomId), TestMatrixCallback(lock, true)) +// // android does not echo the messages sent from itself +// messagesReceivedByBobCount++ +// mTestHelper.await(lock) +// assertEquals(2, messagesReceivedByBobCount) +// +// // Bob send a message +// lock = CountDownLatch(1) +// roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob[1], bobSession, aliceRoomId), TestMatrixCallback(lock, true)) +// // android does not echo the messages sent from itself +// messagesReceivedByBobCount++ +// mTestHelper.await(lock) +// assertEquals(3, messagesReceivedByBobCount) +// +// // Bob send a message +// lock = CountDownLatch(1) +// roomFromBobPOV.sendEvent(buildTextEvent(messagesFromBob[2], bobSession, aliceRoomId), TestMatrixCallback(lock, true)) +// // android does not echo the messages sent from itself +// messagesReceivedByBobCount++ +// mTestHelper.await(lock) +// assertEquals(4, messagesReceivedByBobCount) +// +// // Alice sends a message +// lock = CountDownLatch(2) +// roomFromAlicePOV.sendEvent(buildTextEvent(messagesFromAlice[1], aliceSession, aliceRoomId), TestMatrixCallback(lock, true)) +// mTestHelper.await(lock) +// assertEquals(5, messagesReceivedByBobCount) +// +// return cryptoTestData +// } +// +// fun checkEncryptedEvent(event: CryptoEvent, roomId: String, clearMessage: String, senderSession: Session) { +// assertEquals(EventType.ENCRYPTED, event.wireType) +// assertNotNull(event.wireContent) +// +// val eventWireContent = event.wireContent.asJsonObject +// assertNotNull(eventWireContent) +// +// assertNull(eventWireContent.get("body")) +// assertEquals(MXCRYPTO_ALGORITHM_MEGOLM, eventWireContent.get("algorithm").asString) +// +// assertNotNull(eventWireContent.get("ciphertext")) +// assertNotNull(eventWireContent.get("session_id")) +// assertNotNull(eventWireContent.get("sender_key")) +// +// assertEquals(senderSession.sessionParams.credentials.deviceId, eventWireContent.get("device_id").asString) +// +// assertNotNull(event.getEventId()) +// assertEquals(roomId, event.getRoomId()) +// assertEquals(EventType.MESSAGE, event.getType()) +// assertTrue(event.getAge() < 10000) +// +// val eventContent = event.contentAsJsonObject +// assertNotNull(eventContent) +// assertEquals(clearMessage, eventContent.get("body").asString) +// assertEquals(senderSession.myUserId, event.getSender()) +// } +// +// fun createFakeMegolmBackupAuthData(): MegolmBackupAuthData { +// return MegolmBackupAuthData( +// publicKey = "abcdefg", +// signatures = HashMap>().apply { +// this["something"] = HashMap().apply { +// this["ed25519:something"] = "hijklmnop" +// } +// } +// ) +// } +// +// fun createFakeMegolmBackupCreationInfo(): MegolmBackupCreationInfo { +// return MegolmBackupCreationInfo().apply { +// algorithm = MXCRYPTO_ALGORITHM_MEGOLM_BACKUP +// authData = createFakeMegolmBackupAuthData() +// } +// } +// } diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/MockOkHttpInterceptor.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/MockOkHttpInterceptor.kt new file mode 100644 index 0000000000..da051c7e4c --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/MockOkHttpInterceptor.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.matrix.android.common + +import okhttp3.* +import javax.net.ssl.HttpsURLConnection + +/** + * Allows to intercept network requests for test purpose by + * - re-writing the response + * - changing the response code (200/404/etc..). + * - Test delays.. + * + * Basic usage: + * + * val mockInterceptor = MockOkHttpInterceptor() + * mockInterceptor.addRule(MockOkHttpInterceptor.SimpleRule(".well-known/matrix/client", 200, "{}")) + * + * RestHttpClientFactoryProvider.defaultProvider = RestClientHttpClientFactory(mockInterceptor) + * AutoDiscovery().findClientConfig("matrix.org", ) + * + */ +class MockOkHttpInterceptor : Interceptor { + + private var rules: ArrayList = ArrayList() + + + fun addRule(rule: Rule) { + rules.add(rule) + } + + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + + rules.forEach { rule -> + if (originalRequest.url.toString().contains(rule.match)) { + rule.process(originalRequest)?.let { + return it + } + } + } + + return chain.proceed(originalRequest) + } + + + abstract class Rule(val match: String) { + abstract fun process(originalRequest: Request): Response? + } + + /** + * Simple rule that reply with the given body for any request that matches the match param + */ + class SimpleRule(match: String, + private val code: Int = HttpsURLConnection.HTTP_OK, + private val body: String = "{}") : Rule(match) { + + override fun process(originalRequest: Request): Response? { + return Response.Builder() + .protocol(Protocol.HTTP_1_1) + .request(originalRequest) + .message("mocked answer") + .body(ResponseBody.create(null, body)) + .code(code) + .build() + } + + } +} diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/SessionTestParams.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/SessionTestParams.kt new file mode 100644 index 0000000000..db3b2c9e3f --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/SessionTestParams.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +data class SessionTestParams @JvmOverloads constructor(val withInitialSync: Boolean = false, + val withCryptoEnabled: Boolean = false, + val withLazyLoading: Boolean = true, + val withLegacyCryptoStore: Boolean = false) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestAssertUtil.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestAssertUtil.kt new file mode 100644 index 0000000000..2a62165210 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestAssertUtil.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +import org.junit.Assert.* + +/** + * Compare two lists and their content + */ +fun assertListEquals(list1: List?, list2: List?) { + if (list1 == null) { + assertNull(list2) + } else { + assertNotNull(list2) + + assertEquals("List sizes must match", list1.size, list2!!.size) + + for (i in list1.indices) { + assertEquals("Elements at index $i are not equal", list1[i], list2[i]) + } + } +} + +/** + * Compare two maps and their content + */ +fun assertDictEquals(dict1: Map?, dict2: Map?) { + if (dict1 == null) { + assertNull(dict2) + } else { + assertNotNull(dict2) + + assertEquals("Map sizes must match", dict1.size, dict2!!.size) + + for (i in dict1.keys) { + assertEquals("Values for key $i are not equal", dict1[i], dict2[i]) + } + } +} + +/** + * Compare two byte arrays content. + * Note that if the arrays have not the same size, it also fails. + */ +fun assertByteArrayNotEqual(a1: ByteArray, a2: ByteArray) { + if (a1.size != a2.size) { + fail("Arrays have not the same size.") + } + + for (index in a1.indices) { + if (a1[index] != a2[index]) { + // Difference found! + return + } + } + + fail("Arrays are equals.") +} diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt new file mode 100644 index 0000000000..60cc87d330 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestConstants.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +import android.os.Debug + +object TestConstants { + + const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080" + + // Time out to use when waiting for server response. 60s + private const val AWAIT_TIME_OUT_MILLIS = 60000 + + // Time out to use when waiting for server response, when the debugger is connected. 10 minutes + private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60000 + + const val USER_ALICE = "Alice" + const val USER_BOB = "Bob" + const val USER_SAM = "Sam" + + const val PASSWORD = "password" + + val timeOutMillis: Long + get() = if (Debug.isDebuggerConnected()) { + // Wait more + AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS.toLong() + } else { + AWAIT_TIME_OUT_MILLIS.toLong() + } +} diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestMatrixCallback.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestMatrixCallback.kt new file mode 100644 index 0000000000..2509f77bef --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/common/TestMatrixCallback.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.common + +import androidx.annotation.CallSuper +import im.vector.matrix.android.api.MatrixCallback +import org.junit.Assert.fail +import timber.log.Timber +import java.util.concurrent.CountDownLatch + +/** + * Simple implementation of MatrixCallback, which count down the CountDownLatch on each API callback + * @param onlySuccessful true to fail if an error occurs. This is the default behavior + * @param + */ +open class TestMatrixCallback @JvmOverloads constructor(private val countDownLatch: CountDownLatch, + private val onlySuccessful: Boolean = true) : MatrixCallback { + + @CallSuper + override fun onSuccess(data: T) { + countDownLatch.countDown() + } + + @CallSuper + override fun onFailure(failure: Throwable) { + Timber.e(failure, "TestApiCallback") + + if (onlySuccessful) { + fail("onFailure " + failure.localizedMessage) + } + + countDownLatch.countDown() + } +}