Added Self verification UI test

This commit is contained in:
valere 2023-01-03 19:35:15 +01:00
parent 8eda089edc
commit 419673675c
4 changed files with 120 additions and 88 deletions

View File

@ -194,7 +194,7 @@ class E2eeSanityTests : InstrumentedTest {
val megolmBackupCreationInfo = bobKeysBackupService.prepareKeysBackupVersion(keyBackupPassword, null)
val version = bobKeysBackupService.createKeysBackupVersion(megolmBackupCreationInfo)
Log.v("#E2E TEST", "... Key backup started and enabled for bob")
Log.v("#E2E TEST", "... Key backup started and enabled for bob: version:$version")
// Bob session should now have
val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!!
@ -230,6 +230,7 @@ class E2eeSanityTests : InstrumentedTest {
fail("All keys should be backedup")
}
) {
Log.v("#E2E TEST", "backedUp=${ bobKeysBackupService.getTotalNumbersOfBackedUpKeys()}, known=${bobKeysBackupService.getTotalNumbersOfKeys()}")
bobKeysBackupService.getTotalNumbersOfBackedUpKeys() == bobKeysBackupService.getTotalNumbersOfKeys()
}
Log.v("#E2E TEST", "... Key backup done for Bob")
@ -268,7 +269,7 @@ class E2eeSanityTests : InstrumentedTest {
) // MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID)
// Let's now import keys from backup
Log.v("#E2E TEST", "Restore backup for the new session")
newBobSession.cryptoService().keysBackupService().let { kbs ->
val keyVersionResult = kbs.getVersion(version.version)
@ -284,9 +285,12 @@ class E2eeSanityTests : InstrumentedTest {
}
// ensure bob can now decrypt
Log.v("#E2E TEST", "Check that bob can decrypt now")
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
// Check key trust
Log.v("#E2E TEST", "Check key safety")
sentEventIds.forEach { sentEventId ->
val timelineEvent = newBobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEventId)!!
val result = newBobSession.cryptoService().decryptEvent(timelineEvent.root, "")

View File

@ -296,6 +296,8 @@ internal abstract class SessionModule {
var uri = sessionParams.homeServerConnectionConfig.homeServerUriBase.toString()
if (uri == "http://localhost:8080") {
uri = "http://10.0.2.2:8080"
} else if (uri == "http://localhost:8081") {
uri = "http://10.0.2.2:8081"
}
return retrofitFactory
.create(okHttpClient, uri)

View File

@ -18,6 +18,7 @@ package im.vector.app
import android.net.Uri
import androidx.lifecycle.Observer
import im.vector.app.ui.robot.ElementRobot
import im.vector.app.ui.robot.OnboardingRobot
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
@ -41,6 +42,7 @@ abstract class VerificationTestBase {
val homeServerUrl: String = "http://10.0.2.2:8080"
protected val uiTestBase = OnboardingRobot()
protected val elementRobot = ElementRobot()
fun createAccountAndSync(
matrix: Matrix,
@ -114,9 +116,10 @@ abstract class VerificationTestBase {
private fun syncSession(session: Session) {
val lock = CountDownLatch(1)
GlobalScope.launch(Dispatchers.Main) { session.open() }
GlobalScope.launch(Dispatchers.Main) {
session.open()
session.syncService().startSync(true)
}
val syncLiveData = runBlocking(Dispatchers.Main) {
session.syncService().getSyncStateLive()

View File

@ -19,10 +19,8 @@ package im.vector.app
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.IdlingResource
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
@ -33,19 +31,25 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.adevinta.android.barista.internal.viewaction.SleepViewAction
import im.vector.app.core.utils.getMatrixInstance
import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.espresso.tools.waitUntilViewVisible
import im.vector.app.features.MainActivity
import im.vector.app.features.home.HomeActivity
import im.vector.app.ui.robot.AnalyticsRobot
import im.vector.app.ui.robot.ElementRobot
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.amshove.kluent.internal.assertEquals
import org.hamcrest.CoreMatchers.not
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -54,9 +58,11 @@ import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.getRequest
import org.matrix.android.sdk.api.session.crypto.verification.getTransaction
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
@ -64,7 +70,6 @@ import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
@Ignore
class VerifySessionInteractiveTest : VerificationTestBase() {
var existingSession: Session? = null
@ -72,6 +77,8 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
private val testScope = CoroutineScope(SupervisorJob())
@Before
fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance()
@ -101,28 +108,23 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
uiTestBase.login(userId = userId, password = password, homeServerUrl = homeServerUrl)
// Thread.sleep(6000)
withIdlingResource(activityIdlingResource(HomeActivity::class.java)) {
onView(withId(R.id.roomListContainer))
.check(matches(isDisplayed()))
.perform(closeSoftKeyboard())
}
val analyticsRobot = AnalyticsRobot()
analyticsRobot.optOut()
waitUntilActivityVisible<HomeActivity> {
waitUntilViewVisible(withId(R.id.roomListContainer))
}
val activity = EspressoHelper.getCurrentActivity()!!
val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession()
withIdlingResource(initialSyncIdlingResource(uiSession)) {
onView(withId(R.id.roomListContainer))
.check(matches(isDisplayed()))
waitUntilViewVisible(withId(R.id.roomListContainer))
}
// THIS IS THE ONLY WAY I FOUND TO CLICK ON ALERTERS... :(
// Cannot wait for view because of alerter animation? ...
onView(isRoot())
.perform(waitForView(withId(com.tapadoo.alerter.R.id.llAlertBackground)))
// Thread.sleep(1000)
// onView(withId(com.tapadoo.alerter.R.id.llAlertBackground))
// .perform(click())
Thread.sleep(1000)
val popup = activity.findViewById<View>(com.tapadoo.alerter.R.id.llAlertBackground)
activity.runOnUiThread {
@ -131,74 +133,105 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
onView(isRoot())
.perform(waitForView(withId(R.id.bottomSheetFragmentContainer)))
// .check()
// onView(withId(R.id.bottomSheetFragmentContainer))
// .check(matches(isDisplayed()))
// onView(isRoot()).perform(SleepViewAction.sleep(2000))
onView(withText(R.string.use_latest_app))
onView(withText(R.string.verification_verify_identity))
.check(matches(isDisplayed()))
// 4S is not setup so passphrase option should be hidden
onView(withId(R.id.bottomSheetFragmentContainer))
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(not(hasDescendant(withText(R.string.verification_cannot_access_other_session)))))
val request = runBlockingTest {
existingSession!!.cryptoService().verificationService().requestSelfKeyVerification(
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
)
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withText(R.string.verification_verify_with_another_device))))
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withText(R.string.bad_passphrase_key_reset_all_action))))
val otherRequest = CompletableDeferred<PendingVerificationRequest>()
testScope.launch {
existingSession!!.cryptoService().verificationService().requestEventFlow().collect {
if (it.getRequest() != null) {
otherRequest.complete(it.getRequest()!!)
return@collect cancel()
}
}
}
val transactionId = request.transactionId
val sasReadyIdle = verificationStateIdleResource(transactionId, SasTransactionState.SasShortCodeReady, uiSession)
val otherSessionSasReadyIdle = verificationStateIdleResource(transactionId, SasTransactionState.SasShortCodeReady, existingSession!!)
onView(isRoot()).perform(SleepViewAction.sleep(1000))
// Assert QR code option is there and available
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withText(R.string.verification_scan_their_code))))
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withId(R.id.itemVerificationQrCodeImage))))
// Send out a self verification request
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(
actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText(R.string.verification_scan_emoji_title)),
hasDescendant(withText(R.string.verification_verify_with_another_device)),
click()
)
)
val firstSessionTr = runBlockingTest {
existingSession!!.cryptoService().verificationService().getExistingTransaction(
existingSession!!.myUserId,
transactionId
) as SasVerificationTransaction
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withText(R.string.verification_request_was_sent))))
val txId = runBlockingTest {
otherRequest.await().transactionId
}
IdlingRegistry.getInstance().register(sasReadyIdle)
IdlingRegistry.getInstance().register(otherSessionSasReadyIdle)
onView(isRoot()).perform(SleepViewAction.sleep(300))
// will only execute when Idle is ready
val expectedEmojis = firstSessionTr.getEmojiCodeRepresentation()
val targets = listOf(R.id.emoji0, R.id.emoji1, R.id.emoji2, R.id.emoji3, R.id.emoji4, R.id.emoji5, R.id.emoji6)
targets.forEachIndexed { index, res ->
onView(withId(res))
.check(
matches(hasDescendant(withText(expectedEmojis[index].nameResId)))
// accept from other session
runBlockingTest {
existingSession!!.cryptoService().verificationService().readyPendingVerification(
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
existingSession!!.myUserId,
txId
)
}
IdlingRegistry.getInstance().unregister(sasReadyIdle)
IdlingRegistry.getInstance().unregister(otherSessionSasReadyIdle)
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(waitForView(hasDescendant(withText(R.string.verification_scan_self_notice))))
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(waitForView(hasDescendant(withText(R.string.verification_scan_self_emoji_subtitle))))
val verificationSuccessIdle =
verificationStateIdleResource(transactionId, SasTransactionState.Done(true), uiSession)
// there should be the QR code also
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withId(R.id.itemVerificationQrCodeImage))))
// CLICK ON THEY MATCH
// proceed with emoji
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(
actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText(R.string.verification_scan_self_emoji_subtitle)),
click()
)
)
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(waitForView(hasDescendant(withText(R.string.verification_sas_do_not_match))))
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(waitForView(hasDescendant(withText(R.string.verification_sas_match))))
// check that the code matches
val uiCode = runBlockingTest {
(uiSession.cryptoService()
.verificationService()
.getExistingTransaction(uiSession.myUserId, txId) as SasVerificationTransaction)
.getDecimalCodeRepresentation()!!
}
val backgroundCode = runBlockingTest {
(existingSession!!.cryptoService()
.verificationService()
.getExistingTransaction(uiSession.myUserId, txId) as SasVerificationTransaction)
.getDecimalCodeRepresentation()!!
}
assertEquals("SAS code should be equals", backgroundCode, uiCode)
runBlockingTest {
(existingSession!!.cryptoService()
.verificationService()
.getExistingTransaction(uiSession.myUserId, txId) as SasVerificationTransaction)
.userHasVerifiedShortCode()
}
// Do the same on ui
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(
actionOnItem<RecyclerView.ViewHolder>(
@ -207,23 +240,10 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
)
)
runBlockingTest {
firstSessionTr.userHasVerifiedShortCode()
}
onView(isRoot()).perform(SleepViewAction.sleep(1000))
withIdlingResource(verificationSuccessIdle) {
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(
matches(hasDescendant(withText(R.string.verification_conclusion_ok_self_notice)))
)
}
.perform(waitForView(hasDescendant(withText(R.string.verification_conclusion_ok_notice))))
// Wait a bit before done (to delay a bit sending of secrets to let other have time
// to mark as verified :/
Thread.sleep(5_000)
// Click on done
// click on done
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.perform(
actionOnItem<RecyclerView.ViewHolder>(
@ -232,11 +252,14 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
)
)
// Wait until local secrets are known (gossip)
withIdlingResource(allSecretsKnownIdling(uiSession)) {
onView(withId(R.id.roomListContainer))
.check(matches(isDisplayed()))
}
Thread.sleep(1_000)
// check that current session is actually trusted
assertTrue("I should be verified",
runBlockingTest { uiSession.cryptoService().crossSigningService().isCrossSigningVerified() }
)
ElementRobot().signout(false)
}
fun signout() {