Use Wholphin-specific dispatchers (#1583)

## Description
Switches all usages of `kotlinx.coroutines.Dispatchers` to
`WholphinDispatchers`.

`WholphinDispatchers` provides an easier and more reliable mechanism to
use a test dispatcher in unit tests. For regular app usage,
`WholphinDispatchers` just delegates to `Dispatchers`.

There are no user facing changes

### Related issues
None

### Testing
Basic emulator testing & unit tests

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-06-18 11:17:19 -04:00 • committed by GitHub
parent ddbec0da73
commit e17ae3eb8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 252 additions and 226 deletions

View file

@ -8,17 +8,17 @@ import androidx.lifecycle.LifecycleRegistry
import androidx.work.WorkManager
import com.github.damontecres.wholphin.data.CurrentUser
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.configure
import com.github.damontecres.wholphin.util.reset
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Rule
@ -36,14 +36,14 @@ class LatestNextUpSchedulerServiceTest {
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
WholphinDispatchers.configure(testDispatcher)
every { mockActivity.lifecycle } returns lifecycleRegistry
every { mockServerRepository.current } returns currentUser
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
@After
fun tearDown() = Dispatchers.resetMain()
fun tearDown() = WholphinDispatchers.reset()
private fun createService() =
LatestNextUpSchedulerService(
@ -56,7 +56,7 @@ class LatestNextUpSchedulerServiceTest {
@Test
fun cancels_latestNextUp_work_when_user_null() =
runTest {
runTest(testDispatcher) {
createService()
currentUser.value = null

View file

@ -5,15 +5,15 @@
package com.github.damontecres.wholphin.services
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.configure
import com.github.damontecres.wholphin.util.reset
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import kotlinx.serialization.UseSerializers
import kotlinx.serialization.json.Json
import org.jellyfin.sdk.api.client.ApiClient
@ -56,14 +56,14 @@ class LatestNextUpServiceTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
WholphinDispatchers.configure(testDispatcher)
every { mockApi.tvShowsApi } returns mockTvShowsApi
}
@OptIn(ExperimentalCoroutinesApi::class)
@After
fun tearDown() {
Dispatchers.resetMain()
WholphinDispatchers.reset()
}
@Test

View file

@ -8,19 +8,19 @@ import androidx.work.WorkManager
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.configure
import com.github.damontecres.wholphin.util.reset
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.Response
import org.jellyfin.sdk.model.api.BaseItemDto
@ -48,12 +48,12 @@ class SuggestionServiceTest {
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
WholphinDispatchers.configure(testDispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
WholphinDispatchers.reset()
io.mockk.unmockkObject(GetItemsRequestHandler)
}

View file

@ -11,19 +11,19 @@ import com.github.damontecres.wholphin.data.CurrentUser
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.JellyfinServer
import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.configure
import com.github.damontecres.wholphin.util.reset
import com.google.common.util.concurrent.ListenableFuture
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
@ -43,14 +43,14 @@ class SuggestionsSchedulerServiceTest {
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
WholphinDispatchers.configure(testDispatcher)
every { mockActivity.lifecycle } returns lifecycleRegistry
every { mockServerRepository.current } returns currentUser
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
@After
fun tearDown() = Dispatchers.resetMain()
fun tearDown() = WholphinDispatchers.reset()
private fun createService() =
SuggestionsSchedulerService(

View file

@ -27,18 +27,17 @@ import com.github.damontecres.wholphin.ui.setup.SwitchServerContent
import com.github.damontecres.wholphin.ui.setup.SwitchServerViewModel
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.configure
import com.github.damontecres.wholphin.util.reset
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.jellyfin.sdk.Jellyfin
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.quickConnectApi
@ -105,11 +104,7 @@ class BasicUiTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Before
fun setup() {
Dispatchers.setMain(TestModule.testDispatcher)
mockkStatic(Dispatchers::class)
every { Dispatchers.IO } returns TestModule.testDispatcher
every { Dispatchers.Default } returns TestModule.testDispatcher
WholphinDispatchers.configure(TestModule.testDispatcher)
hiltRule.inject()
screensaverService = mockk(relaxed = true)
@ -130,7 +125,7 @@ class BasicUiTests {
@OptIn(ExperimentalCoroutinesApi::class)
@After
fun tearDown() {
Dispatchers.resetMain()
WholphinDispatchers.reset()
}
/**

View file

@ -36,6 +36,7 @@ import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
import com.github.damontecres.wholphin.services.hilt.IoDispatcher
import com.github.damontecres.wholphin.services.hilt.StandardOkHttpClient
import com.github.damontecres.wholphin.util.CoroutineContextApiClientFactory
import com.github.damontecres.wholphin.util.WholphinDispatchers
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -46,7 +47,6 @@ import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.test.StandardTestDispatcher
@ -225,7 +225,7 @@ object TestDatabaseModule {
.allowMainThreadQueries()
.setQueryCallback({ sqlQuery, args ->
Timber.v("sqlQuery=$sqlQuery, args=$args")
}, Dispatchers.IO.asExecutor())
}, WholphinDispatchers.IO.asExecutor())
.build()
@Provides

View file

@ -0,0 +1,21 @@
package com.github.damontecres.wholphin.util
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
@OptIn(ExperimentalCoroutinesApi::class)
fun WholphinDispatchers.configure(testDispatcher: TestDispatcher) {
Dispatchers.setMain(testDispatcher)
IO = testDispatcher
Default = testDispatcher
}
@OptIn(ExperimentalCoroutinesApi::class)
fun WholphinDispatchers.reset() {
Dispatchers.resetMain()
IO = Dispatchers.IO
Default = Dispatchers.Default
}