From dc95f4553e18526e242569756f3fa970e5364eaf Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 10:38:05 +0200 Subject: [PATCH 1/8] Adding changelog entry --- changelog.d/6155.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6155.misc diff --git a/changelog.d/6155.misc b/changelog.d/6155.misc new file mode 100644 index 0000000000..044e21408e --- /dev/null +++ b/changelog.d/6155.misc @@ -0,0 +1 @@ +Add unit tests for LiveLocationAggregationProcessor code From 51b930147aa3df3a8728808599bc645eceff2625 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 11:30:08 +0200 Subject: [PATCH 2/8] Adding some tests on ignored cases --- .../LiveLocationAggregationProcessor.kt | 28 +++- .../LiveLocationAggregationProcessorTest.kt | 128 ++++++++++++++++++ .../android/sdk/test/fakes/FakeClock.kt | 27 ++++ .../sdk/test/fakes/FakeWorkManagerProvider.kt | 25 ++++ 4 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt index 05bde8f83f..a254552bb3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt @@ -36,16 +36,22 @@ import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject -// TODO add unit tests +/** + * Aggregates all live location sharing related events in local database. + */ internal class LiveLocationAggregationProcessor @Inject constructor( @SessionId private val sessionId: String, private val workManagerProvider: WorkManagerProvider, private val clock: Clock, ) { - fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) { + /** + * Handle the content of a beacon info. + * @return true if it has been processed, false if ignored. + */ + fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean): Boolean { if (event.senderId.isNullOrEmpty() || isLocalEcho) { - return + return false } val isLive = content.isLive.orTrue() @@ -58,7 +64,7 @@ internal class LiveLocationAggregationProcessor @Inject constructor( if (targetEventId.isNullOrEmpty()) { Timber.w("no target event id found for the beacon content") - return + return false } val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate( @@ -83,6 +89,8 @@ internal class LiveLocationAggregationProcessor @Inject constructor( } else { cancelDeactivationAfterTimeout(targetEventId, roomId) } + + return true } private fun scheduleDeactivationAfterTimeout(eventId: String, roomId: String, endOfLiveTimestampMillis: Long?) { @@ -110,6 +118,10 @@ internal class LiveLocationAggregationProcessor @Inject constructor( workManagerProvider.workManager.cancelUniqueWork(workName) } + /** + * Handle the content of a beacon location data. + * @return true if it has been processed, false if ignored. + */ fun handleBeaconLocationData( realm: Realm, event: Event, @@ -117,14 +129,14 @@ internal class LiveLocationAggregationProcessor @Inject constructor( roomId: String, relatedEventId: String?, isLocalEcho: Boolean - ) { + ): Boolean { if (event.senderId.isNullOrEmpty() || isLocalEcho) { - return + return false } if (relatedEventId.isNullOrEmpty()) { Timber.w("no related event id found for the live location content") - return + return false } val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate( @@ -143,6 +155,8 @@ internal class LiveLocationAggregationProcessor @Inject constructor( Timber.d("updating last location of the summary of id=$relatedEventId") aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent()) } + + return true } private fun deactivateAllPreviousBeacons(realm: Realm, roomId: String, userId: String, currentEventId: String) { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt new file mode 100644 index 0000000000..d8c7f7477b --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.aggregation.livelocation + +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent +import org.matrix.android.sdk.test.fakes.FakeClock +import org.matrix.android.sdk.test.fakes.FakeRealm +import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider + +private const val A_SESSION_ID = "session_id" +private const val A_SENDER_ID = "sender_id" +private const val AN_EVENT_ID = "event_id" + +internal class LiveLocationAggregationProcessorTest { + + private val fakeWorkManagerProvider = FakeWorkManagerProvider() + private val fakeClock = FakeClock() + private val fakeRealm = FakeRealm() + + private val liveLocationAggregationProcessor = LiveLocationAggregationProcessor( + sessionId = A_SESSION_ID, + workManagerProvider = fakeWorkManagerProvider.instance, + clock = fakeClock + ) + + @Test + fun `given beacon info when it is local echo then it is ignored`() { + val event = Event(senderId = A_SENDER_ID) + val beaconInfo = MessageBeaconInfoContent() + + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = event, + content = beaconInfo, + roomId = "", + isLocalEcho = true + ) + + result shouldBeEqualTo false + } + + @Test + fun `given beacon info and event when senderId is null or empty then it is ignored`() { + val eventNoSenderId = Event(eventId = AN_EVENT_ID) + val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") + val beaconInfo = MessageBeaconInfoContent() + + val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventNoSenderId, + content = beaconInfo, + roomId = "", + isLocalEcho = false + ) + val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventEmptySenderId, + content = beaconInfo, + roomId = "", + isLocalEcho = false + ) + + resultNoSenderId shouldBeEqualTo false + resultEmptySenderId shouldBeEqualTo false + } + + @Test + fun `given beacon location data when it is local echo then it is ignored`() { + val event = Event(senderId = A_SENDER_ID) + val beaconLocationData = MessageBeaconLocationDataContent() + + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = "", + relatedEventId = "", + isLocalEcho = true + ) + + result shouldBeEqualTo false + } + + @Test + fun `given beacon location data and event when senderId is null or empty then it is ignored`() { + val eventNoSenderId = Event(eventId = AN_EVENT_ID) + val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") + val beaconLocationData = MessageBeaconLocationDataContent() + + val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = eventNoSenderId, + content = beaconLocationData, + roomId = "", + relatedEventId = "", + isLocalEcho = false + ) + val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = eventEmptySenderId, + content = beaconLocationData, + roomId = "", + relatedEventId = "", + isLocalEcho = false + ) + + resultNoSenderId shouldBeEqualTo false + resultEmptySenderId shouldBeEqualTo false + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt new file mode 100644 index 0000000000..febf94f4cf --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import io.mockk.every +import io.mockk.mockk +import org.matrix.android.sdk.internal.util.time.Clock + +internal class FakeClock : Clock by mockk() { + fun givenEpoch(epoch: Long) { + every { epochMillis() } returns epoch + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt new file mode 100644 index 0000000000..9ba072d35c --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import io.mockk.mockk +import org.matrix.android.sdk.internal.di.WorkManagerProvider + +internal class FakeWorkManagerProvider { + + val instance = mockk() +} From dccc3b457debc693bf40650e9cc409c2807ae965 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 11:45:35 +0200 Subject: [PATCH 3/8] Adding more tests on ignored cases --- .../LiveLocationAggregationProcessorTest.kt | 91 +++++++++++++++++-- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index d8c7f7477b..6a52a03880 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent import org.matrix.android.sdk.test.fakes.FakeClock @@ -28,6 +29,7 @@ import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider private const val A_SESSION_ID = "session_id" private const val A_SENDER_ID = "sender_id" private const val AN_EVENT_ID = "event_id" +private const val A_ROOM_ID = "room_id" internal class LiveLocationAggregationProcessorTest { @@ -50,7 +52,7 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = event, content = beaconInfo, - roomId = "", + roomId = A_ROOM_ID, isLocalEcho = true ) @@ -67,14 +69,14 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = eventNoSenderId, content = beaconInfo, - roomId = "", + roomId = A_ROOM_ID, isLocalEcho = false ) val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconInfo( realm = fakeRealm.instance, event = eventEmptySenderId, content = beaconInfo, - roomId = "", + roomId = A_ROOM_ID, isLocalEcho = false ) @@ -82,6 +84,55 @@ internal class LiveLocationAggregationProcessorTest { resultEmptySenderId shouldBeEqualTo false } + @Test + fun `given beacon info when no target eventId is found then it is ignored`() { + val unsignedDataWithNoEventId = UnsignedData( + age = 123 + ) + val unsignedDataWithEmptyEventId = UnsignedData( + age = 123, + replacesState = "" + ) + val eventWithNoEventId = Event(senderId = A_SENDER_ID, unsignedData = unsignedDataWithNoEventId) + val eventWithEmptyEventId = Event(senderId = A_SENDER_ID, eventId = "", unsignedData = unsignedDataWithEmptyEventId) + val beaconInfoLive = MessageBeaconInfoContent(isLive = true) + val beaconInfoNotLive = MessageBeaconInfoContent(isLive = false) + + val resultLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithNoEventId, + content = beaconInfoLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + val resultLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithEmptyEventId, + content = beaconInfoLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + val resultNotLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithNoEventId, + content = beaconInfoNotLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + val resultNotLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithEmptyEventId, + content = beaconInfoNotLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + resultLiveNoEventId shouldBeEqualTo false + resultLiveEmptyEventId shouldBeEqualTo false + resultNotLiveNoEventId shouldBeEqualTo false + resultNotLiveEmptyEventId shouldBeEqualTo false + } + @Test fun `given beacon location data when it is local echo then it is ignored`() { val event = Event(senderId = A_SENDER_ID) @@ -91,14 +142,40 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = event, content = beaconLocationData, - roomId = "", - relatedEventId = "", + roomId = A_ROOM_ID, + relatedEventId = AN_EVENT_ID, isLocalEcho = true ) result shouldBeEqualTo false } + @Test + fun `given beacon location data when relatedEventId is null or empty then it is ignored`() { + val event = Event(senderId = A_SENDER_ID) + val beaconLocationData = MessageBeaconLocationDataContent() + + val resultNoRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = null, + isLocalEcho = false + ) + val resultEmptyRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = "", + isLocalEcho = false + ) + + resultNoRelatedEventId shouldBeEqualTo false + resultEmptyRelatedEventId shouldBeEqualTo false + } + @Test fun `given beacon location data and event when senderId is null or empty then it is ignored`() { val eventNoSenderId = Event(eventId = AN_EVENT_ID) @@ -110,7 +187,7 @@ internal class LiveLocationAggregationProcessorTest { event = eventNoSenderId, content = beaconLocationData, roomId = "", - relatedEventId = "", + relatedEventId = AN_EVENT_ID, isLocalEcho = false ) val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconLocationData( @@ -118,7 +195,7 @@ internal class LiveLocationAggregationProcessorTest { event = eventEmptySenderId, content = beaconLocationData, roomId = "", - relatedEventId = "", + relatedEventId = AN_EVENT_ID, isLocalEcho = false ) From 6386c1603f402dfc2dc4010b603e6efd83a5104e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 16:56:29 +0200 Subject: [PATCH 4/8] Adding tests on beacon info aggregation --- ...cationShareAggregatedSummaryEntityQuery.kt | 1 + .../LiveLocationAggregationProcessorTest.kt | 120 ++++++++++++++++++ .../poll/PollAggregationProcessorTest.kt | 15 +-- .../android/sdk/test/fakes/FakeRealm.kt | 53 +++++++- .../sdk/test/fakes/FakeWorkManagerProvider.kt | 10 ++ 5 files changed, 185 insertions(+), 14 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt index 7dfeb6884a..6bcd737474 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt @@ -84,6 +84,7 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveIn .equalTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) .notEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, ignoredEventId) .findAll() + .toList() } /** diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index 6a52a03880..1638e4b4b4 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -16,26 +16,37 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation +import androidx.work.OneTimeWorkRequest +import io.mockk.verify import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent +import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity +import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields import org.matrix.android.sdk.test.fakes.FakeClock import org.matrix.android.sdk.test.fakes.FakeRealm import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider +import org.matrix.android.sdk.test.fakes.givenEqualTo +import org.matrix.android.sdk.test.fakes.givenFindAll +import org.matrix.android.sdk.test.fakes.givenFindFirst +import org.matrix.android.sdk.test.fakes.givenNotEqualTo private const val A_SESSION_ID = "session_id" private const val A_SENDER_ID = "sender_id" private const val AN_EVENT_ID = "event_id" private const val A_ROOM_ID = "room_id" +private const val A_TIMESTAMP = 1654689143L +private const val A_TIMEOUT_MILLIS = 15 * 60 * 1000L internal class LiveLocationAggregationProcessorTest { private val fakeWorkManagerProvider = FakeWorkManagerProvider() private val fakeClock = FakeClock() private val fakeRealm = FakeRealm() + private val fakeQuery = fakeRealm.givenWhere() private val liveLocationAggregationProcessor = LiveLocationAggregationProcessor( sessionId = A_SESSION_ID, @@ -133,6 +144,85 @@ internal class LiveLocationAggregationProcessorTest { resultNotLiveEmptyEventId shouldBeEqualTo false } + @Test + fun `given beacon info and existing entity when beacon content is correct and active then it is aggregated`() { + val event = Event( + senderId = A_SENDER_ID, + eventId = AN_EVENT_ID + ) + val beaconInfo = MessageBeaconInfoContent( + isLive = true, + unstableTimestampMillis = A_TIMESTAMP, + timeout = A_TIMEOUT_MILLIS + ) + fakeClock.givenEpoch(A_TIMESTAMP + 5000) + val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() + val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = event, + content = beaconInfo, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo true + aggregatedEntity.eventId shouldBeEqualTo AN_EVENT_ID + aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID + aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID + aggregatedEntity.isActive shouldBeEqualTo true + aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS + aggregatedEntity.lastLocationContent shouldBeEqualTo null + previousEntities.forEach { entity -> + entity.isActive shouldBeEqualTo false + } + val workManager = fakeWorkManagerProvider.instance.workManager + verify { workManager.enqueueUniqueWork(any(), any(), any()) } + } + + @Test + fun `given beacon info and existing entity when beacon content is correct and inactive then it is aggregated`() { + val unsignedData = UnsignedData( + age = 123, + replacesState = AN_EVENT_ID + ) + val event = Event( + senderId = A_SENDER_ID, + eventId = "", + unsignedData = unsignedData + ) + val beaconInfo = MessageBeaconInfoContent( + isLive = false, + unstableTimestampMillis = A_TIMESTAMP, + timeout = A_TIMEOUT_MILLIS + ) + fakeClock.givenEpoch(A_TIMESTAMP + 5000) + val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() + val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = event, + content = beaconInfo, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo true + aggregatedEntity.eventId shouldBeEqualTo AN_EVENT_ID + aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID + aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID + aggregatedEntity.isActive shouldBeEqualTo false + aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS + aggregatedEntity.lastLocationContent shouldBeEqualTo null + previousEntities.forEach { entity -> + entity.isActive shouldBeEqualTo false + } + val workManager = fakeWorkManagerProvider.instance.workManager + verify { workManager.cancelUniqueWork(any()) } + } + @Test fun `given beacon location data when it is local echo then it is ignored`() { val event = Event(senderId = A_SENDER_ID) @@ -202,4 +292,34 @@ internal class LiveLocationAggregationProcessorTest { resultNoSenderId shouldBeEqualTo false resultEmptySenderId shouldBeEqualTo false } + + private fun mockLiveLocationShareAggregatedSummaryEntityForEvent(): LiveLocationShareAggregatedSummaryEntity { + val result = LiveLocationShareAggregatedSummaryEntity( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID + ) + fakeQuery + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) + .givenFindFirst(result) + return result + } + + private fun mockPreviousLiveLocationShareAggregatedSummaryEntities(): List { + val results = listOf( + LiveLocationShareAggregatedSummaryEntity( + eventId = "", + roomId = A_ROOM_ID, + userId = A_SENDER_ID, + isActive = true + ) + ) + fakeQuery + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) + .givenNotEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.USER_ID, A_SENDER_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) + .givenFindAll(results) + return results + } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt index 837bbeea26..3044ca5d43 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt @@ -19,8 +19,6 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll import io.mockk.every import io.mockk.mockk import io.realm.RealmList -import io.realm.RealmModel -import io.realm.RealmQuery import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeTrue import org.junit.Before @@ -46,6 +44,8 @@ import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_TIMELINE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_USER_ID_1 import org.matrix.android.sdk.test.fakes.FakeRealm +import org.matrix.android.sdk.test.fakes.givenEqualTo +import org.matrix.android.sdk.test.fakes.givenFindFirst class PollAggregationProcessorTest { @@ -135,14 +135,11 @@ class PollAggregationProcessorTest { pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse() } - private inline fun RealmQuery.givenEqualTo(fieldName: String, value: String, result: RealmQuery) { - every { equalTo(fieldName, value) } returns result - } - private fun mockEventAnnotationsSummaryEntity() { - val queryResult = realm.givenWhereReturns(result = EventAnnotationsSummaryEntity()) - queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!, queryResult) - queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!, queryResult) + realm.givenWhere() + .givenFindFirst(EventAnnotationsSummaryEntity()) + .givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!) + .givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!) } private fun mockRoom( diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt index c07f8e1873..1697921a8d 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt @@ -21,16 +21,59 @@ import io.mockk.mockk import io.realm.Realm import io.realm.RealmModel import io.realm.RealmQuery +import io.realm.RealmResults import io.realm.kotlin.where internal class FakeRealm { val instance = mockk(relaxed = true) - inline fun givenWhereReturns(result: T?): RealmQuery { - val queryResult = mockk>() - every { queryResult.findFirst() } returns result - every { instance.where() } returns queryResult - return queryResult + inline fun givenWhere(): RealmQuery { + val query = mockk>() + every { instance.where() } returns query + return query } } + +inline fun RealmQuery.givenFindFirst( + result: T? +): RealmQuery { + every { findFirst() } returns result + return this +} + +inline fun RealmQuery.givenFindAll( + result: List +): RealmQuery { + val realmResults = mockk>() + result.forEachIndexed { index, t -> + every { realmResults[index] } returns t + } + every { realmResults.size } returns result.size + every { findAll() } returns realmResults + return this +} + +inline fun RealmQuery.givenEqualTo( + fieldName: String, + value: String +): RealmQuery { + every { equalTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenEqualTo( + fieldName: String, + value: Boolean +): RealmQuery { + every { equalTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenNotEqualTo( + fieldName: String, + value: String +): RealmQuery { + every { notEqualTo(fieldName, value) } returns this + return this +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt index 9ba072d35c..b6b435f531 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt @@ -16,10 +16,20 @@ package org.matrix.android.sdk.test.fakes +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.internal.di.WorkManagerProvider internal class FakeWorkManagerProvider { val instance = mockk() + + init { + val workManager = mockk() + every { workManager.enqueueUniqueWork(any(), any(), any()) } returns mockk() + every { workManager.cancelUniqueWork(any()) } returns mockk() + every { instance.workManager } returns workManager + } } From b9b1e2b3973e2a1d77218d5151cbadbc84714965 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 17:43:10 +0200 Subject: [PATCH 5/8] Adding tests on location data aggregation --- .../LiveLocationAggregationProcessor.kt | 7 +- .../LiveLocationAggregationProcessorTest.kt | 68 ++++++++++++++++++- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt index a254552bb3..921749122b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt @@ -151,12 +151,13 @@ internal class LiveLocationAggregationProcessor @Inject constructor( ?.getBestTimestampMillis() ?: 0 - if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) { + return if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) { Timber.d("updating last location of the summary of id=$relatedEventId") aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent()) + true + } else { + false } - - return true } private fun deactivateAllPreviousBeacons(realm: Realm, roomId: String, userId: String, currentEventId: String) { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index 1638e4b4b4..b6e5ea8479 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -22,8 +22,12 @@ import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.UnsignedData +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.LocationInfo import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent +import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields import org.matrix.android.sdk.test.fakes.FakeClock @@ -40,6 +44,10 @@ private const val AN_EVENT_ID = "event_id" private const val A_ROOM_ID = "room_id" private const val A_TIMESTAMP = 1654689143L private const val A_TIMEOUT_MILLIS = 15 * 60 * 1000L +private const val A_LATITUDE = 40.05 +private const val A_LONGITUDE = 29.24 +private const val A_UNCERTAINTY = 30.0 +private const val A_GEO_URI = "geo:$A_LATITUDE,$A_LONGITUDE;$A_UNCERTAINTY" internal class LiveLocationAggregationProcessorTest { @@ -284,7 +292,7 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = eventEmptySenderId, content = beaconLocationData, - roomId = "", + roomId = A_ROOM_ID, relatedEventId = AN_EVENT_ID, isLocalEcho = false ) @@ -293,10 +301,64 @@ internal class LiveLocationAggregationProcessorTest { resultEmptySenderId shouldBeEqualTo false } - private fun mockLiveLocationShareAggregatedSummaryEntityForEvent(): LiveLocationShareAggregatedSummaryEntity { + @Test + fun `given beacon location data when location is less recent than the saved one then it is ignored`() { + val event = Event(eventId = AN_EVENT_ID, senderId = A_SENDER_ID) + val beaconLocationData = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP - 60_000 + ) + val lastBeaconLocationContent = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP + ) + mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = AN_EVENT_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo false + } + + @Test + fun `given beacon location data when location is more recent than the saved one then it is aggregated`() { + val event = Event(eventId = AN_EVENT_ID, senderId = A_SENDER_ID) + val locationInfo = LocationInfo(geoUri = A_GEO_URI) + val beaconLocationData = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP, + unstableLocationInfo = locationInfo + ) + val lastBeaconLocationContent = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP - 60_000 + ) + val entity = mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = AN_EVENT_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo true + val savedLocationData = ContentMapper.map(entity.lastLocationContent).toModel() + savedLocationData?.getBestTimestampMillis() shouldBeEqualTo A_TIMESTAMP + savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI + } + + private fun mockLiveLocationShareAggregatedSummaryEntityForEvent( + lastBeaconLocationContent: MessageBeaconLocationDataContent? = null + ): LiveLocationShareAggregatedSummaryEntity { val result = LiveLocationShareAggregatedSummaryEntity( eventId = AN_EVENT_ID, - roomId = A_ROOM_ID + roomId = A_ROOM_ID, + lastLocationContent = ContentMapper.map(lastBeaconLocationContent?.toContent()) ) fakeQuery .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) From e3981f42e99ecfde92f114ab927308abe8366e5e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 10 Jun 2022 15:17:26 +0200 Subject: [PATCH 6/8] Introducing FakeWorkManager --- .../LiveLocationAggregationProcessorTest.kt | 16 ++++--- .../android/sdk/test/fakes/FakeWorkManager.kt | 45 +++++++++++++++++++ .../sdk/test/fakes/FakeWorkManagerProvider.kt | 15 +++---- 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index b6e5ea8479..646a2194e1 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -16,8 +16,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation -import androidx.work.OneTimeWorkRequest -import io.mockk.verify +import androidx.work.ExistingWorkPolicy import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event @@ -164,6 +163,7 @@ internal class LiveLocationAggregationProcessorTest { timeout = A_TIMEOUT_MILLIS ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) + fakeWorkManagerProvider.fakeWorkManager.expectEnqueueUniqueWork() val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() @@ -185,8 +185,10 @@ internal class LiveLocationAggregationProcessorTest { previousEntities.forEach { entity -> entity.isActive shouldBeEqualTo false } - val workManager = fakeWorkManagerProvider.instance.workManager - verify { workManager.enqueueUniqueWork(any(), any(), any()) } + fakeWorkManagerProvider.fakeWorkManager.verifyEnqueueUniqueWork( + workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = AN_EVENT_ID, roomId = A_ROOM_ID), + policy = ExistingWorkPolicy.REPLACE + ) } @Test @@ -206,6 +208,7 @@ internal class LiveLocationAggregationProcessorTest { timeout = A_TIMEOUT_MILLIS ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) + fakeWorkManagerProvider.fakeWorkManager.expectCancelUniqueWork() val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() @@ -227,8 +230,9 @@ internal class LiveLocationAggregationProcessorTest { previousEntities.forEach { entity -> entity.isActive shouldBeEqualTo false } - val workManager = fakeWorkManagerProvider.instance.workManager - verify { workManager.cancelUniqueWork(any()) } + fakeWorkManagerProvider.fakeWorkManager.verifyCancelUniqueWork( + workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + ) } @Test diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt new file mode 100644 index 0000000000..b29d015a43 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.test.fakes + +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify + +class FakeWorkManager { + + val instance = mockk() + + fun expectEnqueueUniqueWork() { + every { instance.enqueueUniqueWork(any(), any(), any()) } returns mockk() + } + + fun verifyEnqueueUniqueWork(workName: String, policy: ExistingWorkPolicy) { + verify { instance.enqueueUniqueWork(workName, policy, any()) } + } + + fun expectCancelUniqueWork() { + every { instance.cancelUniqueWork(any()) } returns mockk() + } + + fun verifyCancelUniqueWork(workName: String) { + verify { instance.cancelUniqueWork(workName) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt index b6b435f531..51ff24c01d 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt @@ -16,20 +16,15 @@ package org.matrix.android.sdk.test.fakes -import androidx.work.OneTimeWorkRequest -import androidx.work.WorkManager import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.internal.di.WorkManagerProvider -internal class FakeWorkManagerProvider { +internal class FakeWorkManagerProvider( + val fakeWorkManager: FakeWorkManager = FakeWorkManager(), +) { - val instance = mockk() - - init { - val workManager = mockk() - every { workManager.enqueueUniqueWork(any(), any(), any()) } returns mockk() - every { workManager.cancelUniqueWork(any()) } returns mockk() - every { instance.workManager } returns workManager + val instance = mockk().also { + every { it.workManager } returns fakeWorkManager.instance } } From 2c961793832e37884e418f3ef3f2c457f3084d2e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 10 Jun 2022 16:41:44 +0200 Subject: [PATCH 7/8] Renaming helpers to clarify purpose --- .../LiveLocationAggregationProcessorTest.kt | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index 646a2194e1..dd4ece6af3 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -164,8 +164,17 @@ internal class LiveLocationAggregationProcessorTest { ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) fakeWorkManagerProvider.fakeWorkManager.expectEnqueueUniqueWork() - val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() - val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + val aggregatedEntity = givenLastSummaryQueryReturns(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + val previousEntities = givenActiveSummaryListQueryReturns( + listOf( + LiveLocationShareAggregatedSummaryEntity( + eventId = "${AN_EVENT_ID}1", + roomId = A_ROOM_ID, + userId = A_SENDER_ID, + isActive = true + ) + ) + ) val result = liveLocationAggregationProcessor.handleBeaconInfo( realm = fakeRealm.instance, @@ -209,8 +218,18 @@ internal class LiveLocationAggregationProcessorTest { ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) fakeWorkManagerProvider.fakeWorkManager.expectCancelUniqueWork() - val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() - val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + val aggregatedEntity = givenLastSummaryQueryReturns(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + val previousEntities = givenActiveSummaryListQueryReturns( + listOf( + LiveLocationShareAggregatedSummaryEntity( + eventId = "${AN_EVENT_ID}1", + roomId = A_ROOM_ID, + userId = A_SENDER_ID, + isActive = true + ) + ) + + ) val result = liveLocationAggregationProcessor.handleBeaconInfo( realm = fakeRealm.instance, @@ -314,7 +333,11 @@ internal class LiveLocationAggregationProcessorTest { val lastBeaconLocationContent = MessageBeaconLocationDataContent( unstableTimestampMillis = A_TIMESTAMP ) - mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + givenLastSummaryQueryReturns( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + beaconLocationContent = lastBeaconLocationContent + ) val result = liveLocationAggregationProcessor.handleBeaconLocationData( realm = fakeRealm.instance, @@ -339,7 +362,11 @@ internal class LiveLocationAggregationProcessorTest { val lastBeaconLocationContent = MessageBeaconLocationDataContent( unstableTimestampMillis = A_TIMESTAMP - 60_000 ) - val entity = mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + val entity = givenLastSummaryQueryReturns( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + beaconLocationContent = lastBeaconLocationContent + ) val result = liveLocationAggregationProcessor.handleBeaconLocationData( realm = fakeRealm.instance, @@ -356,36 +383,32 @@ internal class LiveLocationAggregationProcessorTest { savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI } - private fun mockLiveLocationShareAggregatedSummaryEntityForEvent( - lastBeaconLocationContent: MessageBeaconLocationDataContent? = null + private fun givenLastSummaryQueryReturns( + eventId: String, + roomId: String, + beaconLocationContent: MessageBeaconLocationDataContent? = null ): LiveLocationShareAggregatedSummaryEntity { val result = LiveLocationShareAggregatedSummaryEntity( - eventId = AN_EVENT_ID, - roomId = A_ROOM_ID, - lastLocationContent = ContentMapper.map(lastBeaconLocationContent?.toContent()) + eventId = eventId, + roomId = roomId, + lastLocationContent = ContentMapper.map(beaconLocationContent?.toContent()) ) fakeQuery - .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) - .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, roomId) .givenFindFirst(result) return result } - private fun mockPreviousLiveLocationShareAggregatedSummaryEntities(): List { - val results = listOf( - LiveLocationShareAggregatedSummaryEntity( - eventId = "", - roomId = A_ROOM_ID, - userId = A_SENDER_ID, - isActive = true - ) - ) + private fun givenActiveSummaryListQueryReturns( + summaryList: List + ): List { fakeQuery .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) .givenNotEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.USER_ID, A_SENDER_ID) .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) - .givenFindAll(results) - return results + .givenFindAll(summaryList) + return summaryList } } From ac4b33647d9828e82a7695e7626b6ed3f75a1ab3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 10:09:09 +0200 Subject: [PATCH 8/8] Mutualizing some similar tests with different parameters --- .../LiveLocationAggregationProcessorTest.kt | 219 +++++++++--------- 1 file changed, 105 insertions(+), 114 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index dd4ece6af3..e6d63f5e5e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -77,78 +77,71 @@ internal class LiveLocationAggregationProcessorTest { result shouldBeEqualTo false } - @Test - fun `given beacon info and event when senderId is null or empty then it is ignored`() { - val eventNoSenderId = Event(eventId = AN_EVENT_ID) - val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") - val beaconInfo = MessageBeaconInfoContent() - - val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventNoSenderId, - content = beaconInfo, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventEmptySenderId, - content = beaconInfo, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - - resultNoSenderId shouldBeEqualTo false - resultEmptySenderId shouldBeEqualTo false - } + private data class IgnoredBeaconInfoEvent( + val event: Event, + val beaconInfo: MessageBeaconInfoContent + ) @Test - fun `given beacon info when no target eventId is found then it is ignored`() { - val unsignedDataWithNoEventId = UnsignedData( - age = 123 - ) - val unsignedDataWithEmptyEventId = UnsignedData( - age = 123, - replacesState = "" - ) - val eventWithNoEventId = Event(senderId = A_SENDER_ID, unsignedData = unsignedDataWithNoEventId) - val eventWithEmptyEventId = Event(senderId = A_SENDER_ID, eventId = "", unsignedData = unsignedDataWithEmptyEventId) - val beaconInfoLive = MessageBeaconInfoContent(isLive = true) - val beaconInfoNotLive = MessageBeaconInfoContent(isLive = false) - - val resultLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithNoEventId, - content = beaconInfoLive, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithEmptyEventId, - content = beaconInfoLive, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultNotLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithNoEventId, - content = beaconInfoNotLive, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultNotLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithEmptyEventId, - content = beaconInfoNotLive, - roomId = A_ROOM_ID, - isLocalEcho = false + fun `given beacon info and event when some values are missing then it is ignored`() { + val ignoredInfoEvents = listOf( + // missing senderId + IgnoredBeaconInfoEvent( + event = Event(eventId = AN_EVENT_ID, senderId = null), + beaconInfo = MessageBeaconInfoContent() + ), + // empty senderId + IgnoredBeaconInfoEvent( + event = Event(eventId = AN_EVENT_ID, senderId = ""), + beaconInfo = MessageBeaconInfoContent() + ), + // beacon is live and no eventId + IgnoredBeaconInfoEvent( + event = Event(eventId = null, senderId = A_SENDER_ID), + beaconInfo = MessageBeaconInfoContent(isLive = true) + ), + // beacon is live and eventId is empty + IgnoredBeaconInfoEvent( + event = Event(eventId = "", senderId = A_SENDER_ID), + beaconInfo = MessageBeaconInfoContent(isLive = true) + ), + // beacon is not live and replaced event id is null + IgnoredBeaconInfoEvent( + event = Event( + eventId = AN_EVENT_ID, + senderId = A_SENDER_ID, + unsignedData = UnsignedData( + age = 123, + replacesState = null + ) + ), + beaconInfo = MessageBeaconInfoContent(isLive = false) + ), + // beacon is not live and replaced event id is empty + IgnoredBeaconInfoEvent( + event = Event( + eventId = AN_EVENT_ID, + senderId = A_SENDER_ID, + unsignedData = UnsignedData( + age = 123, + replacesState = "" + ) + ), + beaconInfo = MessageBeaconInfoContent(isLive = false) + ), ) - resultLiveNoEventId shouldBeEqualTo false - resultLiveEmptyEventId shouldBeEqualTo false - resultNotLiveNoEventId shouldBeEqualTo false - resultNotLiveEmptyEventId shouldBeEqualTo false + ignoredInfoEvents.forEach { + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = it.event, + content = it.beaconInfo, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo false + } } @Test @@ -271,57 +264,55 @@ internal class LiveLocationAggregationProcessorTest { result shouldBeEqualTo false } + private data class IgnoredBeaconLocationDataEvent( + val event: Event, + val beaconLocationData: MessageBeaconLocationDataContent + ) + + @Test + fun `given event and beacon location data when some values are missing then it is ignored`() { + val ignoredLocationDataEvents = listOf( + // missing sender id + IgnoredBeaconLocationDataEvent( + event = Event(eventId = AN_EVENT_ID), + beaconLocationData = MessageBeaconLocationDataContent() + ), + // empty sender id + IgnoredBeaconLocationDataEvent( + event = Event(eventId = AN_EVENT_ID, senderId = ""), + beaconLocationData = MessageBeaconLocationDataContent() + ), + ) + + ignoredLocationDataEvents.forEach { + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = it.event, + content = it.beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = "", + isLocalEcho = false + ) + result shouldBeEqualTo false + } + } + @Test fun `given beacon location data when relatedEventId is null or empty then it is ignored`() { val event = Event(senderId = A_SENDER_ID) val beaconLocationData = MessageBeaconLocationDataContent() - val resultNoRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = event, - content = beaconLocationData, - roomId = A_ROOM_ID, - relatedEventId = null, - isLocalEcho = false - ) - val resultEmptyRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = event, - content = beaconLocationData, - roomId = A_ROOM_ID, - relatedEventId = "", - isLocalEcho = false - ) - - resultNoRelatedEventId shouldBeEqualTo false - resultEmptyRelatedEventId shouldBeEqualTo false - } - - @Test - fun `given beacon location data and event when senderId is null or empty then it is ignored`() { - val eventNoSenderId = Event(eventId = AN_EVENT_ID) - val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") - val beaconLocationData = MessageBeaconLocationDataContent() - - val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = eventNoSenderId, - content = beaconLocationData, - roomId = "", - relatedEventId = AN_EVENT_ID, - isLocalEcho = false - ) - val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = eventEmptySenderId, - content = beaconLocationData, - roomId = A_ROOM_ID, - relatedEventId = AN_EVENT_ID, - isLocalEcho = false - ) - - resultNoSenderId shouldBeEqualTo false - resultEmptySenderId shouldBeEqualTo false + listOf(null, "").forEach { + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = it, + isLocalEcho = false + ) + result shouldBeEqualTo false + } } @Test