Merge pull request #6267 from vector-im/feature/mna/6155-tests-lls-aggregation

Adding unit tests for live location sharing aggregation code (PSF-1063)
This commit is contained in:
Maxime NATUREL 2022-06-20 09:19:38 +02:00 committed by GitHub
commit 539d134b77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 586 additions and 22 deletions

1
changelog.d/6155.misc Normal file
View File

@ -0,0 +1 @@
Add unit tests for LiveLocationAggregationProcessor code

View File

@ -84,6 +84,7 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveIn
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) .equalTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true)
.notEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, ignoredEventId) .notEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, ignoredEventId)
.findAll() .findAll()
.toList()
} }
/** /**

View File

@ -36,16 +36,22 @@ import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
// TODO add unit tests /**
* Aggregates all live location sharing related events in local database.
*/
internal class LiveLocationAggregationProcessor @Inject constructor( internal class LiveLocationAggregationProcessor @Inject constructor(
@SessionId private val sessionId: String, @SessionId private val sessionId: String,
private val workManagerProvider: WorkManagerProvider, private val workManagerProvider: WorkManagerProvider,
private val clock: Clock, 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) { if (event.senderId.isNullOrEmpty() || isLocalEcho) {
return return false
} }
val isLive = content.isLive.orTrue() val isLive = content.isLive.orTrue()
@ -58,7 +64,7 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
if (targetEventId.isNullOrEmpty()) { if (targetEventId.isNullOrEmpty()) {
Timber.w("no target event id found for the beacon content") Timber.w("no target event id found for the beacon content")
return return false
} }
val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate( val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
@ -83,6 +89,8 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
} else { } else {
cancelDeactivationAfterTimeout(targetEventId, roomId) cancelDeactivationAfterTimeout(targetEventId, roomId)
} }
return true
} }
private fun scheduleDeactivationAfterTimeout(eventId: String, roomId: String, endOfLiveTimestampMillis: Long?) { private fun scheduleDeactivationAfterTimeout(eventId: String, roomId: String, endOfLiveTimestampMillis: Long?) {
@ -110,6 +118,10 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
workManagerProvider.workManager.cancelUniqueWork(workName) workManagerProvider.workManager.cancelUniqueWork(workName)
} }
/**
* Handle the content of a beacon location data.
* @return true if it has been processed, false if ignored.
*/
fun handleBeaconLocationData( fun handleBeaconLocationData(
realm: Realm, realm: Realm,
event: Event, event: Event,
@ -117,14 +129,14 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
roomId: String, roomId: String,
relatedEventId: String?, relatedEventId: String?,
isLocalEcho: Boolean isLocalEcho: Boolean
) { ): Boolean {
if (event.senderId.isNullOrEmpty() || isLocalEcho) { if (event.senderId.isNullOrEmpty() || isLocalEcho) {
return return false
} }
if (relatedEventId.isNullOrEmpty()) { if (relatedEventId.isNullOrEmpty()) {
Timber.w("no related event id found for the live location content") Timber.w("no related event id found for the live location content")
return return false
} }
val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate( val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
@ -139,9 +151,12 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
?.getBestTimestampMillis() ?.getBestTimestampMillis()
?: 0 ?: 0
if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) { return if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
Timber.d("updating last location of the summary of id=$relatedEventId") Timber.d("updating last location of the summary of id=$relatedEventId")
aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent()) aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
true
} else {
false
} }
} }

View File

@ -0,0 +1,405 @@
/*
* 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 androidx.work.ExistingWorkPolicy
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
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
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 {
private val fakeWorkManagerProvider = FakeWorkManagerProvider()
private val fakeClock = FakeClock()
private val fakeRealm = FakeRealm()
private val fakeQuery = fakeRealm.givenWhere<LiveLocationShareAggregatedSummaryEntity>()
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 = A_ROOM_ID,
isLocalEcho = true
)
result shouldBeEqualTo false
}
private data class IgnoredBeaconInfoEvent(
val event: Event,
val beaconInfo: MessageBeaconInfoContent
)
@Test
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)
),
)
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
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)
fakeWorkManagerProvider.fakeWorkManager.expectEnqueueUniqueWork()
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,
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
}
fakeWorkManagerProvider.fakeWorkManager.verifyEnqueueUniqueWork(
workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = AN_EVENT_ID, roomId = A_ROOM_ID),
policy = ExistingWorkPolicy.REPLACE
)
}
@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)
fakeWorkManagerProvider.fakeWorkManager.expectCancelUniqueWork()
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,
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
}
fakeWorkManagerProvider.fakeWorkManager.verifyCancelUniqueWork(
workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = AN_EVENT_ID, roomId = A_ROOM_ID)
)
}
@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 = A_ROOM_ID,
relatedEventId = AN_EVENT_ID,
isLocalEcho = true
)
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()
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
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
)
givenLastSummaryQueryReturns(
eventId = AN_EVENT_ID,
roomId = A_ROOM_ID,
beaconLocationContent = 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 = givenLastSummaryQueryReturns(
eventId = AN_EVENT_ID,
roomId = A_ROOM_ID,
beaconLocationContent = 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<MessageBeaconLocationDataContent>()
savedLocationData?.getBestTimestampMillis() shouldBeEqualTo A_TIMESTAMP
savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI
}
private fun givenLastSummaryQueryReturns(
eventId: String,
roomId: String,
beaconLocationContent: MessageBeaconLocationDataContent? = null
): LiveLocationShareAggregatedSummaryEntity {
val result = LiveLocationShareAggregatedSummaryEntity(
eventId = eventId,
roomId = roomId,
lastLocationContent = ContentMapper.map(beaconLocationContent?.toContent())
)
fakeQuery
.givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId)
.givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, roomId)
.givenFindFirst(result)
return result
}
private fun givenActiveSummaryListQueryReturns(
summaryList: List<LiveLocationShareAggregatedSummaryEntity>
): List<LiveLocationShareAggregatedSummaryEntity> {
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(summaryList)
return summaryList
}
}

View File

@ -19,8 +19,6 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmModel
import io.realm.RealmQuery
import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldBeTrue import org.amshove.kluent.shouldBeTrue
import org.junit.Before 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_TIMELINE_EVENT
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_USER_ID_1 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.FakeRealm
import org.matrix.android.sdk.test.fakes.givenEqualTo
import org.matrix.android.sdk.test.fakes.givenFindFirst
class PollAggregationProcessorTest { class PollAggregationProcessorTest {
@ -135,14 +135,11 @@ class PollAggregationProcessorTest {
pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse() pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse()
} }
private inline fun <reified T : RealmModel> RealmQuery<T>.givenEqualTo(fieldName: String, value: String, result: RealmQuery<T>) {
every { equalTo(fieldName, value) } returns result
}
private fun mockEventAnnotationsSummaryEntity() { private fun mockEventAnnotationsSummaryEntity() {
val queryResult = realm.givenWhereReturns(result = EventAnnotationsSummaryEntity()) realm.givenWhere<EventAnnotationsSummaryEntity>()
queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!, queryResult) .givenFindFirst(EventAnnotationsSummaryEntity())
queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!, queryResult) .givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!)
.givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!)
} }
private fun mockRoom( private fun mockRoom(

View File

@ -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
}
}

View File

@ -21,16 +21,59 @@ import io.mockk.mockk
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel import io.realm.RealmModel
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.RealmResults
import io.realm.kotlin.where import io.realm.kotlin.where
internal class FakeRealm { internal class FakeRealm {
val instance = mockk<Realm>(relaxed = true) val instance = mockk<Realm>(relaxed = true)
inline fun <reified T : RealmModel> givenWhereReturns(result: T?): RealmQuery<T> { inline fun <reified T : RealmModel> givenWhere(): RealmQuery<T> {
val queryResult = mockk<RealmQuery<T>>() val query = mockk<RealmQuery<T>>()
every { queryResult.findFirst() } returns result every { instance.where<T>() } returns query
every { instance.where<T>() } returns queryResult return query
return queryResult
} }
} }
inline fun <reified T : RealmModel> RealmQuery<T>.givenFindFirst(
result: T?
): RealmQuery<T> {
every { findFirst() } returns result
return this
}
inline fun <reified T : RealmModel> RealmQuery<T>.givenFindAll(
result: List<T>
): RealmQuery<T> {
val realmResults = mockk<RealmResults<T>>()
result.forEachIndexed { index, t ->
every { realmResults[index] } returns t
}
every { realmResults.size } returns result.size
every { findAll() } returns realmResults
return this
}
inline fun <reified T : RealmModel> RealmQuery<T>.givenEqualTo(
fieldName: String,
value: String
): RealmQuery<T> {
every { equalTo(fieldName, value) } returns this
return this
}
inline fun <reified T : RealmModel> RealmQuery<T>.givenEqualTo(
fieldName: String,
value: Boolean
): RealmQuery<T> {
every { equalTo(fieldName, value) } returns this
return this
}
inline fun <reified T : RealmModel> RealmQuery<T>.givenNotEqualTo(
fieldName: String,
value: String
): RealmQuery<T> {
every { notEqualTo(fieldName, value) } returns this
return this
}

View File

@ -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<WorkManager>()
fun expectEnqueueUniqueWork() {
every { instance.enqueueUniqueWork(any(), any(), any<OneTimeWorkRequest>()) } returns mockk()
}
fun verifyEnqueueUniqueWork(workName: String, policy: ExistingWorkPolicy) {
verify { instance.enqueueUniqueWork(workName, policy, any<OneTimeWorkRequest>()) }
}
fun expectCancelUniqueWork() {
every { instance.cancelUniqueWork(any()) } returns mockk()
}
fun verifyCancelUniqueWork(workName: String) {
verify { instance.cancelUniqueWork(workName) }
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.di.WorkManagerProvider
internal class FakeWorkManagerProvider(
val fakeWorkManager: FakeWorkManager = FakeWorkManager(),
) {
val instance = mockk<WorkManagerProvider>().also {
every { it.workManager } returns fakeWorkManager.instance
}
}