From 7f04e9a8e5775967e7aed950cf3bdfb8e2f37b3f Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Wed, 19 Jun 2024 17:04:24 +0200 Subject: [PATCH 1/2] Add delay at least wrapper and apply to test api method --- .../kotlin/net/mullvad/mullvadvpn/util/Delay.kt | 11 +++++++++++ .../viewmodel/ApiAccessMethodDetailsViewModel.kt | 7 +++++-- .../viewmodel/EditApiAccessMethodViewModel.kt | 7 ++++++- .../mullvad/mullvadvpn/viewmodel/LoginViewModel.kt | 14 ++++++-------- 4 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Delay.kt diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Delay.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Delay.kt new file mode 100644 index 000000000000..3449ee23bfbb --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Delay.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn.util + +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay + +suspend fun delayAtLeast(duration: Long, f: suspend () -> T): T = coroutineScope { + val result = async { f() } + delay(timeMillis = duration) + result.await() +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt index a6ba01e81c9f..fc52cb7eeefb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModel.kt @@ -14,10 +14,12 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.state.ApiAccessMethodDetailsUiState +import net.mullvad.mullvadvpn.constant.MINIMUM_LOADING_TIME_MILLIS import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId import net.mullvad.mullvadvpn.lib.model.TestApiAccessMethodError import net.mullvad.mullvadvpn.repository.ApiAccessRepository +import net.mullvad.mullvadvpn.util.delayAtLeast class ApiAccessMethodDetailsViewModel( private val apiAccessMethodId: ApiAccessMethodId, @@ -107,8 +109,9 @@ class ApiAccessMethodDetailsViewModel( private suspend fun testMethodById(): Either { isTestingApiAccessMethodState.value = true - return apiAccessRepository - .testApiAccessMethodById(apiAccessMethodId) + return delayAtLeast(MINIMUM_LOADING_TIME_MILLIS) { + apiAccessRepository.testApiAccessMethodById(apiAccessMethodId) + } .onLeft { isTestingApiAccessMethodState.value = false } .onRight { isTestingApiAccessMethodState.value = false } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt index 87316e90e21d..043c50379229 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.state.ApiAccessMethodTypes import net.mullvad.mullvadvpn.compose.state.EditApiAccessFormData import net.mullvad.mullvadvpn.compose.state.EditApiAccessMethodUiState +import net.mullvad.mullvadvpn.constant.MINIMUM_LOADING_TIME_MILLIS import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodName @@ -32,6 +33,7 @@ import net.mullvad.mullvadvpn.lib.model.ParsePortError import net.mullvad.mullvadvpn.lib.model.Port import net.mullvad.mullvadvpn.lib.model.SocksAuth import net.mullvad.mullvadvpn.repository.ApiAccessRepository +import net.mullvad.mullvadvpn.util.delayAtLeast import org.apache.commons.validator.routines.InetAddressValidator class EditApiAccessMethodViewModel( @@ -104,7 +106,10 @@ class EditApiAccessMethodViewModel( { errors -> formData.update { it.updateWithErrors(errors) } }, { customProxy -> isTestingApiAccessMethod.value = true - val result = apiAccessRepository.testCustomApiAccessMethod(customProxy) + val result = + delayAtLeast(MINIMUM_LOADING_TIME_MILLIS) { + apiAccessRepository.testCustomApiAccessMethod(customProxy) + } _uiSideEffect.send( EditApiAccessSideEffect.TestApiAccessMethodResult(result.isRight()) ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt index e35baa571eb6..30bea42cfee3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt @@ -29,6 +29,7 @@ import net.mullvad.mullvadvpn.lib.model.LoginAccountError import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.repository.NewDeviceRepository import net.mullvad.mullvadvpn.usecase.InternetAvailableUseCase +import net.mullvad.mullvadvpn.util.delayAtLeast import net.mullvad.mullvadvpn.util.getOrDefault private const val MINIMUM_LOADING_SPINNER_TIME_MILLIS = 500L @@ -101,15 +102,12 @@ class LoginViewModel( } _loginState.value = Loading.LoggingIn viewModelScope.launch(dispatcher) { - // Ensure we always take at least MINIMUM_LOADING_SPINNER_TIME_MILLIS to show the - // loading indicator - val result = async { accountRepository.login(AccountNumber(accountNumber)) } - - delay(MINIMUM_LOADING_SPINNER_TIME_MILLIS) - val uiState = - result - .await() + // Ensure we always take at least MINIMUM_LOADING_SPINNER_TIME_MILLIS to show the + // loading indicator + delayAtLeast(MINIMUM_LOADING_SPINNER_TIME_MILLIS) { + accountRepository.login(AccountNumber(accountNumber)) + } .fold( { it.toUiState() }, { From 953bbdc07024a5a287dd6540376711da33335f9d Mon Sep 17 00:00:00 2001 From: Jonatan Rhodin Date: Wed, 19 Jun 2024 18:02:36 +0200 Subject: [PATCH 2/2] Fix unit test --- .../viewmodel/ApiAccessMethodDetailsViewModelTest.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt index 631deb12e46e..63ce73c1a3ae 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ApiAccessMethodDetailsViewModelTest.kt @@ -1,12 +1,14 @@ package net.mullvad.mullvadvpn.viewmodel import app.cash.turbine.test +import arrow.core.Either import arrow.core.left import arrow.core.right import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk +import io.mockk.mockkStatic import java.time.Duration import kotlin.test.assertIs import kotlinx.coroutines.flow.MutableStateFlow @@ -20,6 +22,7 @@ import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting import net.mullvad.mullvadvpn.lib.model.TestApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.UnknownApiAccessMethodError import net.mullvad.mullvadvpn.repository.ApiAccessRepository +import net.mullvad.mullvadvpn.util.delayAtLeast import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -38,6 +41,7 @@ class ApiAccessMethodDetailsViewModelTest { @BeforeEach fun setUp() { + mockkStatic(DELAY_UTIL) every { mockApiAccessRepository.apiAccessMethodSettingById(apiAccessMethodId) } returns accessMethodFlow every { mockApiAccessRepository.enabledApiAccessMethods() } returns enabledMethodsFlow @@ -58,6 +62,8 @@ class ApiAccessMethodDetailsViewModelTest { Unit.right() coEvery { mockApiAccessRepository.setCurrentApiAccessMethod(any()) } returns Unit.right() + coEvery { delayAtLeast>(any(), any()) } returns + Unit.right() // Act apiAccessMethodDetailsViewModel.setCurrentMethod() @@ -177,4 +183,8 @@ class ApiAccessMethodDetailsViewModelTest { ) } } + + companion object { + private const val DELAY_UTIL = "net.mullvad.mullvadvpn.util.DelayKt" + } }