Fix RegistrationEndToEndTest flakiness.

This commit is contained in:
Greyson Parrelli
2026-07-22 12:14:46 -04:00
committed by Michelle Tang
parent ab61b79028
commit 8b1c2bf2af
2 changed files with 26 additions and 11 deletions
@@ -118,7 +118,6 @@ class RegistrationEndToEndTest {
networkController = FakeNetworkController()
storageController = FakeStorageController()
repository = RegistrationRepository(context, networkController, storageController, isLinkAndSyncAvailable = false)
viewModel = RegistrationViewModel(repository, SavedStateHandle())
}
@Test
@@ -952,10 +951,14 @@ class RegistrationEndToEndTest {
enterPhoneNumber()
enterAep(wrongAep)
// The server rejects the recovery password derived from the wrong AEP, disabling submission until the key changes
waitFor("the incorrect AEP to be rejected") {
// The server rejects the recovery password derived from the wrong AEP, disabling submission until the key changes.
// Wait on the error text rather than the disabled button, which is also disabled while the attempt is in flight.
waitForText(ApplicationProvider.getApplicationContext<Application>().getString(R.string.EnterAepScreen__incorrect_recovery_key))
assert(
composeTestRule.onAllNodesWithTag(TestTags.ENTER_AEP_NEXT_BUTTON).fetchSemanticsNodes().firstOrNull()
?.config?.getOrNull(SemanticsProperties.Disabled) != null
) {
"Expected submission to be disabled after the AEP was rejected"
}
assert(networkController.lastRegisterAccountRequest?.recoveryPassword == wrongAep.deriveMasterKey().deriveRegistrationRecoveryPassword()) {
"Expected a registration attempt with the wrong recovery password but was ${networkController.lastRegisterAccountRequest}"
@@ -1295,6 +1298,9 @@ class RegistrationEndToEndTest {
folderPickerResult: Uri? = null,
onRegistrationComplete: () -> Unit = {}
) {
// Created here rather than in setup() so that its async init cannot read controller state before the test has finished configuring it
viewModel = RegistrationViewModel(repository, SavedStateHandle())
composeTestRule.setContent {
SignalTheme {
ActivityResultInterceptor(folderPickerResult) {
@@ -29,6 +29,9 @@ import java.time.LocalDateTime
*/
class FakeStorageController : StorageController {
/** Guards [inProgressData], whose read-modify-write updates arrive concurrently from multiple [kotlinx.coroutines.Dispatchers.IO] threads. */
private val dataLock = Any()
var inProgressData: RegistrationData = RegistrationData()
private set
var committedData: RegistrationData? = null
@@ -73,24 +76,30 @@ class FakeStorageController : StorageController {
override suspend fun getPreExistingRegistrationData(): PreExistingRegistrationData? = preExistingRegistrationData
override suspend fun clearAllData() {
inProgressData = RegistrationData()
synchronized(dataLock) {
inProgressData = RegistrationData()
}
}
override suspend fun clearLocalDataAndRestart() = notExpected()
override suspend fun readInProgressRegistrationData(): RegistrationData = inProgressData
override suspend fun readInProgressRegistrationData(): RegistrationData = synchronized(dataLock) { inProgressData }
override suspend fun updateInProgressRegistrationData(updater: RegistrationData.Builder.() -> Unit) {
inProgressData = inProgressData.newBuilder().apply(updater).lastUpdatedMillis(System.currentTimeMillis()).build()
synchronized(dataLock) {
inProgressData = inProgressData.newBuilder().apply(updater).lastUpdatedMillis(System.currentTimeMillis()).build()
}
}
override suspend fun commitRegistrationData() {
val accountData = inProgressData.accountData
val accountDataComplete = accountData != null && accountData.e164.isNotEmpty() && accountData.aci.isNotEmpty() && accountData.pni.isNotEmpty() && accountData.servicePassword.isNotEmpty()
if (!inProgressData.accountDataCommitted && accountDataComplete) {
inProgressData = inProgressData.newBuilder().accountDataCommitted(true).build()
synchronized(dataLock) {
val accountData = inProgressData.accountData
val accountDataComplete = accountData != null && accountData.e164.isNotEmpty() && accountData.aci.isNotEmpty() && accountData.pni.isNotEmpty() && accountData.servicePassword.isNotEmpty()
if (!inProgressData.accountDataCommitted && accountDataComplete) {
inProgressData = inProgressData.newBuilder().accountDataCommitted(true).build()
}
committedData = inProgressData
}
committedData = inProgressData
}
override suspend fun setRestoreDecision(decision: RestoreDecision) {