fix(deps): update kotlin to 2.3.0 (#5917)

* fix(deps): update kotlin to 2.3.0

* Cleanup - remove `datetime` compat version

* Fix several lint issues caused by the Kotlin compiler inference working better (checks in nullables, vars, etc.)

* Fix tests by removing mock in `File.readBytes`, it seems like it's no longer allowed. Using a tmp file works well enough.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benoit Marty <benoit@matrix.org>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
renovate[bot] 2025-12-22 14:28:15 +01:00 committed by GitHub
parent dbf32a1bfe
commit 00dcbf4a7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 119 additions and 107 deletions

View file

@ -177,8 +177,8 @@ class DefaultActiveCallManager(
suspend fun incomingCallTimedOut(displayMissedCallNotification: Boolean) = mutex.withLock {
Timber.tag(tag).d("Incoming call timed out")
val previousActiveCall = activeCall.value ?: return
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return
val previousActiveCall = activeCall.value ?: return@withLock
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return@withLock
activeCall.value = null
if (activeWakeLock?.isHeld == true) {
Timber.tag(tag).d("Releasing partial wakelock after timeout")
@ -196,11 +196,11 @@ class DefaultActiveCallManager(
Timber.tag(tag).d("Hung up call: $callType")
val currentActiveCall = activeCall.value ?: run {
Timber.tag(tag).w("No active call, ignoring hang up")
return
return@withLock
}
if (currentActiveCall.callType != callType) {
Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring")
return
return@withLock
}
if (currentActiveCall.callState is CallState.Ringing) {
// Decline the call

View file

@ -51,15 +51,10 @@ import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.test
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -76,17 +71,6 @@ class ConfigureRoomPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
@Before
fun setup() {
mockkStatic(File::readBytes)
every { any<File>().readBytes() } returns byteArrayOf()
}
@After
fun tearDown() {
unmockkAll()
}
@Test
fun `present - initial state`() = runTest {
val presenter = createConfigureRoomPresenter()
@ -261,20 +245,25 @@ class ConfigureRoomPresenterTest {
val initialState = initialState()
dataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY))
skipItems(1)
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk())))
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
val file = File.createTempFile("test", "jpg")
try {
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(file, mockk(), mockk())))
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterCreateRoom = awaitItem()
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
assertThat(analyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
val stateAfterCreateRoom = awaitItem()
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
assertThat(analyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
} finally {
file.delete()
}
}
}

View file

@ -394,9 +394,8 @@ class TimelinePresenter(
newMostRecentItemId != prevMostRecentItemIdValue
if (hasNewEvent) {
val newMostRecentEvent = newMostRecentItem
// Scroll to bottom if the new event is from me, even if sent from another device
val fromMe = newMostRecentEvent?.isMine == true
val fromMe = newMostRecentItem.isMine
newEventState.value = if (fromMe) {
NewEventState.FromMe
} else {

View file

@ -56,8 +56,6 @@ class EditUserProfilePresenterTest {
private val userAvatarUri: Uri = mockk()
private val anotherAvatarUri: Uri = mockk()
private val fakeFileContents = ByteArray(2)
@Before
fun setup() {
fakePickerProvider = FakePickerProvider()
@ -397,7 +395,7 @@ class EditUserProfilePresenterTest {
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
val matrixClient = FakeMatrixClient()
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
givenPickerReturnsFile()
val tmpFile = givenPickerReturnsFile()
val presenter = createEditUserProfilePresenter(
matrixClient = matrixClient,
matrixUser = user,
@ -405,12 +403,16 @@ class EditUserProfilePresenterTest {
deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) }
),
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.Save)
consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled }
assertThat(matrixClient.uploadAvatarCalled).isTrue()
try {
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(EditUserProfileEvent.Save)
consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled }
assertThat(matrixClient.uploadAvatarCalled).isTrue()
}
} finally {
tmpFile.delete()
}
}
@ -457,30 +459,38 @@ class EditUserProfilePresenterTest {
@Test
fun `present - sets save action to failure if setting avatar fails`() = runTest {
givenPickerReturnsFile()
val tmpFile = givenPickerReturnsFile()
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
val matrixClient = FakeMatrixClient().apply {
givenUploadAvatarResult(Result.failure(RuntimeException("!")))
}
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
try {
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
} finally {
tmpFile.delete()
}
}
@Test
fun `present - CloseDialog resets save action state`() = runTest {
givenPickerReturnsFile()
val tmpFile = givenPickerReturnsFile()
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
val matrixClient = FakeMatrixClient().apply {
givenSetDisplayNameResult(Result.failure(RuntimeException("!")))
}
val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo"))
initialState.eventSink(EditUserProfileEvent.Save)
skipItems(2)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(EditUserProfileEvent.CloseDialog)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
try {
presenter.test {
val initialState = awaitItem()
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo"))
initialState.eventSink(EditUserProfileEvent.Save)
skipItems(2)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(EditUserProfileEvent.CloseDialog)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
}
} finally {
tmpFile.delete()
}
}
@ -502,20 +512,18 @@ class EditUserProfilePresenterTest {
}
}
private fun givenPickerReturnsFile() {
mockkStatic(File::readBytes)
val processedFile: File = mockk {
every { readBytes() } returns fakeFileContents
}
private fun givenPickerReturnsFile(): File {
val file = File.createTempFile("test", "jpg")
fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(
Result.success(
MediaUploadInfo.AnyFile(
file = processedFile,
file = file,
fileInfo = mockk(),
)
)
)
return file
}
companion object {

View file

@ -323,7 +323,7 @@ class DefaultBugReporterTest {
while (part != null) {
part.headers["Content-Disposition"]?.let { contentDisposition ->
regex.find(contentDisposition)?.groupValues?.get(1)?.let { name ->
foundValues.put(name, part!!.body.readUtf8())
foundValues.put(name, part.body.readUtf8())
}
}
part = multipartReader.nextPart()

View file

@ -35,6 +35,7 @@ import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.matching
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.test
import io.mockk.every
@ -528,22 +529,29 @@ class RoomDetailsEditPresenterTest {
avatarUrl = AN_AVATAR_URL,
updateAvatarResult = updateAvatarResult,
)
givenPickerReturnsFile()
val tmpFile = givenPickerReturnsFile()
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
val presenter = createRoomDetailsEditPresenter(
room = room,
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvent.Save)
skipItems(4)
updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents))
deleteCallback.assertions().isCalledExactly(2).withSequence(
listOf(value(null)),
listOf(value(roomAvatarUri)),
)
try {
presenter.test {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvent.Save)
skipItems(4)
updateAvatarResult.assertions().isCalledOnce().with(
value(MimeTypes.Jpeg),
matching<ByteArray> { it.contentEquals(fakeFileContents) }
)
deleteCallback.assertions().isCalledExactly(2).withSequence(
listOf(value(null)),
listOf(value(roomAvatarUri)),
)
}
} finally {
tmpFile.delete()
}
}
@ -605,19 +613,23 @@ class RoomDetailsEditPresenterTest {
@Test
fun `present - sets save action to failure if setting avatar fails`() = runTest {
givenPickerReturnsFile()
val tmpFile = givenPickerReturnsFile()
val room = aJoinedRoom(
topic = "My topic",
displayName = "Name",
avatarUrl = AN_AVATAR_URL,
updateAvatarResult = { _, _ -> Result.failure(RuntimeException("!")) },
)
saveAndAssertFailure(room, RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2)
try {
saveAndAssertFailure(room, RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2)
} finally {
tmpFile.delete()
}
}
@Test
fun `present - CancelSaveChanges resets save action state`() = runTest {
givenPickerReturnsFile()
val tmpFile = givenPickerReturnsFile()
val room = aJoinedRoom(
topic = "My topic",
displayName = "Name",
@ -629,14 +641,18 @@ class RoomDetailsEditPresenterTest {
room = room,
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvent.UpdateRoomTopic("foo"))
initialState.eventSink(RoomDetailsEditEvent.Save)
skipItems(3)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(RoomDetailsEditEvent.CloseDialog)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
try {
presenter.test {
val initialState = awaitItem()
initialState.eventSink(RoomDetailsEditEvent.UpdateRoomTopic("foo"))
initialState.eventSink(RoomDetailsEditEvent.Save)
skipItems(3)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
initialState.eventSink(RoomDetailsEditEvent.CloseDialog)
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
}
} finally {
tmpFile.delete()
}
}
@ -736,20 +752,19 @@ class RoomDetailsEditPresenterTest {
}
}
private fun givenPickerReturnsFile() {
mockkStatic(File::readBytes)
val processedFile: File = mockk {
every { readBytes() } returns fakeFileContents
}
private fun givenPickerReturnsFile(): File {
val tmpFile = File.createTempFile("test", "jpg")
tmpFile.writeBytes(fakeFileContents)
fakePickerProvider.givenResult(anotherAvatarUri)
fakeMediaPreProcessor.givenResult(
Result.success(
MediaUploadInfo.AnyFile(
file = processedFile,
file = tmpFile,
fileInfo = mockk(),
)
)
)
return tmpFile
}
private fun aJoinedRoom(