diff --git a/vector-app/src/androidTest/java/im/vector/app/CantVerifyTest.kt b/vector-app/src/androidTest/java/im/vector/app/CantVerifyTest.kt index 6f9d6cdde97..93204e1fc0e 100644 --- a/vector-app/src/androidTest/java/im/vector/app/CantVerifyTest.kt +++ b/vector-app/src/androidTest/java/im/vector/app/CantVerifyTest.kt @@ -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 @@ -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()}" @@ -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() + } + } + } } diff --git a/vector-app/src/androidTest/java/im/vector/app/ClearCurrentSessionRule.kt b/vector-app/src/androidTest/java/im/vector/app/ClearCurrentSessionRule.kt index f09e5229322..5b46016989b 100644 --- a/vector-app/src/androidTest/java/im/vector/app/ClearCurrentSessionRule.kt +++ b/vector-app/src/androidTest/java/im/vector/app/ClearCurrentSessionRule.kt @@ -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 { @@ -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 { +fun reflectAnalyticDatastore(context: Context): DataStore { val klass = AnalyticsStore::class.asTopLevel() val method = klass.getMethod("access\$getDataStore", Context::class.java) return method.invoke(klass, context) as DataStore diff --git a/vector-app/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt b/vector-app/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt index 1243758b2f7..8db126e9946 100644 --- a/vector-app/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt +++ b/vector-app/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt @@ -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 @@ -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)) diff --git a/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt b/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt index 97a2a14da33..0b8c1d4809c 100644 --- a/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt +++ b/vector-app/src/androidTest/java/im/vector/app/VerificationTestBase.kt @@ -17,7 +17,11 @@ 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 @@ -25,6 +29,7 @@ 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 @@ -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) @@ -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() + } + } + } } diff --git a/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt index 901ef8e4c17..940d1c91ee8 100644 --- a/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt +++ b/vector-app/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt @@ -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 @@ -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 { + doSync { existingSession!!.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -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)) @@ -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)))) @@ -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) @@ -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 diff --git a/vector-app/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt b/vector-app/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt index 53e088118b4..afb43df11c0 100644 --- a/vector-app/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt +++ b/vector-app/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt @@ -37,6 +37,7 @@ 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 @@ -44,9 +45,9 @@ 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 @@ -59,14 +60,15 @@ 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() { @@ -74,7 +76,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() { val matrix = getMatrixInstance() val userName = "foobar_${Random.nextLong()}" existingSession = createAccountAndSync(matrix, userName, password, true) - doSync { + doSync { existingSession!!.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -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))