Update state in runUpdatingState when CancellationException occurs (#5243)

This commit is contained in:
João Breno
2025-09-01 10:07:32 -03:00
committed by GitHub
parent 307596d5a3
commit fbca1b3dfe
3 changed files with 50 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ package io.element.android.libraries.architecture
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlinx.coroutines.TimeoutCancellationException
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@@ -159,16 +160,19 @@ suspend inline fun <T> runUpdatingState(
callsInPlace(resultBlock, InvocationKind.EXACTLY_ONCE)
}
state.value = AsyncAction.Loading
return resultBlock().fold(
return try {
resultBlock()
} catch (e: TimeoutCancellationException) {
state.value = AsyncAction.Failure(errorTransform(e))
throw e
}.fold(
onSuccess = {
state.value = AsyncAction.Success(it)
Result.success(it)
},
onFailure = {
val error = errorTransform(it)
state.value = AsyncAction.Failure(
error = error,
)
state.value = AsyncAction.Failure(error)
Result.failure(error)
}
)

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.architecture
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withTimeout
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Test
import kotlin.time.Duration.Companion.milliseconds
class AsyncActionTest {
@Test
fun `updates state on timeout`() = runTest {
val state: MutableState<AsyncAction<Int>> = mutableStateOf(AsyncAction.Uninitialized)
val timeoutMillis = 500L
val operationTimeMillis = 1000L
try {
runUpdatingState(state = state) {
withTimeout(timeoutMillis.milliseconds) {
delay(operationTimeMillis)
}
Result.success(0)
}
fail("Expected TimeoutCancellationException, but nothing was thrown")
} catch (e: TimeoutCancellationException) {
assertTrue(state.value.isFailure())
assertSame(e, state.value.errorOrNull())
}
}
}

View File

@@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.ForwardEventException
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.MsgLikeKind
import org.matrix.rustcomponents.sdk.RoomListService
@@ -63,9 +62,6 @@ class RoomContentForwarder(
}
}.onFailure {
failedForwardingTo.add(RoomId(room.id()))
if (it is CancellationException) {
throw it
}
}
}