Add catchingExceptions method to replace runCatching (#4797)

- Add `runCatchingExceptions` and `mapCatchingExceptions` to replace `runCatching` and `mapCatching`.
- Make `tryOrNull { ... }` catch only exceptions too.
- Apply the changes to the whole project.
- Add new Rust fakes for tests to handle the code that's now unblocked - previously it just threw an `UnsatisfiedLinkError` which we ignored.
- Add a new `detekt-rules` project with a `RunCatchingRule` to prevent `runCatching` and `mapCatching` usages.
This commit is contained in:
Jorge Martin Espinosa 2025-06-04 09:02:26 +02:00 committed by GitHub
parent 7816529fd7
commit efdc10e60a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
144 changed files with 716 additions and 375 deletions

View file

@ -56,6 +56,7 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@ -387,7 +388,7 @@ class MessagesPresenter @AssistedInject constructor(
private fun CoroutineScope.reinviteOtherUser(inviteProgress: MutableState<AsyncData<Unit>>) = launch(dispatchers.io) {
inviteProgress.value = AsyncData.Loading()
runCatching {
runCatchingExceptions {
val memberList = when (val memberState = room.membersStateFlow.value) {
is RoomMembersState.Ready -> memberState.roomMembers
is RoomMembersState.Error -> memberState.prevRoomMembers.orEmpty()

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.firstInstanceOf
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
@ -240,7 +241,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
sendActionState: MutableState<SendActionState>,
dismissAfterSend: Boolean,
replyParameters: ReplyParameters?,
) = runCatching {
) = runCatchingExceptions {
val context = coroutineContext
val progressCallback = object : ProgressCallback {
override fun onProgress(current: Long, total: Long) {

View file

@ -24,7 +24,7 @@ open class ForwardMessagesStateProvider : PreviewParameterProvider<ForwardMessag
)
),
aForwardMessagesState(
forwardAction = AsyncAction.Failure(Throwable("error")),
forwardAction = AsyncAction.Failure(RuntimeException("error")),
),
)
}

View file

@ -41,6 +41,7 @@ import io.element.android.features.messages.impl.messagecomposer.suggestions.Sug
import io.element.android.features.messages.impl.timeline.TimelineController
import io.element.android.features.messages.impl.utils.TextPillificationHelper
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.featureflag.api.FeatureFlagService
@ -512,7 +513,7 @@ class MessageComposerPresenter @AssistedInject constructor(
private suspend fun sendMedia(
uri: Uri,
mimeType: String,
) = runCatching {
) = runCatchingExceptions {
mediaSender.sendMedia(
uri = uri,
mimeType = mimeType,

View file

@ -17,7 +17,7 @@ open class ReportMessageStateProvider : PreviewParameterProvider<ReportMessageSt
aReportMessageState(reason = "This user is making the chat very toxic."),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Loading),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Failure(Throwable("error"))),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Failure(RuntimeException("error"))),
aReportMessageState(reason = "This user is making the chat very toxic.", blockUser = true, result = AsyncAction.Success(Unit)),
// Add other states here
)

View file

@ -62,11 +62,11 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransa
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_CAPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_ID_2
import io.element.android.libraries.matrix.test.core.aBuildMeta
@ -744,7 +744,7 @@ class MessagesPresenterTest {
canUserPinUnpinResult = { Result.success(true) },
),
typingNoticeResult = { Result.success(Unit) },
inviteUserResult = { Result.failure(Throwable("Oops!")) },
inviteUserResult = { Result.failure(RuntimeException("Oops!")) },
)
room.givenRoomMembersState(
RoomMembersState.Ready(
@ -892,7 +892,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action pin`() = runTest {
val successPinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) }
val failurePinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(A_THROWABLE) }
val failurePinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(AN_EXCEPTION) }
val analyticsService = FakeAnalyticsService()
val timeline = FakeTimeline()
val room = FakeJoinedRoom(
@ -932,7 +932,7 @@ class MessagesPresenterTest {
@Test
fun `present - handle action unpin`() = runTest {
val successUnpinEventLambda = lambdaRecorder { _: EventId -> Result.success(true) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(A_THROWABLE) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId -> Result.failure<Boolean>(AN_EXCEPTION) }
val timeline = FakeTimeline()
val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom(

View file

@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
@ -157,7 +157,7 @@ class PinnedMessagesListPresenterTest {
@Test
fun `present - unpin event`() = runTest {
val successUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.success(true) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.failure<Boolean>(A_THROWABLE) }
val failureUnpinEventLambda = lambdaRecorder { _: EventId? -> Result.failure<Boolean>(AN_EXCEPTION) }
val pinnedEventsTimeline = createPinnedMessagesTimeline()
val analyticsService = FakeAnalyticsService()
val room = FakeJoinedRoom(

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createComposeRule
import com.google.common.truth.Truth.assertThat
import io.element.android.features.messages.impl.utils.FakeMentionSpanFormatter
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
@ -35,7 +36,7 @@ class DefaultHtmlConverterProviderTest {
@Test
fun `calling provide without calling Update first should throw an exception`() {
val exception = runCatching { provider.provide() }.exceptionOrNull()
val exception = runCatchingExceptions { provider.provide() }.exceptionOrNull()
assertThat(exception).isInstanceOf(IllegalStateException::class.java)
}
@ -47,7 +48,7 @@ class DefaultHtmlConverterProviderTest {
provider.Update()
}
}
val htmlConverter = runCatching { provider.provide() }.getOrNull()
val htmlConverter = runCatchingExceptions { provider.provide() }.getOrNull()
assertThat(htmlConverter).isNotNull()
}

View file

@ -562,7 +562,7 @@ import kotlin.time.Duration.Companion.seconds
liveTimeline = FakeTimeline(
timelineItems = flowOf(emptyList()),
),
createTimelineResult = { Result.failure(Throwable("An error")) },
createTimelineResult = { Result.failure(RuntimeException("An error")) },
baseRoom = FakeBaseRoom(canUserSendMessageResult = { _, _ -> Result.success(true) }),
)
)