Skip to content

Instantly share code, notes, and snippets.

@DevNebulae
Last active October 11, 2022 23:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DevNebulae/a0ead640f3c9047bb1495cceb35e7e33 to your computer and use it in GitHub Desktop.
Save DevNebulae/a0ead640f3c9047bb1495cceb35e7e33 to your computer and use it in GitHub Desktop.
Lifecycle-aware class intended to be used to handle periodic tasks. Its main purpose is to handle periodic tasks in a viewmodel (e.g. refreshing a list from a web service)
// Uses packages:
// * org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.time.Duration
class TickHandler {
private var job: Job? = null
fun start(scope: CoroutineScope, interval: Duration, block: suspend () -> Unit) {
job = scope.launch {
while (isActive) {
block()
delay(interval)
}
}
}
fun stop() {
job?.cancel()
}
}
// Uses packages:
// * org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4
// * io.kotest:kotest-runner-junit5:5.5.1
// * io.kotest:kotest-assertions-core:5.5.1
import io.kotest.core.spec.style.FunSpec
import io.kotest.core.test.testCoroutineScheduler
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@OptIn(ExperimentalCoroutinesApi::class, ExperimentalStdlibApi::class)
class TickHandlerTest : FunSpec({
val tickHandler = TickHandler()
test("Periodically emits events").config(
coroutineTestScope = true,
timeout = 10.seconds
) {
// Arrange
// An initial delay is added to allow the ticker to immediately emit an event when the
// ticker is started (probably has to do with initializing the (scope of a) test).
val initialDelay = 100.milliseconds
val interval = 1.seconds
var tickCounter = 0
// Act
tickHandler.start(this, interval) { tickCounter++ }
testCoroutineScheduler.advanceTimeBy(interval * 4 + initialDelay)
// Assert
// It should be 5 after 4 seconds, as it immediately emits an event when the ticker is
// started.
tickCounter shouldBe 5
// Tear down
tickHandler.stop()
}
})
@OptIn(ExperimentalCoroutinesApi::class)
private fun TestCoroutineScheduler.advanceTimeBy(delay: Duration) =
advanceTimeBy(delay.inWholeMilliseconds)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment