Merge pull request #4429 from vector-im/feature/adm/relogin-sanity-check

Sign out - Sign in sanity check & nightly sanity check
This commit is contained in:
Adam Brown 2021-11-08 20:21:15 +00:00 committed by GitHub
commit 9dd01d5b20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 85 deletions

View File

@ -1,9 +1,9 @@
name: Sanity Test name: Sanity Test
on: on:
pull_request: { } schedule:
push: # At 20:00 every day UTC
branches: [ main, develop ] - cron: '0 20 * * *'
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
@ -14,13 +14,15 @@ env:
jobs: jobs:
integration-tests: integration-tests:
name: Sanity Tests (Synapse) name: Sanity Tests (Synapse)
runs-on: ubuntu-latest runs-on: macos-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
api-level: [28] api-level: [28]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
ref: develop
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
@ -46,8 +48,12 @@ jobs:
python3 -m venv .synapse python3 -m venv .synapse
source .synapse/bin/activate source .synapse/bin/activate
pip install synapse matrix-synapse pip install synapse matrix-synapse
curl -sL https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh --no-rate-limit \ curl -sL https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh \
| sed s/127.0.0.1/0.0.0.0/g | bash | sed s/127.0.0.1/0.0.0.0/g | sed 's/http:\/\/localhost/http:\/\/10.0.2.2/g' | bash -s -- --no-rate-limit
- uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
- name: Run sanity tests on API ${{ matrix.api-level }} - name: Run sanity tests on API ${{ matrix.api-level }}
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
with: with:

View File

@ -51,6 +51,11 @@ internal class SessionManager @Inject constructor(private val matrixComponent: M
} }
} }
fun stopSession(sessionId: String) {
val sessionComponent = sessionComponents[sessionId] ?: throw RuntimeException("You don't have a session for id $sessionId")
sessionComponent.session().stopSync()
}
fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent { fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent {
return sessionComponents.getOrPut(sessionParams.credentials.sessionId()) { return sessionComponents.getOrPut(sessionParams.credentials.sessionId()) {
DaggerSessionComponent DaggerSessionComponent

View File

@ -44,7 +44,7 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
override suspend fun execute(params: DeactivateAccountTask.Params) { override suspend fun execute(params: DeactivateAccountTask.Params) {
val deactivateAccountParams = DeactivateAccountParams.create(params.userAuthParam, params.eraseAllData) val deactivateAccountParams = DeactivateAccountParams.create(params.userAuthParam, params.eraseAllData)
cleanupSession.stopActiveTasks()
val canCleanup = try { val canCleanup = try {
executeRequest(globalErrorReceiver) { executeRequest(globalErrorReceiver) {
accountAPI.deactivate(deactivateAccountParams) accountAPI.deactivate(deactivateAccountParams)
@ -71,7 +71,7 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
runCatching { identityDisconnectTask.execute(Unit) } runCatching { identityDisconnectTask.execute(Unit) }
.onFailure { Timber.w(it, "Unable to disconnect identity server") } .onFailure { Timber.w(it, "Unable to disconnect identity server") }
cleanupSession.handle() cleanupSession.cleanup()
} }
} }
} }

View File

@ -50,20 +50,26 @@ internal class CleanupSession @Inject constructor(
@CryptoDatabase private val realmCryptoConfiguration: RealmConfiguration, @CryptoDatabase private val realmCryptoConfiguration: RealmConfiguration,
@UserMd5 private val userMd5: String @UserMd5 private val userMd5: String
) { ) {
suspend fun handle() {
fun stopActiveTasks() {
Timber.d("Cleanup: cancel pending works...")
workManagerProvider.cancelAllWorks()
Timber.d("Cleanup: stop session...")
sessionManager.stopSession(sessionId)
}
suspend fun cleanup() {
val sessionRealmCount = Realm.getGlobalInstanceCount(realmSessionConfiguration) val sessionRealmCount = Realm.getGlobalInstanceCount(realmSessionConfiguration)
val cryptoRealmCount = Realm.getGlobalInstanceCount(realmCryptoConfiguration) val cryptoRealmCount = Realm.getGlobalInstanceCount(realmCryptoConfiguration)
Timber.d("Realm instance ($sessionRealmCount - $cryptoRealmCount)") Timber.d("Realm instance ($sessionRealmCount - $cryptoRealmCount)")
Timber.d("Cleanup: delete session params...")
sessionParamsStore.delete(sessionId)
Timber.d("Cleanup: cancel pending works...")
workManagerProvider.cancelAllWorks()
Timber.d("Cleanup: release session...") Timber.d("Cleanup: release session...")
sessionManager.releaseSession(sessionId) sessionManager.releaseSession(sessionId)
Timber.d("Cleanup: delete session params...")
sessionParamsStore.delete(sessionId)
Timber.d("Cleanup: clear session data...") Timber.d("Cleanup: clear session data...")
clearSessionDataTask.execute(Unit) clearSessionDataTask.execute(Unit)

View File

@ -43,6 +43,7 @@ internal class DefaultSignOutTask @Inject constructor(
override suspend fun execute(params: SignOutTask.Params) { override suspend fun execute(params: SignOutTask.Params) {
// It should be done even after a soft logout, to be sure the deviceId is deleted on the // It should be done even after a soft logout, to be sure the deviceId is deleted on the
if (params.signOutFromHomeserver) { if (params.signOutFromHomeserver) {
cleanupSession.stopActiveTasks()
Timber.d("SignOut: send request...") Timber.d("SignOut: send request...")
try { try {
executeRequest(globalErrorReceiver) { executeRequest(globalErrorReceiver) {
@ -67,6 +68,6 @@ internal class DefaultSignOutTask @Inject constructor(
.onFailure { Timber.w(it, "Unable to disconnect identity server") } .onFailure { Timber.w(it, "Unable to disconnect identity server") }
Timber.d("SignOut: cleanup session...") Timber.d("SignOut: cleanup session...")
cleanupSession.handle() cleanupSession.cleanup()
} }
} }

View File

@ -160,6 +160,9 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
synchronized(lock) { lock.wait() } synchronized(lock) { lock.wait() }
Timber.tag(loggerTag.value).d("...retry") Timber.tag(loggerTag.value).d("...retry")
} else if (!isTokenValid) { } else if (!isTokenValid) {
if (state == SyncState.Killing) {
continue
}
Timber.tag(loggerTag.value).d("Token is invalid. Waiting...") Timber.tag(loggerTag.value).d("Token is invalid. Waiting...")
updateStateTo(SyncState.InvalidToken) updateStateTo(SyncState.InvalidToken)
synchronized(lock) { lock.wait() } synchronized(lock) { lock.wait() }

View File

@ -16,23 +16,14 @@
package im.vector.app.ui package im.vector.app.ui
import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest import androidx.test.filters.LargeTest
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.EspressoHelper
import im.vector.app.R
import im.vector.app.SleepViewAction
import im.vector.app.features.MainActivity import im.vector.app.features.MainActivity
import im.vector.app.ui.robot.ElementRobot import im.vector.app.ui.robot.ElementRobot
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.lang.Thread.sleep
import java.util.UUID import java.util.UUID
/** /**
@ -55,7 +46,7 @@ class UiAllScreensSanityTest {
fun allScreensTest() { fun allScreensTest() {
// Create an account // Create an account
val userId = "UiTest_" + UUID.randomUUID().toString() val userId = "UiTest_" + UUID.randomUUID().toString()
elementRobot.login(userId) elementRobot.signUp(userId)
elementRobot.settings { elementRobot.settings {
general { crawl() } general { crawl() }
@ -89,34 +80,12 @@ class UiAllScreensSanityTest {
verifyCreatedRoom() verifyCreatedRoom()
} }
// Disable until the "you don't have a session for id %d" sign out bug is fixed elementRobot.signout(expectSignOutWarning = true)
// elementRobot.signout()
// // Login again on the same account
// elementRobot.login(userId)
//
// ignoreVerification()
//
// elementRobot.signout()
// clickDialogPositiveButton()
// Login again on the same account
elementRobot.login(userId)
elementRobot.dismissVerificationIfPresent()
// TODO Deactivate account instead of logout? // TODO Deactivate account instead of logout?
} elementRobot.signout(expectSignOutWarning = false)
private fun ignoreVerification() {
sleep(6000)
val activity = EspressoHelper.getCurrentActivity()!!
val popup = activity.findViewById<View>(com.tapadoo.alerter.R.id.llAlertBackground)
activity.runOnUiThread {
popup.performClick()
}
assertDisplayed(R.id.bottomSheetFragmentContainer)
onView(isRoot()).perform(SleepViewAction.sleep(2000))
clickOn(R.string.skip)
assertDisplayed(R.string.are_you_sure)
clickOn(R.string.skip)
} }
} }

View File

@ -16,15 +16,17 @@
package im.vector.app.ui.robot package im.vector.app.ui.robot
import android.view.View
import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaDialogInteractions.clickDialogNegativeButton
import com.adevinta.android.barista.interaction.BaristaDialogInteractions.clickDialogPositiveButton
import com.adevinta.android.barista.interaction.BaristaDrawerInteractions.openDrawer import com.adevinta.android.barista.interaction.BaristaDrawerInteractions.openDrawer
import im.vector.app.EspressoHelper import im.vector.app.EspressoHelper
import im.vector.app.R import im.vector.app.R
import im.vector.app.activityIdlingResource
import im.vector.app.espresso.tools.waitUntilActivityVisible import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.espresso.tools.waitUntilViewVisible import im.vector.app.espresso.tools.waitUntilViewVisible
import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.createdirect.CreateDirectRoomActivity
@ -33,25 +35,31 @@ import im.vector.app.features.login.LoginActivity
import im.vector.app.initialSyncIdlingResource import im.vector.app.initialSyncIdlingResource
import im.vector.app.ui.robot.settings.SettingsRobot import im.vector.app.ui.robot.settings.SettingsRobot
import im.vector.app.withIdlingResource import im.vector.app.withIdlingResource
import timber.log.Timber
class ElementRobot { class ElementRobot {
fun login(userId: String) { fun signUp(userId: String) {
val onboardingRobot = OnboardingRobot() val onboardingRobot = OnboardingRobot()
onboardingRobot.createAccount(userId = userId) onboardingRobot.createAccount(userId = userId)
waitForHome()
}
withIdlingResource(activityIdlingResource(HomeActivity::class.java)) { fun login(userId: String) {
BaristaVisibilityAssertions.assertDisplayed(R.id.roomListContainer) val onboardingRobot = OnboardingRobot()
ViewActions.closeSoftKeyboard() onboardingRobot.login(userId = userId)
waitForHome()
}
private fun waitForHome() {
waitUntilActivityVisible<HomeActivity> {
waitUntilViewVisible(withId(R.id.roomListContainer))
} }
val activity = EspressoHelper.getCurrentActivity()!! val activity = EspressoHelper.getCurrentActivity()!!
val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession() val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession()
withIdlingResource(initialSyncIdlingResource(uiSession)) { withIdlingResource(initialSyncIdlingResource(uiSession)) {
BaristaVisibilityAssertions.assertDisplayed(R.id.roomListContainer) waitUntilViewVisible(withId(R.id.bottomNavigationView))
} }
waitUntilViewVisible(withId(R.id.bottomNavigationView))
} }
fun settings(block: SettingsRobot.() -> Unit) { fun settings(block: SettingsRobot.() -> Unit) {
@ -87,10 +95,49 @@ class ElementRobot {
waitUntilViewVisible(withId(R.id.bottomNavigationView)) waitUntilViewVisible(withId(R.id.bottomNavigationView))
} }
fun signout() { fun signout(expectSignOutWarning: Boolean) {
OnboardingRobot().signout() clickOn(R.id.groupToolbarAvatarImageView)
clickOn(R.id.homeDrawerHeaderSignoutView)
val isShowingSignOutWarning = kotlin.runCatching {
waitUntilViewVisible(withId(R.id.exitAnywayButton))
}.isSuccess
if (expectSignOutWarning != isShowingSignOutWarning) {
Timber.w("Unexpected sign out flow, expected warning to be: ${expectSignOutWarning.toWarningType()} but was ${isShowingSignOutWarning.toWarningType()}")
}
if (isShowingSignOutWarning) {
// We have sent a message in a e2e room, accept to loose it
clickOn(R.id.exitAnywayButton)
// Dark pattern
waitUntilViewVisible(withId(android.R.id.button2))
clickDialogNegativeButton()
} else {
waitUntilViewVisible(withId(android.R.id.button1))
clickDialogPositiveButton()
}
waitUntilActivityVisible<LoginActivity> { waitUntilActivityVisible<LoginActivity> {
BaristaVisibilityAssertions.assertDisplayed(R.id.loginSplashLogo) assertDisplayed(R.id.loginSplashLogo)
} }
} }
fun dismissVerificationIfPresent() {
kotlin.runCatching {
Thread.sleep(6000)
val activity = EspressoHelper.getCurrentActivity()!!
val popup = activity.findViewById<View>(com.tapadoo.alerter.R.id.llAlertBackground)!!
activity.runOnUiThread { popup.performClick() }
waitUntilViewVisible(withId(R.id.bottomSheetFragmentContainer))
waitUntilViewVisible(ViewMatchers.withText(R.string.skip))
clickOn(R.string.skip)
assertDisplayed(R.string.are_you_sure)
clickOn(R.string.skip)
waitUntilViewVisible(withId(R.id.bottomSheetFragmentContainer))
}.onFailure { Timber.w("Verification popup missing", it) }
}
} }
private fun Boolean.toWarningType() = if (this) "shown" else "skipped"

View File

@ -24,11 +24,8 @@ import com.adevinta.android.barista.assertion.BaristaEnabledAssertions.assertDis
import com.adevinta.android.barista.assertion.BaristaEnabledAssertions.assertEnabled import com.adevinta.android.barista.assertion.BaristaEnabledAssertions.assertEnabled
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaDialogInteractions
import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo
import im.vector.app.R import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.features.home.HomeActivity
import im.vector.app.waitForView import im.vector.app.waitForView
class OnboardingRobot { class OnboardingRobot {
@ -78,20 +75,5 @@ class OnboardingRobot {
closeSoftKeyboard() closeSoftKeyboard()
clickOn(R.id.loginSubmit) clickOn(R.id.loginSubmit)
// Wait
waitUntilActivityVisible<HomeActivity> {
assertDisplayed(R.id.homeDetailFragmentContainer)
}
}
fun signout() {
clickOn(R.id.groupToolbarAvatarImageView)
clickOn(R.id.homeDrawerHeaderSignoutView)
// We have sent a message in a e2e room, accept to loose it
clickOn(R.id.exitAnywayButton)
// Dark pattern
BaristaDialogInteractions.clickDialogNegativeButton()
} }
} }