resolve flaky jvm multi threaded coroutine tests

By switching to using the jvm only (hopefully by 1.6 in common) coroutine test lib we can swap to using a fake dispatcher instead of defining threads which should resolve any odd timing issues.
This commit is contained in:
Tristan 2021-06-15 19:26:27 +01:00 committed by Michael Bull
parent 08cf52bfd4
commit 07b4d61d3f
3 changed files with 25 additions and 20 deletions

View File

@ -3,6 +3,7 @@ object Versions {
const val kotlin = "1.5.10"
const val kotlinBenchmark = "0.2.0-dev-20"
const val kotlinCoroutines = "1.5.0"
const val kotlinCoroutinesTest = "1.5.0"
const val ktor = "1.5.4"
const val logback = "1.2.3"
const val versionsPlugin = "0.38.0"

View File

@ -31,6 +31,7 @@ kotlin {
dependencies {
implementation(kotlin("test-junit"))
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinCoroutinesTest}")
}
}

View File

@ -3,19 +3,19 @@ package com.github.michaelbull.result.coroutines.binding
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import java.util.concurrent.Executors
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ExperimentalCoroutinesApi
class AsyncSuspendableBindingTest {
private sealed class BindingError {
@ -35,7 +35,7 @@ class AsyncSuspendableBindingTest {
return Ok(2)
}
runBlocking {
runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
@ -67,7 +67,7 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
runBlocking {
runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
@ -95,18 +95,19 @@ class AsyncSuspendableBindingTest {
}
suspend fun provideY(): Result<Int, BindingError.BindingErrorB> {
// as this test uses a new thread for each coroutine, we want to set this delay to a high enough number that
// there isn't any chance of a jvm run actually completing this suspending function in this thread first
// otherwise the assertions might fail.
delay(100)
delay(3)
yStateChange = true
return Err(BindingError.BindingErrorB)
}
val dispatcherA = TestCoroutineDispatcher()
val dispatcherB = TestCoroutineDispatcher()
runBlocking {
val result = binding<Int, BindingError> {
val x = async(newThread("ThreadA")) { provideX().bind() }
val y = async(newThread("ThreadB")) { provideY().bind() }
val x = async(dispatcherA) { provideX().bind() }
val y = async(dispatcherB) { provideY().bind() }
dispatcherA.advanceTimeBy(2)
x.await() + y.await()
}
@ -144,11 +145,17 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
val dispatcherA = TestCoroutineDispatcher()
val dispatcherB = TestCoroutineDispatcher()
val dispatcherC = TestCoroutineDispatcher()
runBlocking {
val result = binding<Unit, BindingError> {
launch(newThread("Thread A")) { provideX().bind() }
launch(newThread("Thread B")) { provideY().bind() }
launch(newThread("Thread C")) { provideZ().bind() }
launch(dispatcherA) { provideX().bind() }
dispatcherA.advanceTimeBy(20)
launch(dispatcherB) { provideY().bind() }
dispatcherB.advanceTimeBy(20)
launch(dispatcherC) { provideZ().bind() }
}
assertTrue(result is Err)
@ -161,8 +168,4 @@ class AsyncSuspendableBindingTest {
assertFalse(zStateChange)
}
}
private fun newThread(name: String): CoroutineContext {
return Executors.newSingleThreadExecutor().asCoroutineDispatcher() + CoroutineName(name)
}
}