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 01d6012760
commit 58a3ea8b1f
144 changed files with 716 additions and 375 deletions

View file

@ -24,6 +24,7 @@ import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.StateEventType
@ -216,7 +217,7 @@ class RoomDetailsEditPresenter @Inject constructor(
}
private suspend fun updateAvatar(avatarUri: Uri?): Result<Unit> {
return runCatching {
return runCatchingExceptions {
if (avatarUri != null) {
val preprocessed = mediaPreProcessor.process(
uri = avatarUri,

View file

@ -27,7 +27,7 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEd
aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
aRoomDetailsEditState(saveAction = AsyncAction.Loading),
aRoomDetailsEditState(saveAction = AsyncAction.Failure(Throwable("Whelp"))),
aRoomDetailsEditState(saveAction = AsyncAction.Failure(RuntimeException("Whelp"))),
)
}

View file

@ -19,9 +19,9 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider<
aRoomNotificationSettingsState(),
aRoomNotificationSettingsState(isDefault = false),
aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Loading),
aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Failure(Throwable("error"))),
aRoomNotificationSettingsState(setNotificationSettingAction = AsyncAction.Failure(RuntimeException("error"))),
aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Loading),
aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Failure(Throwable("error"))),
aRoomNotificationSettingsState(restoreDefaultAction = AsyncAction.Failure(RuntimeException("error"))),
aRoomNotificationSettingsState(displayMentionsOnlyDisclaimer = true)
)

View file

@ -258,7 +258,7 @@ class RoomDetailsPresenterTest {
@Test
fun `present - initial state when canInvite errors`() = runTest {
val room = aJoinedRoom(
canInviteResult = { Result.failure(Throwable("Whoops")) },
canInviteResult = { Result.failure(RuntimeException("Whoops")) },
canUserJoinCallResult = { Result.success(true) },
canSendStateResult = { _, _ -> Result.success(true) },
)
@ -277,7 +277,7 @@ class RoomDetailsPresenterTest {
when (stateEventType) {
StateEventType.ROOM_TOPIC -> Result.success(true)
StateEventType.ROOM_NAME -> Result.success(false)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
canBanResult = { Result.success(false) },
@ -306,7 +306,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME,
StateEventType.ROOM_AVATAR -> Result.success(true)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
canKickResult = { Result.success(false) },
@ -357,7 +357,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_AVATAR,
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME -> Result.success(true)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
userDisplayNameResult = { Result.success(A_USER_NAME) },
@ -403,7 +403,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME,
StateEventType.ROOM_AVATAR -> Result.success(true)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
canKickResult = {
@ -436,7 +436,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME,
StateEventType.ROOM_AVATAR -> Result.success(false)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
canBanResult = {
@ -468,7 +468,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_AVATAR,
StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_TOPIC -> Result.success(false)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
canKickResult = {
@ -500,7 +500,7 @@ class RoomDetailsPresenterTest {
StateEventType.ROOM_AVATAR,
StateEventType.ROOM_TOPIC,
StateEventType.ROOM_NAME -> Result.success(true)
else -> Result.failure(Throwable("Whelp"))
else -> Result.failure(RuntimeException("Whelp"))
}
},
canKickResult = {

View file

@ -124,7 +124,7 @@ class RoomDetailsEditPresenterTest {
when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(true)
StateEventType.ROOM_AVATAR -> Result.success(false)
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops"))
StateEventType.ROOM_TOPIC -> Result.failure(RuntimeException("Oops"))
else -> lambdaError()
}
},
@ -157,7 +157,7 @@ class RoomDetailsEditPresenterTest {
when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(false)
StateEventType.ROOM_AVATAR -> Result.success(true)
StateEventType.ROOM_TOPIC -> Result.failure(Throwable("Oops"))
StateEventType.ROOM_TOPIC -> Result.failure(RuntimeException("Oops"))
else -> lambdaError()
}
}
@ -188,7 +188,7 @@ class RoomDetailsEditPresenterTest {
canSendStateResult = { _, stateEventType ->
when (stateEventType) {
StateEventType.ROOM_NAME -> Result.success(false)
StateEventType.ROOM_AVATAR -> Result.failure(Throwable("Oops"))
StateEventType.ROOM_AVATAR -> Result.failure(RuntimeException("Oops"))
StateEventType.ROOM_TOPIC -> Result.success(true)
else -> lambdaError()
}
@ -559,7 +559,7 @@ class RoomDetailsEditPresenterTest {
canSendStateResult = { _, _ -> Result.success(true) }
)
fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no")))
fakeMediaPreProcessor.givenResult(Result.failure(RuntimeException("Oh no")))
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
val presenter = createRoomDetailsEditPresenter(
room = room,
@ -580,7 +580,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
setNameResult = { Result.failure(Throwable("!")) },
setNameResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"), deleteCallbackNumberOfInvocation = 1)
@ -592,7 +592,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
setTopicResult = { Result.failure(Throwable("!")) },
setTopicResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"), deleteCallbackNumberOfInvocation = 1)
@ -604,7 +604,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
removeAvatarResult = { Result.failure(Throwable("!")) },
removeAvatarResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 2)
@ -617,7 +617,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) },
updateAvatarResult = { _, _ -> Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2)
@ -630,7 +630,7 @@ class RoomDetailsEditPresenterTest {
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
setTopicResult = { Result.failure(Throwable("!")) },
setTopicResult = { Result.failure(RuntimeException("!")) },
canSendStateResult = { _, _ -> Result.success(true) }
)
val deleteCallback = lambdaRecorder<Uri?, Unit> {}

View file

@ -205,7 +205,7 @@ class RoomDetailsEditViewTest {
rule.setRoomDetailsEditView(
aRoomDetailsEditState(
eventSink = eventsRecorder,
saveAction = AsyncAction.Failure(Throwable("Whelp")),
saveAction = AsyncAction.Failure(RuntimeException("Whelp")),
),
)
rule.clickOn(CommonStrings.action_ok)

View file

@ -186,7 +186,7 @@ class RoomMemberListPresenterTest {
val presenter = createPresenter(
joinedRoom = FakeJoinedRoom(
baseRoom = FakeBaseRoom(
canInviteResult = { Result.failure(Throwable("Eek")) },
canInviteResult = { Result.failure(RuntimeException("Eek")) },
updateMembersResult = { Result.success(Unit) }
)
)

View file

@ -78,8 +78,8 @@ class RoomMemberDetailsPresenterTest {
avatarUrl = "Alice Avatar url",
)
val room = aJoinedRoom(
userDisplayNameResult = { Result.failure(Throwable()) },
userAvatarUrlResult = { Result.failure(Throwable()) },
userDisplayNameResult = { Result.failure(RuntimeException()) },
userAvatarUrlResult = { Result.failure(RuntimeException()) },
getUpdatedMemberResult = { Result.failure(AN_EXCEPTION) },
).apply {
givenRoomMembersState(RoomMembersState.Ready(persistentListOf(roomMember)))

View file

@ -13,8 +13,8 @@ import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.roomdetails.impl.aJoinedRoom
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.tests.testutils.awaitLastSequentialItem
@ -71,7 +71,7 @@ class RoomNotificationSettingsPresenterTest {
@Test
fun `present - notification settings set custom failed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
notificationSettingsService.givenSetNotificationModeError(A_THROWABLE)
notificationSettingsService.givenSetNotificationModeError(AN_EXCEPTION)
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -131,7 +131,7 @@ class RoomNotificationSettingsPresenterTest {
@Test
fun `present - notification settings restore default failed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
notificationSettingsService.givenRestoreDefaultNotificationModeError(A_THROWABLE)
notificationSettingsService.givenRestoreDefaultNotificationModeError(AN_EXCEPTION)
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.toMatrixUser
@ -41,7 +42,7 @@ class ChangeRolesViewTest {
@Test
fun `passing a 'USER' role throws an exception`() {
val exception = runCatching {
val exception = runCatchingExceptions {
rule.setChangeRolesContent(
state = aChangeRolesState(
role = RoomMember.Role.USER,