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:
commit
9dd01d5b20
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() }
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue