Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't ignore verification UI tests #7295

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
22 changes: 21 additions & 1 deletion vector-app/src/androidTest/java/im/vector/app/CantVerifyTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@
package im.vector.app

import android.view.View
import androidx.datastore.preferences.core.edit
import androidx.test.espresso.Espresso
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
import com.adevinta.android.barista.internal.viewaction.SleepViewAction
import dagger.hilt.EntryPoints
import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.features.MainActivity
import im.vector.app.ui.robot.ElementRobot
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
Expand All @@ -39,7 +45,6 @@ class CantVerifyTest {
@get:Rule
val testRule = RuleChain
.outerRule(ActivityScenarioRule(MainActivity::class.java))
.around(ClearCurrentSessionRule())

private val elementRobot = ElementRobot()
var userName: String = "loginTest_${UUID.randomUUID()}"
Expand Down Expand Up @@ -76,4 +81,19 @@ class CantVerifyTest {
Espresso.onView(ViewMatchers.withText(R.string.bottom_sheet_setup_secure_backup_title))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
}

@After
fun tearDown() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
runBlocking {
reflectAnalyticDatastore(context).edit { it.clear() }
runCatching {
val entryPoint = EntryPoints.get(context.applicationContext, SingletonEntryPoint::class.java)
val sessionHolder = entryPoint.activeSessionHolder()
sessionHolder.getSafeActiveSession()?.signOutService()?.signOut(true)
entryPoint.vectorPreferences().clearPreferences()
sessionHolder.clearActiveSession()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import kotlin.reflect.KClass
* The VectorPreferences and AnalyticsDatastore are also cleared in an attempt to recreate a fresh base.
*/
class ClearCurrentSessionRule : TestWatcher() {

override fun apply(base: Statement, description: Description): Statement {
val context = InstrumentationRegistry.getInstrumentation().targetContext
runBlocking {
Expand All @@ -59,7 +60,7 @@ private fun KClass<*>.asTopLevel() = Class.forName("${qualifiedName}Kt")
* via reflection to avoid exposing property to all callers.
*/
@Suppress("UNCHECKED_CAST")
private fun reflectAnalyticDatastore(context: Context): DataStore<Preferences> {
fun reflectAnalyticDatastore(context: Context): DataStore<Preferences> {
val klass = AnalyticsStore::class.asTopLevel()
val method = klass.getMethod("access\$getDataStore", Context::class.java)
return method.invoke(klass, context) as DataStore<Preferences>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import im.vector.app.core.utils.getMatrixInstance
import im.vector.app.features.MainActivity
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.home.HomeActivity
import org.hamcrest.CoreMatchers.not
Expand Down Expand Up @@ -82,6 +83,12 @@ class SecurityBootstrapTest : VerificationTestBase() {

uiTestBase.login(userId = userId, password = password, homeServerUrl = homeServerUrl)

withIdlingResource(activityIdlingResource(AnalyticsOptInActivity::class.java)) {
onView(withId(R.id.submit))
.check(matches(isDisplayed()))
.perform(click())
}

// Thread.sleep(6000)
withIdlingResource(activityIdlingResource(HomeActivity::class.java)) {
onView(withId(R.id.roomListContainer))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
package im.vector.app

import android.net.Uri
import androidx.datastore.preferences.core.edit
import androidx.lifecycle.Observer
import androidx.test.platform.app.InstrumentationRegistry
import dagger.hilt.EntryPoints
import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.ui.robot.OnboardingRobot
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.junit.After
import org.junit.Assert
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixCallback
Expand Down Expand Up @@ -114,7 +119,9 @@ abstract class VerificationTestBase {
private fun syncSession(session: Session) {
val lock = CountDownLatch(1)

GlobalScope.launch(Dispatchers.Main) { session.open() }
runBlocking(Dispatchers.Main) {
session.open()
}

session.syncService().startSync(true)

Expand All @@ -133,4 +140,22 @@ abstract class VerificationTestBase {

lock.await(20_000, TimeUnit.MILLISECONDS)
}

/**
* Clears the session after every test. It is necessary to reset otherwise further UI tests fails.
*/
@After
fun tearDown() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
runBlocking {
reflectAnalyticDatastore(context).edit { it.clear() }
runCatching {
val entryPoint = EntryPoints.get(context.applicationContext, SingletonEntryPoint::class.java)
val sessionHolder = entryPoint.activeSessionHolder()
sessionHolder.getSafeActiveSession()?.signOutService()?.signOut(true)
entryPoint.vectorPreferences().clearPreferences()
sessionHolder.clearActiveSession()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ import androidx.test.filters.LargeTest
import com.adevinta.android.barista.internal.viewaction.SleepViewAction
import im.vector.app.core.utils.getMatrixInstance
import im.vector.app.features.MainActivity
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.home.HomeActivity
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
Expand All @@ -59,20 +60,21 @@ import kotlin.random.Random

@RunWith(AndroidJUnit4::class)
@LargeTest
@Ignore
class VerifySessionInteractiveTest : VerificationTestBase() {

var existingSession: Session? = null

@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
val testRule: RuleChain = RuleChain
.outerRule(ActivityScenarioRule(MainActivity::class.java))
.around(ClearCurrentSessionRule())

@Before
fun createSessionWithCrossSigning() {
val matrix = getMatrixInstance()
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {
doSync {
existingSession!!.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
Expand All @@ -96,6 +98,12 @@ class VerifySessionInteractiveTest : VerificationTestBase() {

uiTestBase.login(userId = userId, password = password, homeServerUrl = homeServerUrl)

withIdlingResource(activityIdlingResource(AnalyticsOptInActivity::class.java)) {
onView(withId(R.id.submit))
.check(matches(isDisplayed()))
.perform(click())
}

// Thread.sleep(6000)
withIdlingResource(activityIdlingResource(HomeActivity::class.java)) {
onView(withId(R.id.roomListContainer))
Expand Down Expand Up @@ -139,21 +147,29 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
onView(withId(R.id.bottomSheetFragmentContainer))
.check(matches(not(hasDescendant(withText(R.string.verification_cannot_access_other_session)))))

val request = existingSession!!.cryptoService().verificationService().requestKeyVerification(
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
existingSession!!.myUserId,
listOf(uiSession.sessionParams.deviceId!!)
// The emulator at this point has sent requests to other sessions.
// Find the incoming request from the existing session and start the verification process.
val incomingRequest = existingSession!!.cryptoService().verificationService().getExistingVerificationRequests(existingSession!!.myUserId).first {
it.requestInfo?.fromDevice == uiSession.sessionParams.deviceId
}

existingSession!!.cryptoService().verificationService().readyPendingVerification(
listOf(
VerificationMethod.SAS,
VerificationMethod.QR_CODE_SCAN,
VerificationMethod.QR_CODE_SHOW
), existingSession!!.myUserId, incomingRequest.transactionId!!
)

val transactionId = request.transactionId!!
val transactionId = incomingRequest.transactionId!!
val sasReadyIdle = verificationStateIdleResource(transactionId, VerificationTxState.ShortCodeReady, uiSession)
val otherSessionSasReadyIdle = verificationStateIdleResource(transactionId, VerificationTxState.ShortCodeReady, existingSession!!)

onView(isRoot()).perform(SleepViewAction.sleep(1000))
onView(isRoot()).perform(SleepViewAction.sleep(5000))

// Assert QR code option is there and available
onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withText(R.string.verification_scan_their_code))))
.check(matches(hasDescendant(withText(R.string.verification_scan_with_this_device))))

onView(withId(R.id.bottomSheetVerificationRecyclerView))
.check(matches(hasDescendant(withId(R.id.itemVerificationQrCodeImage))))
Expand All @@ -173,7 +189,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {

IdlingRegistry.getInstance().register(sasReadyIdle)
IdlingRegistry.getInstance().register(otherSessionSasReadyIdle)
onView(isRoot()).perform(SleepViewAction.sleep(300))
onView(isRoot()).perform(SleepViewAction.sleep(5000))
// 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)
Expand Down Expand Up @@ -241,7 +257,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
.perform(click())
}

fun verificationStateIdleResource(transactionId: String, checkForState: VerificationTxState, session: Session): IdlingResource {
private fun verificationStateIdleResource(transactionId: String, checkForState: VerificationTxState, session: Session): IdlingResource {
val idle = object : IdlingResource, VerificationService.Listener {
private var callback: IdlingResource.ResourceCallback? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,17 @@ import com.adevinta.android.barista.internal.viewaction.SleepViewAction
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.getMatrixInstance
import im.vector.app.features.MainActivity
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
import im.vector.app.features.crypto.recover.BootstrapCrossSigningTask
import im.vector.app.features.crypto.recover.Params
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.home.HomeActivity
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
Expand All @@ -59,22 +60,23 @@ import kotlin.random.Random

@RunWith(AndroidJUnit4::class)
@LargeTest
@Ignore
class VerifySessionPassphraseTest : VerificationTestBase() {

var existingSession: Session? = null
val passphrase = "person woman camera tv"

@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
val testRule: RuleChain = RuleChain
.outerRule(ActivityScenarioRule(MainActivity::class.java))
.around(ClearCurrentSessionRule())

@Before
fun createSessionWithCrossSigningAnd4S() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = getMatrixInstance()
val userName = "foobar_${Random.nextLong()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {
doSync {
existingSession!!.cryptoService().crossSigningService()
.initializeCrossSigning(
object : UserInteractiveAuthInterceptor {
Expand Down Expand Up @@ -120,6 +122,12 @@ class VerifySessionPassphraseTest : VerificationTestBase() {

uiTestBase.login(userId = userId, password = password, homeServerUrl = homeServerUrl)

withIdlingResource(activityIdlingResource(AnalyticsOptInActivity::class.java)) {
onView(withId(R.id.submit))
.check(matches(isDisplayed()))
.perform(click())
}

// Thread.sleep(6000)
withIdlingResource(activityIdlingResource(HomeActivity::class.java)) {
onView(withId(R.id.roomListContainer))
Expand Down