Add a periodic DB vacuuming task

This commit is contained in:
Jorge Martín
2025-12-05 14:19:36 +01:00
committed by Jorge Martin Espinosa
parent 9c72310cb4
commit 482d7e0648
22 changed files with 172 additions and 21 deletions

View File

@@ -12,7 +12,7 @@ import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.MatrixClient
import timber.log.Timber
interface VacuumStoresUseCase {
fun interface VacuumStoresUseCase {
suspend operator fun invoke()
}
@@ -21,7 +21,7 @@ class DefaultVacuumStoresUseCase(
private val matrixClient: MatrixClient,
) : VacuumStoresUseCase {
override suspend fun invoke() {
matrixClient.vacuumStores()
matrixClient.performDatabaseVacuum()
.onFailure { Timber.e(it, "Failed to vacuum stores") }
}
}

View File

@@ -17,6 +17,7 @@ import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
@@ -212,6 +213,23 @@ class DeveloperSettingsPresenterTest {
}
}
@Test
fun `present - VacuumStores action invokes the VacuumStoresUseCase`() = runTest {
var vacuumCalled = false
val presenter = createDeveloperSettingsPresenter(
vacuumStoresUseCase = VacuumStoresUseCase {
vacuumCalled = true
}
)
presenter.test {
val state = awaitItem()
assertThat(vacuumCalled).isFalse()
state.eventSink(DeveloperSettingsEvents.VacuumStores)
skipItems(1)
assertThat(vacuumCalled).isTrue()
}
}
private fun createDeveloperSettingsPresenter(
sessionId: SessionId = A_SESSION_ID,
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(
@@ -230,6 +248,7 @@ class DeveloperSettingsPresenterTest {
preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(),
buildMeta: BuildMeta = aBuildMeta(),
enterpriseService: EnterpriseService = FakeEnterpriseService(),
vacuumStoresUseCase: VacuumStoresUseCase = VacuumStoresUseCase {},
): DeveloperSettingsPresenter {
return DeveloperSettingsPresenter(
sessionId = sessionId,
@@ -240,6 +259,7 @@ class DeveloperSettingsPresenterTest {
appPreferencesStore = preferencesStore,
buildMeta = buildMeta,
enterpriseService = enterpriseService,
vacuumStoresUseCase = vacuumStoresUseCase,
)
}
}

View File

@@ -195,7 +195,7 @@ interface MatrixClient {
*/
suspend fun markRoomAsFullyRead(roomId: RoomId, eventId: EventId): Result<Unit>
suspend fun vacuumStores(): Result<Unit>
suspend fun performDatabaseVacuum(): Result<Unit>
}
/**

View File

@@ -34,6 +34,7 @@ dependencies {
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.network)
implementation(projects.libraries.preferences.api)
implementation(projects.libraries.workmanager.api)
implementation(projects.services.analytics.api)
implementation(projects.services.toolbox.api)
api(projects.libraries.matrix.api)
@@ -49,6 +50,7 @@ dependencies {
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.previewutils)
testImplementation(projects.libraries.sessionStorage.test)
testImplementation(projects.libraries.workmanager.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.services.toolbox.test)
}

View File

@@ -75,7 +75,10 @@ import io.element.android.libraries.matrix.impl.util.SessionPathsProvider
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
import io.element.android.libraries.matrix.impl.workmanager.PerformDatabaseVacuumWorkManagerRequest
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.collections.immutable.ImmutableList
@@ -133,6 +136,7 @@ class RustMatrixClient(
timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
private val featureFlagService: FeatureFlagService,
private val analyticsService: AnalyticsService,
private val workManagerScheduler: WorkManagerScheduler,
) : MatrixClient {
override val sessionId: UserId = UserId(innerClient.userId())
override val deviceId: DeviceId = DeviceId(innerClient.deviceId())
@@ -276,6 +280,9 @@ class RustMatrixClient(
// Force a refresh of the profile
getUserProfile()
}
// Schedule regular database vacuuming to ensure DB performance remains optimal
scheduleDatabaseVacuum()
}
override fun userIdServerName(): String {
@@ -726,8 +733,9 @@ class RustMatrixClient(
}
}
override suspend fun vacuumStores(): Result<Unit> = withContext(sessionDispatcher) {
override suspend fun performDatabaseVacuum(): Result<Unit> = withContext(sessionDispatcher) {
runCatchingExceptions {
Timber.d("Performing database vacuuming for session $sessionId...")
innerClient.optimizeStores()
}
}
@@ -756,6 +764,15 @@ class RustMatrixClient(
// Delete all the files for this session
sessionPathsProvider.provides(sessionId)?.deleteRecursively()
}
private fun scheduleDatabaseVacuum() {
// If there's already a periodic work request, do not schedule another one
if (workManagerScheduler.hasPendingWork(sessionId, WorkManagerRequestType.DB_VACUUM)) return
Timber.i("Scheduling periodic database vacuuming for session $sessionId")
val request = PerformDatabaseVacuumWorkManagerRequest(sessionId)
workManagerScheduler.submit(request)
}
}
private val defaultRoomCreationPowerLevels = PowerLevels(

View File

@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.impl.util.anonymizedTokens
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope
@@ -63,6 +64,7 @@ class RustMatrixClientFactory(
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
private val clientBuilderProvider: ClientBuilderProvider,
private val sqliteStoreBuilderProvider: SqliteStoreBuilderProvider,
private val workManagerScheduler: WorkManagerScheduler,
) {
private val sessionDelegate = RustClientSessionDelegate(sessionStore, appCoroutineScope, coroutineDispatchers)
@@ -116,6 +118,7 @@ class RustMatrixClientFactory(
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
featureFlagService = featureFlagService,
analyticsService = analyticsService,
workManagerScheduler = workManagerScheduler,
).also {
Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'")
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.workmanager
import androidx.work.Constraints
import androidx.work.Data
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkRequest
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.impl.workmanager.VacuumDatabaseWorker.Companion.SESSION_ID_PARAM
import io.element.android.libraries.workmanager.api.WorkManagerRequest
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
import io.element.android.libraries.workmanager.api.workManagerTag
import java.util.concurrent.TimeUnit
class PerformDatabaseVacuumWorkManagerRequest(
private val sessionId: SessionId,
) : WorkManagerRequest {
override fun build(): Result<List<WorkRequest>> {
val data = Data.Builder().putString(SESSION_ID_PARAM, sessionId.value).build()
val workRequest = PeriodicWorkRequest.Builder(
workerClass = VacuumDatabaseWorker::class,
// Run once a day
repeatInterval = 1,
repeatIntervalTimeUnit = TimeUnit.DAYS,
)
.addTag(workManagerTag(sessionId, WorkManagerRequestType.DB_VACUUM))
.setInputData(data)
// Only run when the device is idle to avoid impacting user experience
.setConstraints(Constraints.Builder().setRequiresDeviceIdle(true).build())
.build()
return Result.success(listOf(workRequest))
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.workmanager
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.SessionId
@AssistedInject
class VacuumDatabaseWorker(
@Assisted workerParams: WorkerParameters,
@ApplicationContext private val context: Context,
private val matrixClientProvider: MatrixClientProvider,
) : CoroutineWorker(context, workerParams) {
companion object {
const val SESSION_ID_PARAM = "session_id"
}
override suspend fun doWork(): Result {
val sessionId = inputData.getString(SESSION_ID_PARAM)?.let(::SessionId) ?: return Result.failure()
val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return Result.failure()
return client.performDatabaseVacuum()
.fold(
onSuccess = { Result.success() },
onFailure = { Result.failure() }
)
}
}

View File

@@ -19,8 +19,11 @@ import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
import io.element.android.libraries.sessionstorage.test.aSessionData
import io.element.android.libraries.workmanager.api.WorkManagerRequest
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -30,9 +33,14 @@ import java.io.File
class RustMatrixClientFactoryTest {
@Test
fun test() = runTest {
val sut = createRustMatrixClientFactory()
val scheduleVacuumLambda = lambdaRecorder<WorkManagerRequest, Unit> {}
val workManagerScheduler = FakeWorkManagerScheduler(submitLambda = scheduleVacuumLambda)
val sut = createRustMatrixClientFactory(workManagerScheduler = workManagerScheduler)
val result = sut.create(aSessionData())
assertThat(result.sessionId).isEqualTo(SessionId("@alice:server.org"))
scheduleVacuumLambda.assertions().isCalledOnce()
result.destroy()
}
}
@@ -43,6 +51,7 @@ fun TestScope.createRustMatrixClientFactory(
updateUserProfileResult = { _, _, _ -> },
),
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(),
) = RustMatrixClientFactory(
cacheDirectory = cacheDirectory,
appCoroutineScope = backgroundScope,
@@ -57,4 +66,5 @@ fun TestScope.createRustMatrixClientFactory(
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
clientBuilderProvider = clientBuilderProvider,
sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(),
workManagerScheduler = workManagerScheduler,
)

View File

@@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
import io.element.android.libraries.sessionstorage.test.aSessionData
import io.element.android.libraries.workmanager.test.FakeWorkManagerScheduler
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
import io.element.android.tests.testutils.lambda.lambdaRecorder
@@ -116,5 +117,6 @@ class RustMatrixClientTest {
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
featureFlagService = FakeFeatureFlagService(),
analyticsService = FakeAnalyticsService(),
workManagerScheduler = FakeWorkManagerScheduler(submitLambda = {}),
)
}

View File

@@ -104,6 +104,7 @@ class FakeMatrixClient(
private val getRecentEmojisLambda: () -> Result<List<String>> = { Result.success(emptyList()) },
private val addRecentEmojiLambda: (String) -> Result<Unit> = { Result.success(Unit) },
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
private val performDatabaseVacuumLambda: () -> Result<Unit> = { lambdaError() },
) : MatrixClient {
var setDisplayNameCalled: Boolean = false
private set
@@ -351,4 +352,8 @@ class FakeMatrixClient(
override suspend fun markRoomAsFullyRead(roomId: RoomId, eventId: EventId): Result<Unit> {
return markRoomAsFullyReadResult(roomId, eventId)
}
override suspend fun performDatabaseVacuum(): Result<Unit> {
return performDatabaseVacuumLambda()
}
}

View File

@@ -17,7 +17,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.push.api.push.NotificationEventRequest
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
import io.element.android.libraries.push.impl.workmanager.SyncNotificationWorkManagerRequest
import io.element.android.libraries.push.impl.workmanager.WorkerDataConverter
import io.element.android.libraries.push.impl.workmanager.SyncNotificationsWorkerDataConverter
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
import kotlinx.coroutines.CoroutineScope
@@ -50,7 +50,7 @@ class DefaultNotificationResolverQueue(
private val appCoroutineScope: CoroutineScope,
private val workManagerScheduler: WorkManagerScheduler,
private val featureFlagService: FeatureFlagService,
private val workerDataConverter: WorkerDataConverter,
private val workerDataConverter: SyncNotificationsWorkerDataConverter,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
) : NotificationResolverQueue {
companion object {

View File

@@ -49,7 +49,7 @@ class FetchNotificationsWorker(
private val workManagerScheduler: WorkManagerScheduler,
private val syncOnNotifiableEvent: SyncOnNotifiableEvent,
private val coroutineDispatchers: CoroutineDispatchers,
private val workerDataConverter: WorkerDataConverter,
private val workerDataConverter: SyncNotificationsWorkerDataConverter,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result = withContext(coroutineDispatchers.io) {

View File

@@ -26,7 +26,7 @@ import java.security.InvalidParameterException
class SyncNotificationWorkManagerRequest(
private val sessionId: SessionId,
private val notificationEventRequests: List<NotificationEventRequest>,
private val workerDataConverter: WorkerDataConverter,
private val workerDataConverter: SyncNotificationsWorkerDataConverter,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
) : WorkManagerRequest {
override fun build(): Result<List<WorkRequest>> {

View File

@@ -21,7 +21,7 @@ import io.element.android.libraries.push.api.push.NotificationEventRequest
import timber.log.Timber
@Inject
class WorkerDataConverter(
class SyncNotificationsWorkerDataConverter(
private val json: JsonProvider,
) {
fun serialize(notificationEventRequests: List<NotificationEventRequest>): Result<List<Data>> {

View File

@@ -47,7 +47,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableEven
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
import io.element.android.libraries.push.impl.test.DefaultTestPush
import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler
import io.element.android.libraries.push.impl.workmanager.WorkerDataConverter
import io.element.android.libraries.push.impl.workmanager.SyncNotificationsWorkerDataConverter
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.libraries.pushstore.api.UserPushStore
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
@@ -718,7 +718,7 @@ class DefaultPushHandlerTest {
appCoroutineScope = backgroundScope,
workManagerScheduler = workManagerScheduler,
featureFlagService = featureFlagService,
workerDataConverter = WorkerDataConverter(DefaultJsonProvider()),
workerDataConverter = SyncNotificationsWorkerDataConverter(DefaultJsonProvider()),
buildVersionSdkIntProvider = FakeBuildVersionSdkIntProvider(33),
),
appCoroutineScope = backgroundScope,

View File

@@ -177,7 +177,7 @@ class FetchNotificationWorkerTest {
workManagerScheduler = workManagerScheduler,
syncOnNotifiableEvent = syncOnNotifiableEvent,
coroutineDispatchers = testCoroutineDispatchers(),
workerDataConverter = WorkerDataConverter(DefaultJsonProvider()),
workerDataConverter = SyncNotificationsWorkerDataConverter(DefaultJsonProvider()),
buildVersionSdkIntProvider = FakeBuildVersionSdkIntProvider(33),
)

View File

@@ -78,7 +78,7 @@ class SyncNotificationWorkManagerRequestTest {
val request = createSyncNotificationWorkManagerRequest(
sessionId = A_SESSION_ID,
notificationEventRequests = listOf(aNotificationEventRequest()),
workerDataConverter = WorkerDataConverter({ error("error during serialization") })
workerDataConverter = SyncNotificationsWorkerDataConverter({ error("error during serialization") })
)
val result = request.build()
assertThat(result.isFailure).isTrue()
@@ -88,7 +88,7 @@ class SyncNotificationWorkManagerRequestTest {
private fun createSyncNotificationWorkManagerRequest(
sessionId: SessionId,
notificationEventRequests: List<NotificationEventRequest>,
workerDataConverter: WorkerDataConverter = WorkerDataConverter(DefaultJsonProvider()),
workerDataConverter: SyncNotificationsWorkerDataConverter = SyncNotificationsWorkerDataConverter(DefaultJsonProvider()),
sdkVersion: Int = 33,
) = SyncNotificationWorkManagerRequest(
sessionId = sessionId,

View File

@@ -57,10 +57,10 @@ class WorkerDataConverterTest {
providerInfo = "info$it",
)
}
val sut = WorkerDataConverter(DefaultJsonProvider())
val sut = SyncNotificationsWorkerDataConverter(DefaultJsonProvider())
val serialized = sut.serialize(data)
assertThat(serialized.getOrNull()?.size).isGreaterThan(1)
assertThat(serialized.getOrNull()?.size).isEqualTo(100 / WorkerDataConverter.CHUNK_SIZE)
assertThat(serialized.getOrNull()?.size).isEqualTo(100 / SyncNotificationsWorkerDataConverter.CHUNK_SIZE)
// All the items are present
val deserialized = serialized.getOrNull()?.flatMap { sut.deserialize(it)!! }
assertThat(deserialized).containsExactlyElementsIn(data)
@@ -76,10 +76,10 @@ class WorkerDataConverterTest {
providerInfo = "info$it",
)
}
val sut = WorkerDataConverter(DefaultJsonProvider())
val sut = SyncNotificationsWorkerDataConverter(DefaultJsonProvider())
val serialized = sut.serialize(data)
assertThat(serialized.getOrNull()?.size).isGreaterThan(1)
assertThat(serialized.getOrNull()?.size).isEqualTo(100 / WorkerDataConverter.CHUNK_SIZE + 1)
assertThat(serialized.getOrNull()?.size).isEqualTo(100 / SyncNotificationsWorkerDataConverter.CHUNK_SIZE + 1)
// All the items are present
val deserialized = serialized.getOrNull()?.flatMap { sut.deserialize(it)!! }
assertThat(deserialized).containsExactlyElementsIn(data)
@@ -112,7 +112,7 @@ class WorkerDataConverterTest {
)
}
val data = (data1 + data2 + data3).shuffled()
val sut = WorkerDataConverter(DefaultJsonProvider())
val sut = SyncNotificationsWorkerDataConverter(DefaultJsonProvider())
val serialized = sut.serialize(data)
assertThat(serialized.getOrNull()?.size).isEqualTo(2)
// All the items are present
@@ -133,7 +133,7 @@ class WorkerDataConverterTest {
}
private fun testIdentity(data: List<NotificationEventRequest>) {
val sut = WorkerDataConverter(DefaultJsonProvider())
val sut = SyncNotificationsWorkerDataConverter(DefaultJsonProvider())
val serialized = sut.serialize(data).getOrThrow()
val result = sut.deserialize(serialized.first())
assertThat(result).isEqualTo(data)

View File

@@ -12,16 +12,19 @@ import io.element.android.libraries.matrix.api.core.SessionId
interface WorkManagerScheduler {
fun submit(workManagerRequest: WorkManagerRequest)
fun hasPendingWork(sessionId: SessionId, requestType: WorkManagerRequestType): Boolean
fun cancel(sessionId: SessionId)
}
fun workManagerTag(sessionId: SessionId, requestType: WorkManagerRequestType): String {
val prefix = when (requestType) {
WorkManagerRequestType.NOTIFICATION_SYNC -> "notifications"
WorkManagerRequestType.DB_VACUUM -> "db_vacuum"
}
return "$prefix-$sessionId"
}
enum class WorkManagerRequestType {
NOTIFICATION_SYNC,
DB_VACUUM,
}

View File

@@ -11,6 +11,7 @@ package io.element.android.libraries.workmanager.impl
import androidx.work.WorkManager
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.bool.orFalse
import dev.zacsweers.metro.SingleIn
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
@@ -51,6 +52,10 @@ class DefaultWorkManagerScheduler(
)
}
override fun hasPendingWork(sessionId: SessionId, requestType: WorkManagerRequestType): Boolean {
return workManager.getWorkInfosByTag(workManagerTag(sessionId, requestType)).get()?.isNotEmpty().orFalse()
}
override fun cancel(sessionId: SessionId) {
Timber.d("Cancelling work for sessionId: $sessionId")
for (requestType in WorkManagerRequestType.entries) {

View File

@@ -10,17 +10,23 @@ package io.element.android.libraries.workmanager.test
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.workmanager.api.WorkManagerRequest
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
import io.element.android.tests.testutils.lambda.lambdaError
class FakeWorkManagerScheduler(
private val submitLambda: (WorkManagerRequest) -> Unit = { lambdaError() },
private val hasPendingWorkLambda: (SessionId, WorkManagerRequestType) -> Boolean = { _, _ -> false },
private val cancelLambda: (SessionId) -> Unit = { lambdaError() },
) : WorkManagerScheduler {
override fun submit(workManagerRequest: WorkManagerRequest) {
submitLambda(workManagerRequest)
}
override fun hasPendingWork(sessionId: SessionId, requestType: WorkManagerRequestType): Boolean {
return hasPendingWorkLambda(sessionId, requestType)
}
override fun cancel(sessionId: SessionId) {
cancelLambda(sessionId)
}