Merge branch 'develop' into fix/crash-on-nightly-incorrect-di-cast
This commit is contained in:
commit
55185b540d
11 changed files with 119 additions and 107 deletions
4
.idea/kotlinc.xml
generated
4
.idea/kotlinc.xml
generated
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.2.20" />
|
<option name="version" value="2.3.0" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -177,8 +177,8 @@ class DefaultActiveCallManager(
|
||||||
suspend fun incomingCallTimedOut(displayMissedCallNotification: Boolean) = mutex.withLock {
|
suspend fun incomingCallTimedOut(displayMissedCallNotification: Boolean) = mutex.withLock {
|
||||||
Timber.tag(tag).d("Incoming call timed out")
|
Timber.tag(tag).d("Incoming call timed out")
|
||||||
|
|
||||||
val previousActiveCall = activeCall.value ?: return
|
val previousActiveCall = activeCall.value ?: return@withLock
|
||||||
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return
|
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return@withLock
|
||||||
activeCall.value = null
|
activeCall.value = null
|
||||||
if (activeWakeLock?.isHeld == true) {
|
if (activeWakeLock?.isHeld == true) {
|
||||||
Timber.tag(tag).d("Releasing partial wakelock after timeout")
|
Timber.tag(tag).d("Releasing partial wakelock after timeout")
|
||||||
|
|
@ -196,11 +196,11 @@ class DefaultActiveCallManager(
|
||||||
Timber.tag(tag).d("Hung up call: $callType")
|
Timber.tag(tag).d("Hung up call: $callType")
|
||||||
val currentActiveCall = activeCall.value ?: run {
|
val currentActiveCall = activeCall.value ?: run {
|
||||||
Timber.tag(tag).w("No active call, ignoring hang up")
|
Timber.tag(tag).w("No active call, ignoring hang up")
|
||||||
return
|
return@withLock
|
||||||
}
|
}
|
||||||
if (currentActiveCall.callType != callType) {
|
if (currentActiveCall.callType != callType) {
|
||||||
Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring")
|
Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring")
|
||||||
return
|
return@withLock
|
||||||
}
|
}
|
||||||
if (currentActiveCall.callState is CallState.Ringing) {
|
if (currentActiveCall.callState is CallState.Ringing) {
|
||||||
// Decline the call
|
// Decline the call
|
||||||
|
|
|
||||||
|
|
@ -51,15 +51,10 @@ import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
|
||||||
import io.mockk.unmockkAll
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.advanceUntilIdle
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
@ -76,17 +71,6 @@ class ConfigureRoomPresenterTest {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val warmUpRule = WarmUpRule()
|
val warmUpRule = WarmUpRule()
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
mockkStatic(File::readBytes)
|
|
||||||
every { any<File>().readBytes() } returns byteArrayOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun tearDown() {
|
|
||||||
unmockkAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = createConfigureRoomPresenter()
|
val presenter = createConfigureRoomPresenter()
|
||||||
|
|
@ -261,20 +245,25 @@ class ConfigureRoomPresenterTest {
|
||||||
val initialState = initialState()
|
val initialState = initialState()
|
||||||
dataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY))
|
dataStore.setAvatarUri(Uri.parse(AN_URI_FROM_GALLERY))
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(mockk(), mockk(), mockk())))
|
val file = File.createTempFile("test", "jpg")
|
||||||
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
|
try {
|
||||||
|
mediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image(file, mockk(), mockk())))
|
||||||
|
matrixClient.givenUploadMediaResult(Result.failure(AN_EXCEPTION))
|
||||||
|
|
||||||
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
initialState.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
val stateAfterCreateRoom = awaitItem()
|
val stateAfterCreateRoom = awaitItem()
|
||||||
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
|
assertThat(stateAfterCreateRoom.createRoomAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
assertThat(analyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
|
assertThat(analyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
|
||||||
|
|
||||||
matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
|
matrixClient.givenUploadMediaResult(Result.success(AN_AVATAR_URL))
|
||||||
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
|
stateAfterCreateRoom.eventSink(ConfigureRoomEvents.CreateRoom)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Loading::class.java)
|
||||||
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
|
assertThat(awaitItem().createRoomAction).isInstanceOf(AsyncAction.Success::class.java)
|
||||||
|
} finally {
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -394,9 +394,8 @@ class TimelinePresenter(
|
||||||
newMostRecentItemId != prevMostRecentItemIdValue
|
newMostRecentItemId != prevMostRecentItemIdValue
|
||||||
|
|
||||||
if (hasNewEvent) {
|
if (hasNewEvent) {
|
||||||
val newMostRecentEvent = newMostRecentItem
|
|
||||||
// Scroll to bottom if the new event is from me, even if sent from another device
|
// 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.value = if (fromMe) {
|
||||||
NewEventState.FromMe
|
NewEventState.FromMe
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,6 @@ class EditUserProfilePresenterTest {
|
||||||
private val userAvatarUri: Uri = mockk()
|
private val userAvatarUri: Uri = mockk()
|
||||||
private val anotherAvatarUri: Uri = mockk()
|
private val anotherAvatarUri: Uri = mockk()
|
||||||
|
|
||||||
private val fakeFileContents = ByteArray(2)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
fakePickerProvider = FakePickerProvider()
|
fakePickerProvider = FakePickerProvider()
|
||||||
|
|
@ -397,7 +395,7 @@ class EditUserProfilePresenterTest {
|
||||||
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
|
fun `present - save processes and sets avatar when processor returns successfully`() = runTest {
|
||||||
val matrixClient = FakeMatrixClient()
|
val matrixClient = FakeMatrixClient()
|
||||||
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
val user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
givenPickerReturnsFile()
|
val tmpFile = givenPickerReturnsFile()
|
||||||
val presenter = createEditUserProfilePresenter(
|
val presenter = createEditUserProfilePresenter(
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
matrixUser = user,
|
matrixUser = user,
|
||||||
|
|
@ -405,12 +403,16 @@ class EditUserProfilePresenterTest {
|
||||||
deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) }
|
deleteLambda = { assertThat(it).isEqualTo(userAvatarUri) }
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
presenter.test {
|
try {
|
||||||
val initialState = awaitItem()
|
presenter.test {
|
||||||
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(EditUserProfileEvent.Save)
|
initialState.eventSink(EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled }
|
initialState.eventSink(EditUserProfileEvent.Save)
|
||||||
assertThat(matrixClient.uploadAvatarCalled).isTrue()
|
consumeItemsUntilPredicate { matrixClient.uploadAvatarCalled }
|
||||||
|
assertThat(matrixClient.uploadAvatarCalled).isTrue()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
tmpFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,30 +459,38 @@ class EditUserProfilePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - sets save action to failure if setting avatar fails`() = runTest {
|
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 user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenUploadAvatarResult(Result.failure(RuntimeException("!")))
|
givenUploadAvatarResult(Result.failure(RuntimeException("!")))
|
||||||
}
|
}
|
||||||
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
try {
|
||||||
|
saveAndAssertFailure(user, matrixClient, EditUserProfileEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
|
} finally {
|
||||||
|
tmpFile.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - CloseDialog resets save action state`() = runTest {
|
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 user = aMatrixUser(id = A_USER_ID.value, displayName = "Name", avatarUrl = AN_AVATAR_URL)
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenSetDisplayNameResult(Result.failure(RuntimeException("!")))
|
givenSetDisplayNameResult(Result.failure(RuntimeException("!")))
|
||||||
}
|
}
|
||||||
val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient)
|
val presenter = createEditUserProfilePresenter(matrixUser = user, matrixClient = matrixClient)
|
||||||
presenter.test {
|
try {
|
||||||
val initialState = awaitItem()
|
presenter.test {
|
||||||
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo"))
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(EditUserProfileEvent.Save)
|
initialState.eventSink(EditUserProfileEvent.UpdateDisplayName("foo"))
|
||||||
skipItems(2)
|
initialState.eventSink(EditUserProfileEvent.Save)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
skipItems(2)
|
||||||
initialState.eventSink(EditUserProfileEvent.CloseDialog)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::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() {
|
private fun givenPickerReturnsFile(): File {
|
||||||
mockkStatic(File::readBytes)
|
val file = File.createTempFile("test", "jpg")
|
||||||
val processedFile: File = mockk {
|
|
||||||
every { readBytes() } returns fakeFileContents
|
|
||||||
}
|
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
fakeMediaPreProcessor.givenResult(
|
fakeMediaPreProcessor.givenResult(
|
||||||
Result.success(
|
Result.success(
|
||||||
MediaUploadInfo.AnyFile(
|
MediaUploadInfo.AnyFile(
|
||||||
file = processedFile,
|
file = file,
|
||||||
fileInfo = mockk(),
|
fileInfo = mockk(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ class DefaultBugReporterTest {
|
||||||
while (part != null) {
|
while (part != null) {
|
||||||
part.headers["Content-Disposition"]?.let { contentDisposition ->
|
part.headers["Content-Disposition"]?.let { contentDisposition ->
|
||||||
regex.find(contentDisposition)?.groupValues?.get(1)?.let { name ->
|
regex.find(contentDisposition)?.groupValues?.get(1)?.let { name ->
|
||||||
foundValues.put(name, part!!.body.readUtf8())
|
foundValues.put(name, part.body.readUtf8())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
part = multipartReader.nextPart()
|
part = multipartReader.nextPart()
|
||||||
|
|
|
||||||
|
|
@ -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.fake.FakeTemporaryUriDeleter
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
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.lambda.value
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
|
@ -528,22 +529,29 @@ class RoomDetailsEditPresenterTest {
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
updateAvatarResult = updateAvatarResult,
|
updateAvatarResult = updateAvatarResult,
|
||||||
)
|
)
|
||||||
givenPickerReturnsFile()
|
val tmpFile = givenPickerReturnsFile()
|
||||||
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
|
||||||
val presenter = createRoomDetailsEditPresenter(
|
val presenter = createRoomDetailsEditPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
|
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
|
||||||
)
|
)
|
||||||
presenter.test {
|
try {
|
||||||
val initialState = awaitItem()
|
presenter.test {
|
||||||
initialState.eventSink(RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvent.Save)
|
initialState.eventSink(RoomDetailsEditEvent.HandleAvatarAction(AvatarAction.ChoosePhoto))
|
||||||
skipItems(4)
|
initialState.eventSink(RoomDetailsEditEvent.Save)
|
||||||
updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents))
|
skipItems(4)
|
||||||
deleteCallback.assertions().isCalledExactly(2).withSequence(
|
updateAvatarResult.assertions().isCalledOnce().with(
|
||||||
listOf(value(null)),
|
value(MimeTypes.Jpeg),
|
||||||
listOf(value(roomAvatarUri)),
|
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
|
@Test
|
||||||
fun `present - sets save action to failure if setting avatar fails`() = runTest {
|
fun `present - sets save action to failure if setting avatar fails`() = runTest {
|
||||||
givenPickerReturnsFile()
|
val tmpFile = givenPickerReturnsFile()
|
||||||
val room = aJoinedRoom(
|
val room = aJoinedRoom(
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
avatarUrl = AN_AVATAR_URL,
|
avatarUrl = AN_AVATAR_URL,
|
||||||
updateAvatarResult = { _, _ -> Result.failure(RuntimeException("!")) },
|
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
|
@Test
|
||||||
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
fun `present - CancelSaveChanges resets save action state`() = runTest {
|
||||||
givenPickerReturnsFile()
|
val tmpFile = givenPickerReturnsFile()
|
||||||
val room = aJoinedRoom(
|
val room = aJoinedRoom(
|
||||||
topic = "My topic",
|
topic = "My topic",
|
||||||
displayName = "Name",
|
displayName = "Name",
|
||||||
|
|
@ -629,14 +641,18 @@ class RoomDetailsEditPresenterTest {
|
||||||
room = room,
|
room = room,
|
||||||
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
|
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
|
||||||
)
|
)
|
||||||
presenter.test {
|
try {
|
||||||
val initialState = awaitItem()
|
presenter.test {
|
||||||
initialState.eventSink(RoomDetailsEditEvent.UpdateRoomTopic("foo"))
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(RoomDetailsEditEvent.Save)
|
initialState.eventSink(RoomDetailsEditEvent.UpdateRoomTopic("foo"))
|
||||||
skipItems(3)
|
initialState.eventSink(RoomDetailsEditEvent.Save)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
skipItems(3)
|
||||||
initialState.eventSink(RoomDetailsEditEvent.CloseDialog)
|
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||||
assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Uninitialized::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() {
|
private fun givenPickerReturnsFile(): File {
|
||||||
mockkStatic(File::readBytes)
|
val tmpFile = File.createTempFile("test", "jpg")
|
||||||
val processedFile: File = mockk {
|
tmpFile.writeBytes(fakeFileContents)
|
||||||
every { readBytes() } returns fakeFileContents
|
|
||||||
}
|
|
||||||
fakePickerProvider.givenResult(anotherAvatarUri)
|
fakePickerProvider.givenResult(anotherAvatarUri)
|
||||||
fakeMediaPreProcessor.givenResult(
|
fakeMediaPreProcessor.givenResult(
|
||||||
Result.success(
|
Result.success(
|
||||||
MediaUploadInfo.AnyFile(
|
MediaUploadInfo.AnyFile(
|
||||||
file = processedFile,
|
file = tmpFile,
|
||||||
fileInfo = mockk(),
|
fileInfo = mockk(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
return tmpFile
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun aJoinedRoom(
|
private fun aJoinedRoom(
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
# Project
|
# Project
|
||||||
android_gradle_plugin = "8.13.2"
|
android_gradle_plugin = "8.13.2"
|
||||||
# When updateing this, please also update the version in the file ./idea/kotlinc.xml
|
# When updateing this, please also update the version in the file ./idea/kotlinc.xml
|
||||||
kotlin = "2.2.20"
|
kotlin = "2.3.0"
|
||||||
kotlinpoet = "2.2.0"
|
kotlinpoet = "2.2.0"
|
||||||
ksp = "2.2.20-2.0.4"
|
ksp = "2.3.4"
|
||||||
firebaseAppDistribution = "5.2.0"
|
firebaseAppDistribution = "5.2.0"
|
||||||
|
|
||||||
# AndroidX
|
# AndroidX
|
||||||
|
|
@ -62,7 +62,7 @@ detekt = "1.23.8"
|
||||||
# See https://github.com/pinterest/ktlint/releases/
|
# See https://github.com/pinterest/ktlint/releases/
|
||||||
ktlint = "1.8.0"
|
ktlint = "1.8.0"
|
||||||
androidx-test-ext-junit = "1.3.0"
|
androidx-test-ext-junit = "1.3.0"
|
||||||
kover = "0.9.2"
|
kover = "0.9.4"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Project
|
# Project
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class DiffCacheUpdater<ListItem, CachedItem>(
|
||||||
private val cacheInvalidator: DiffCacheInvalidator<CachedItem> = DefaultDiffCacheInvalidator(),
|
private val cacheInvalidator: DiffCacheInvalidator<CachedItem> = DefaultDiffCacheInvalidator(),
|
||||||
private val areItemsTheSame: (oldItem: ListItem?, newItem: ListItem?) -> Boolean,
|
private val areItemsTheSame: (oldItem: ListItem?, newItem: ListItem?) -> Boolean,
|
||||||
) {
|
) {
|
||||||
private val lock = Object()
|
private val lock = Any()
|
||||||
private var prevOriginalList: List<ListItem> = emptyList()
|
private var prevOriginalList: List<ListItem> = emptyList()
|
||||||
|
|
||||||
private val listUpdateCallback = object : ListUpdateCallback {
|
private val listUpdateCallback = object : ListUpdateCallback {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ object LinkifyHelper {
|
||||||
@LinkifyCompat.LinkifyMask linkifyMask: Int = Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES,
|
@LinkifyCompat.LinkifyMask linkifyMask: Int = Linkify.WEB_URLS or Linkify.PHONE_NUMBERS or Linkify.EMAIL_ADDRESSES,
|
||||||
): CharSequence {
|
): CharSequence {
|
||||||
// Convert the text to a Spannable to be able to add URL spans, return the original text if it's not possible (in tests, i.e.)
|
// Convert the text to a Spannable to be able to add URL spans, return the original text if it's not possible (in tests, i.e.)
|
||||||
|
@Suppress("USELESS_ELVIS")
|
||||||
val spannable = text.toSpannable() ?: return text
|
val spannable = text.toSpannable() ?: return text
|
||||||
|
|
||||||
// Get all URL spans, as they will be removed by LinkifyCompat.addLinks
|
// Get all URL spans, as they will be removed by LinkifyCompat.addLinks
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ class FakeTimeline(
|
||||||
imageInfo: ImageInfo,
|
imageInfo: ImageInfo,
|
||||||
body: String?,
|
body: String?,
|
||||||
formattedBody: String?,
|
formattedBody: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
||||||
Result.success(FakeMediaUploadHandler())
|
Result.success(FakeMediaUploadHandler())
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +169,7 @@ class FakeTimeline(
|
||||||
imageInfo: ImageInfo,
|
imageInfo: ImageInfo,
|
||||||
caption: String?,
|
caption: String?,
|
||||||
formattedCaption: String?,
|
formattedCaption: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> = simulateLongTask {
|
): Result<MediaUploadHandler> = simulateLongTask {
|
||||||
sendImageLambda(
|
sendImageLambda(
|
||||||
file,
|
file,
|
||||||
|
|
@ -187,7 +187,7 @@ class FakeTimeline(
|
||||||
videoInfo: VideoInfo,
|
videoInfo: VideoInfo,
|
||||||
body: String?,
|
body: String?,
|
||||||
formattedBody: String?,
|
formattedBody: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
||||||
Result.success(FakeMediaUploadHandler())
|
Result.success(FakeMediaUploadHandler())
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +198,7 @@ class FakeTimeline(
|
||||||
videoInfo: VideoInfo,
|
videoInfo: VideoInfo,
|
||||||
caption: String?,
|
caption: String?,
|
||||||
formattedCaption: String?,
|
formattedCaption: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> = simulateLongTask {
|
): Result<MediaUploadHandler> = simulateLongTask {
|
||||||
sendVideoLambda(
|
sendVideoLambda(
|
||||||
file,
|
file,
|
||||||
|
|
@ -215,7 +215,7 @@ class FakeTimeline(
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
caption: String?,
|
caption: String?,
|
||||||
formattedCaption: String?,
|
formattedCaption: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
||||||
Result.success(FakeMediaUploadHandler())
|
Result.success(FakeMediaUploadHandler())
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +225,7 @@ class FakeTimeline(
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
caption: String?,
|
caption: String?,
|
||||||
formattedCaption: String?,
|
formattedCaption: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> = simulateLongTask {
|
): Result<MediaUploadHandler> = simulateLongTask {
|
||||||
sendAudioLambda(
|
sendAudioLambda(
|
||||||
file,
|
file,
|
||||||
|
|
@ -241,7 +241,7 @@ class FakeTimeline(
|
||||||
fileInfo: FileInfo,
|
fileInfo: FileInfo,
|
||||||
caption: String?,
|
caption: String?,
|
||||||
formattedCaption: String?,
|
formattedCaption: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
||||||
Result.success(FakeMediaUploadHandler())
|
Result.success(FakeMediaUploadHandler())
|
||||||
}
|
}
|
||||||
|
|
@ -251,7 +251,7 @@ class FakeTimeline(
|
||||||
fileInfo: FileInfo,
|
fileInfo: FileInfo,
|
||||||
caption: String?,
|
caption: String?,
|
||||||
formattedCaption: String?,
|
formattedCaption: String?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> = simulateLongTask {
|
): Result<MediaUploadHandler> = simulateLongTask {
|
||||||
sendFileLambda(
|
sendFileLambda(
|
||||||
file,
|
file,
|
||||||
|
|
@ -266,7 +266,7 @@ class FakeTimeline(
|
||||||
file: File,
|
file: File,
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
waveform: List<Float>,
|
waveform: List<Float>,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
) -> Result<MediaUploadHandler> = { _, _, _, _ ->
|
) -> Result<MediaUploadHandler> = { _, _, _, _ ->
|
||||||
Result.success(FakeMediaUploadHandler())
|
Result.success(FakeMediaUploadHandler())
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +275,7 @@ class FakeTimeline(
|
||||||
file: File,
|
file: File,
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
waveform: List<Float>,
|
waveform: List<Float>,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
): Result<MediaUploadHandler> = simulateLongTask {
|
): Result<MediaUploadHandler> = simulateLongTask {
|
||||||
sendVoiceMessageLambda(
|
sendVoiceMessageLambda(
|
||||||
file,
|
file,
|
||||||
|
|
@ -291,7 +291,7 @@ class FakeTimeline(
|
||||||
description: String?,
|
description: String?,
|
||||||
zoomLevel: Int?,
|
zoomLevel: Int?,
|
||||||
assetType: AssetType?,
|
assetType: AssetType?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
) -> Result<Unit> = { _, _, _, _, _, _ ->
|
) -> Result<Unit> = { _, _, _, _, _, _ ->
|
||||||
lambdaError()
|
lambdaError()
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +302,7 @@ class FakeTimeline(
|
||||||
description: String?,
|
description: String?,
|
||||||
zoomLevel: Int?,
|
zoomLevel: Int?,
|
||||||
assetType: AssetType?,
|
assetType: AssetType?,
|
||||||
inReplyToEventId: EventId??,
|
inReplyToEventId: EventId?,
|
||||||
): Result<Unit> = simulateLongTask {
|
): Result<Unit> = simulateLongTask {
|
||||||
sendLocationLambda(
|
sendLocationLambda(
|
||||||
body,
|
body,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue