Update kotlinx-coroutines to 1.6.0

Closes #69
This commit is contained in:
Michael Bull 2022-01-08 13:11:58 +00:00
parent b3f1edc369
commit 72df4c0ff6
10 changed files with 176 additions and 265 deletions

View File

@ -2,8 +2,7 @@ object Versions {
const val dokka = "1.6.10"
const val kotlin = "1.6.10"
const val kotlinBenchmark = "0.4.1"
const val kotlinCoroutines = "1.5.2"
const val kotlinCoroutinesTest = "1.5.2"
const val kotlinCoroutines = "1.6.0"
const val ktor = "2.0.0-beta-1"
const val logback = "1.2.3"
const val versionsPlugin = "0.41.0"

View File

@ -24,6 +24,7 @@ kotlin {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinCoroutines}")
}
}
@ -31,7 +32,6 @@ kotlin {
dependencies {
implementation(kotlin("test-junit"))
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinCoroutinesTest}")
}
}
@ -40,30 +40,6 @@ kotlin {
implementation(kotlin("test-js"))
}
}
val nativeTest by creating {
dependsOn(commonTest)
}
val linuxX64Test by getting {
dependsOn(nativeTest)
}
val mingwX64Test by getting {
dependsOn(nativeTest)
}
val macosX64Test by getting {
dependsOn(nativeTest)
}
val iosX64Test by getting {
dependsOn(nativeTest)
}
val iosArm64Test by getting {
dependsOn(nativeTest)
}
}
}

View File

@ -1,14 +0,0 @@
package com.github.michaelbull.result.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/**
* Workaround to use suspending functions in unit tests for multiplatform/native projects.
* Solution was found here: https://github.com/Kotlin/kotlinx.coroutines/issues/885#issuecomment-446586161
*/
expect fun runBlockingTest(
context: CoroutineContext = EmptyCoroutineContext,
testBody: suspend CoroutineScope.() -> Unit
)

View File

@ -3,19 +3,21 @@ 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 com.github.michaelbull.result.coroutines.runBlockingTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ExperimentalCoroutinesApi
class SuspendableBindingTest {
private object BindingError
@Test
fun returnsOkIfAllBindsSuccessful() {
fun returnsOkIfAllBindsSuccessful() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
return Ok(1)
@ -26,23 +28,20 @@ class SuspendableBindingTest {
return Ok(2)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
x + y
}
assertTrue(result is Ok)
assertEquals(
expected = 3,
actual = result.value
)
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
x + y
}
assertEquals(
expected = Ok(3),
actual = result
)
}
@Test
fun returnsOkIfAllBindsOfDifferentTypeAreSuccessful() {
fun returnsOkIfAllBindsOfDifferentTypeAreSuccessful() = runTest {
suspend fun provideX(): Result<String, BindingError> {
delay(1)
return Ok("1")
@ -53,23 +52,20 @@ class SuspendableBindingTest {
return Ok(x + 2)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY(x.toInt()).bind()
y
}
assertTrue(result is Ok)
assertEquals(
expected = 3,
actual = result.value
)
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY(x.toInt()).bind()
y
}
assertEquals(
expected = Ok(3),
actual = result
)
}
@Test
fun returnsFirstErrIfBindingFailed() {
fun returnsFirstErrIfBindingFailed() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
return Ok(1)
@ -85,27 +81,25 @@ class SuspendableBindingTest {
return Ok(2)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
val z = provideZ().bind()
x + y + z
}
assertTrue(result is Err)
assertEquals(
expected = BindingError,
actual = result.error
)
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
val z = provideZ().bind()
x + y + z
}
assertEquals(
expected = Err(BindingError),
actual = result
)
}
@Test
fun returnsStateChangedUntilFirstBindFailed() {
fun returnsStateChangedUntilFirstBindFailed() = runTest {
var xStateChange = false
var yStateChange = false
var zStateChange = false
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
xStateChange = true
@ -124,27 +118,25 @@ class SuspendableBindingTest {
return Err(BindingError)
}
runBlockingTest {
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
val z = provideZ().bind()
x + y + z
}
assertTrue(result is Err)
assertEquals(
expected = BindingError,
actual = result.error
)
assertTrue(xStateChange)
assertTrue(yStateChange)
assertFalse(zStateChange)
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
val z = provideZ().bind()
x + y + z
}
assertEquals(
expected = Err(BindingError),
actual = result
)
assertTrue(xStateChange)
assertTrue(yStateChange)
assertFalse(zStateChange)
}
@Test
fun returnsFirstErrIfBindingsOfDifferentTypesFailed() {
fun returnsFirstErrIfBindingsOfDifferentTypesFailed() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
return Ok(1)
@ -160,19 +152,16 @@ class SuspendableBindingTest {
return Ok(2)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
val z = provideZ().bind()
x + y.toInt() + z
}
assertTrue(result is Err)
assertEquals(
expected = BindingError,
actual = result.error
)
val result = binding<Int, BindingError> {
val x = provideX().bind()
val y = provideY().bind()
val z = provideZ().bind()
x + y.toInt() + z
}
assertEquals(
expected = Err(BindingError),
actual = result
)
}
}

View File

@ -1,13 +0,0 @@
package com.github.michaelbull.result.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.promise
import kotlin.coroutines.CoroutineContext
actual fun runBlockingTest(context: CoroutineContext, testBody: suspend CoroutineScope.() -> Unit): dynamic {
return GlobalScope.promise(context) {
testBody(this)
}
}

View File

@ -3,14 +3,16 @@ 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 com.github.michaelbull.result.coroutines.runBlockingTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ExperimentalCoroutinesApi
class AsyncSuspendableBindingTest {
private sealed class BindingError {
@ -19,7 +21,7 @@ class AsyncSuspendableBindingTest {
}
@Test
fun returnsOkIfAllBindsSuccessful(): dynamic {
fun returnsOkIfAllBindsSuccessful() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(100)
return Ok(1)
@ -30,22 +32,20 @@ class AsyncSuspendableBindingTest {
return Ok(2)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
x.await() + y.await()
}
assertTrue(result is Ok)
assertEquals(
expected = 3,
actual = result.value
)
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
x.await() + y.await()
}
assertEquals(
expected = Ok(3),
actual = result
)
}
@Test
fun returnsFirstErrIfBindingFailed(): dynamic {
fun returnsFirstErrIfBindingFailed() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(10)
return Ok(1)
@ -61,27 +61,25 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
val z = async { provideZ().bind() }
x.await() + y.await() + z.await()
}
assertTrue(result is Err)
assertEquals(
expected = BindingError.BindingErrorB,
actual = result.error
)
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
val z = async { provideZ().bind() }
x.await() + y.await() + z.await()
}
assertEquals(
expected = Err(BindingError.BindingErrorB),
actual = result
)
}
@Test
fun returnsStateChangedForOnlyTheFirstAsyncBindFailWhenEagerlyCancellingBinding(): dynamic {
fun returnsStateChangedForOnlyTheFirstAsyncBindFailWhenEagerlyCancellingBinding() = runTest {
var xStateChange = false
var yStateChange = false
var zStateChange = false
suspend fun provideX(): Result<Int, BindingError> {
delay(20)
xStateChange = true
@ -100,23 +98,21 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
return runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
val z = async { provideZ().bind() }
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
val z = async { provideZ().bind() }
x.await() + y.await() + z.await()
}
assertTrue(result is Err)
assertEquals(
expected = BindingError.BindingErrorB,
actual = result.error
)
assertFalse(xStateChange)
assertFalse(yStateChange)
assertTrue(zStateChange)
x.await() + y.await() + z.await()
}
assertEquals(
expected = Err(BindingError.BindingErrorB),
actual = result
)
assertFalse(xStateChange)
assertFalse(yStateChange)
assertTrue(zStateChange)
}
}

View File

@ -1,11 +0,0 @@
package com.github.michaelbull.result.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext
actual fun runBlockingTest(context: CoroutineContext, testBody: suspend CoroutineScope.() -> Unit) {
return runBlocking {
testBody(this)
}
}

View File

@ -8,7 +8,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
@ -17,7 +18,7 @@ import kotlin.test.assertNull
class RunSuspendCatchingTest {
@Test
fun propagatesCoroutineCancellation() = runBlockingTest {
fun propagatesCoroutineCancellation() = runTest(UnconfinedTestDispatcher()) {
var value: String? = null
launch(CoroutineName("outer scope")) {
@ -31,7 +32,8 @@ class RunSuspendCatchingTest {
result.onSuccess { value = it }
}
advanceTimeBy(2_000)
testScheduler.advanceTimeBy(2_000)
testScheduler.runCurrent()
// Cancel outer scope, which should cancel inner scope
cancel()
@ -41,7 +43,7 @@ class RunSuspendCatchingTest {
}
@Test
fun returnsOkIfInvocationSuccessful() = runBlockingTest {
fun returnsOkIfInvocationSuccessful() = runTest {
val block = { "example" }
val result = runSuspendCatching(block)
@ -52,7 +54,7 @@ class RunSuspendCatchingTest {
}
@Test
fun returnsErrIfInvocationFailsWithAnythingOtherThanCancellationException() = runBlockingTest {
fun returnsErrIfInvocationFailsWithAnythingOtherThanCancellationException() = runTest {
val exception = IllegalArgumentException("throw me")
val block = { throw exception }
val result = runSuspendCatching(block)

View File

@ -5,11 +5,10 @@ import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@ -24,7 +23,7 @@ class AsyncSuspendableBindingTest {
}
@Test
fun returnsOkIfAllBindsSuccessful() {
fun returnsOkIfAllBindsSuccessful() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(100)
return Ok(1)
@ -35,23 +34,20 @@ class AsyncSuspendableBindingTest {
return Ok(2)
}
runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
x.await() + y.await()
}
assertTrue(result is Ok)
assertEquals(
expected = 3,
actual = result.value
)
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
x.await() + y.await()
}
assertEquals(
expected = Ok(3),
actual = result
)
}
@Test
fun returnsFirstErrIfBindingFailed() {
fun returnsFirstErrIfBindingFailed() = runTest {
suspend fun provideX(): Result<Int, BindingError> {
delay(3)
return Ok(1)
@ -67,24 +63,21 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
runBlockingTest {
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
val z = async { provideZ().bind() }
x.await() + y.await() + z.await()
}
assertTrue(result is Err)
assertEquals(
expected = BindingError.BindingErrorB,
actual = result.error
)
val result = binding<Int, BindingError> {
val x = async { provideX().bind() }
val y = async { provideY().bind() }
val z = async { provideZ().bind() }
x.await() + y.await() + z.await()
}
assertEquals(
expected = Err(BindingError.BindingErrorB),
actual = result
)
}
@Test
fun returnsStateChangedForOnlyTheFirstAsyncBindFailWhenEagerlyCancellingBinding() {
fun returnsStateChangedForOnlyTheFirstAsyncBindFailWhenEagerlyCancellingBinding() = runTest {
var xStateChange = false
var yStateChange = false
@ -100,29 +93,30 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
val dispatcherA = TestCoroutineDispatcher()
val dispatcherB = TestCoroutineDispatcher()
val dispatcherA = StandardTestDispatcher(testScheduler)
val dispatcherB = StandardTestDispatcher(testScheduler)
runBlocking {
val result = binding<Int, BindingError> {
val x = async(dispatcherA) { provideX().bind() }
val y = async(dispatcherB) { provideY().bind() }
dispatcherA.advanceTimeBy(2)
x.await() + y.await()
}
val result = binding<Int, BindingError> {
val x = async(dispatcherA) { provideX().bind() }
val y = async(dispatcherB) { provideY().bind() }
assertTrue(result is Err)
assertEquals(
expected = BindingError.BindingErrorA,
actual = result.error
)
assertTrue(xStateChange)
assertFalse(yStateChange)
testScheduler.advanceTimeBy(2)
testScheduler.runCurrent()
x.await() + y.await()
}
assertEquals(
expected = Err(BindingError.BindingErrorA),
actual = result
)
assertTrue(xStateChange)
assertFalse(yStateChange)
}
@Test
fun returnsStateChangedForOnlyTheFirstLaunchBindFailWhenEagerlyCancellingBinding() {
fun returnsStateChangedForOnlyTheFirstLaunchBindFailWhenEagerlyCancellingBinding() = runTest {
var xStateChange = false
var yStateChange = false
var zStateChange = false
@ -145,27 +139,31 @@ class AsyncSuspendableBindingTest {
return Err(BindingError.BindingErrorB)
}
val dispatcherA = TestCoroutineDispatcher()
val dispatcherB = TestCoroutineDispatcher()
val dispatcherC = TestCoroutineDispatcher()
val dispatcherA = StandardTestDispatcher(testScheduler)
val dispatcherB = StandardTestDispatcher(testScheduler)
val dispatcherC = StandardTestDispatcher(testScheduler)
runBlocking {
val result = binding<Unit, BindingError> {
launch(dispatcherA) { provideX().bind() }
dispatcherA.advanceTimeBy(20)
launch(dispatcherB) { provideY().bind() }
dispatcherB.advanceTimeBy(20)
launch(dispatcherC) { provideZ().bind() }
}
val result = binding<Unit, BindingError> {
launch(dispatcherA) { provideX().bind() }
assertTrue(result is Err)
assertEquals(
expected = BindingError.BindingErrorA,
actual = result.error
)
assertTrue(xStateChange)
assertTrue(yStateChange)
assertFalse(zStateChange)
testScheduler.advanceTimeBy(20)
testScheduler.runCurrent()
launch(dispatcherB) { provideY().bind() }
testScheduler.advanceTimeBy(20)
testScheduler.runCurrent()
launch(dispatcherC) { provideZ().bind() }
}
assertEquals(
expected = Err(BindingError.BindingErrorA),
actual = result
)
assertTrue(xStateChange)
assertTrue(yStateChange)
assertFalse(zStateChange)
}
}

View File

@ -1,11 +0,0 @@
package com.github.michaelbull.result.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.CoroutineContext
actual fun runBlockingTest(context: CoroutineContext, testBody: suspend CoroutineScope.() -> Unit) {
runBlocking {
testBody(this)
}
}