Skip to content

Commit e051ab0

Browse files
authored
Merge pull request #637 from code-payments/feat/testing-support
Feat/testing support
2 parents 4a5a4c3 + 0c16e65 commit e051ab0

67 files changed

Lines changed: 1567 additions & 71 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.flipcash.app.inject
2+
3+
import com.flipcash.libs.coroutines.DispatcherProvider
4+
import com.flipcash.app.internal.dispatchers.DefaultDispatcherProvider
5+
import dagger.Module
6+
import dagger.Provides
7+
import dagger.hilt.InstallIn
8+
import dagger.hilt.components.SingletonComponent
9+
import javax.inject.Singleton
10+
11+
@Module
12+
@InstallIn(SingletonComponent::class)
13+
object DispatcherModule {
14+
15+
@Provides
16+
@Singleton
17+
fun providesDispatcherProvider(): DispatcherProvider = DefaultDispatcherProvider()
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.flipcash.app.inject
2+
3+
import com.flipcash.app.core.time.TimeProvider
4+
import com.flipcash.app.internal.time.SystemTimeProvider
5+
import dagger.Module
6+
import dagger.Provides
7+
import dagger.hilt.InstallIn
8+
import dagger.hilt.components.SingletonComponent
9+
import javax.inject.Singleton
10+
11+
@Module
12+
@InstallIn(SingletonComponent::class)
13+
object TimeModule {
14+
15+
@Provides
16+
@Singleton
17+
fun providesTimeProvider(): TimeProvider = SystemTimeProvider()
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.flipcash.app.internal.dispatchers
2+
3+
import com.flipcash.libs.coroutines.DispatcherProvider
4+
import kotlinx.coroutines.CoroutineDispatcher
5+
import kotlinx.coroutines.Dispatchers
6+
7+
class DefaultDispatcherProvider : DispatcherProvider {
8+
override val Default: CoroutineDispatcher = Dispatchers.Default
9+
override val Main: CoroutineDispatcher = Dispatchers.Main
10+
override val IO: CoroutineDispatcher = Dispatchers.IO
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.flipcash.app.internal.time
2+
3+
import com.flipcash.app.core.time.TimeProvider
4+
import kotlin.time.Clock
5+
6+
class SystemTimeProvider : TimeProvider {
7+
override fun now() = Clock.System.now()
8+
override fun currentTimeMillis() = System.currentTimeMillis()
9+
}

apps/flipcash/core/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ android {
77
}
88

99
dependencies {
10+
testImplementation(kotlin("test"))
11+
testImplementation(libs.bundles.unit.testing)
12+
1013
implementation(libs.androidx.browser)
1114

1215
implementation(libs.kotlinx.serialization.core)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.flipcash.app.core.time
2+
3+
import kotlin.time.Clock
4+
import kotlin.time.Duration
5+
import kotlin.time.Instant
6+
7+
class FakeTimeProvider(
8+
private var currentTime: Instant = Clock.System.now()
9+
) : TimeProvider {
10+
override fun now(): Instant = currentTime
11+
fun advanceBy(duration: Duration) { currentTime += duration }
12+
fun setTime(instant: Instant) { currentTime = instant }
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.flipcash.app.core.time
2+
3+
import kotlin.time.Instant
4+
5+
interface TimeProvider {
6+
fun now(): Instant
7+
fun currentTimeMillis(): Long = now().toEpochMilliseconds()
8+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.flipcash.app.core
2+
3+
import kotlinx.coroutines.Dispatchers
4+
import kotlinx.coroutines.ExperimentalCoroutinesApi
5+
import kotlinx.coroutines.test.StandardTestDispatcher
6+
import kotlinx.coroutines.test.TestDispatcher
7+
import kotlinx.coroutines.test.resetMain
8+
import kotlinx.coroutines.test.setMain
9+
import org.junit.rules.TestWatcher
10+
import org.junit.runner.Description
11+
12+
@OptIn(ExperimentalCoroutinesApi::class)
13+
class MainCoroutineRule(
14+
val dispatcher: TestDispatcher = StandardTestDispatcher()
15+
) : TestWatcher() {
16+
17+
override fun starting(description: Description) {
18+
Dispatchers.setMain(dispatcher)
19+
}
20+
21+
override fun finished(description: Description) {
22+
Dispatchers.resetMain()
23+
}
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.flipcash.app.core.dispatchers
2+
3+
import com.flipcash.libs.coroutines.DispatcherProvider
4+
import kotlinx.coroutines.CoroutineDispatcher
5+
import kotlinx.coroutines.test.StandardTestDispatcher
6+
import kotlinx.coroutines.test.TestDispatcher
7+
8+
class TestDispatcherProvider(
9+
val testDispatcher: TestDispatcher = StandardTestDispatcher()
10+
) : DispatcherProvider {
11+
override val Default: CoroutineDispatcher get() = testDispatcher
12+
override val Main: CoroutineDispatcher get() = testDispatcher
13+
override val IO: CoroutineDispatcher get() = testDispatcher
14+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.flipcash.app.core.time
2+
3+
import org.junit.Test
4+
import kotlin.test.assertEquals
5+
import kotlin.time.Duration.Companion.milliseconds
6+
import kotlin.time.Duration.Companion.minutes
7+
import kotlin.time.Duration.Companion.seconds
8+
import kotlin.time.Instant
9+
10+
class FakeTimeProviderTest {
11+
12+
@Test
13+
fun `now returns start time initially`() {
14+
val startTime = Instant.fromEpochMilliseconds(1_000_000L)
15+
val provider = FakeTimeProvider(startTime)
16+
17+
assertEquals(startTime, provider.now())
18+
}
19+
20+
@Test
21+
fun `currentTimeMillis returns epoch millis of now`() {
22+
val startTime = Instant.fromEpochMilliseconds(1_000_000L)
23+
val provider = FakeTimeProvider(startTime)
24+
25+
assertEquals(1_000_000L, provider.currentTimeMillis())
26+
}
27+
28+
@Test
29+
fun `advanceBy moves time forward`() {
30+
val startTime = Instant.fromEpochMilliseconds(0L)
31+
val provider = FakeTimeProvider(startTime)
32+
33+
provider.advanceBy(5.seconds)
34+
35+
assertEquals(5_000L, provider.currentTimeMillis())
36+
}
37+
38+
@Test
39+
fun `advanceBy accumulates across multiple calls`() {
40+
val startTime = Instant.fromEpochMilliseconds(0L)
41+
val provider = FakeTimeProvider(startTime)
42+
43+
provider.advanceBy(1.minutes)
44+
provider.advanceBy(30.seconds)
45+
46+
assertEquals(90_000L, provider.currentTimeMillis())
47+
}
48+
49+
@Test
50+
fun `setTime overrides current time`() {
51+
val provider = FakeTimeProvider(Instant.fromEpochMilliseconds(0L))
52+
val newTime = Instant.fromEpochMilliseconds(999_999L)
53+
54+
provider.setTime(newTime)
55+
56+
assertEquals(newTime, provider.now())
57+
assertEquals(999_999L, provider.currentTimeMillis())
58+
}
59+
60+
@Test
61+
fun `advanceBy works after setTime`() {
62+
val provider = FakeTimeProvider(Instant.fromEpochMilliseconds(0L))
63+
64+
provider.setTime(Instant.fromEpochMilliseconds(10_000L))
65+
provider.advanceBy(500.milliseconds)
66+
67+
assertEquals(10_500L, provider.currentTimeMillis())
68+
}
69+
}

0 commit comments

Comments
 (0)