parent
b3f1edc369
commit
72df4c0ff6
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
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
|
||||
expected = Ok(3),
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsFirstErrIfBindingFailed() {
|
||||
fun returnsFirstErrIfBindingFailed() = runTest {
|
||||
suspend fun provideX(): Result<Int, BindingError> {
|
||||
delay(1)
|
||||
return Ok(1)
|
||||
@ -85,7 +81,6 @@ class SuspendableBindingTest {
|
||||
return Ok(2)
|
||||
}
|
||||
|
||||
return runBlockingTest {
|
||||
val result = binding<Int, BindingError> {
|
||||
val x = provideX().bind()
|
||||
val y = provideY().bind()
|
||||
@ -93,19 +88,18 @@ class SuspendableBindingTest {
|
||||
x + y + z
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError,
|
||||
actual = result.error
|
||||
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,7 +118,6 @@ class SuspendableBindingTest {
|
||||
return Err(BindingError)
|
||||
}
|
||||
|
||||
runBlockingTest {
|
||||
val result = binding<Int, BindingError> {
|
||||
val x = provideX().bind()
|
||||
val y = provideY().bind()
|
||||
@ -132,19 +125,18 @@ class SuspendableBindingTest {
|
||||
x + y + z
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError,
|
||||
actual = result.error
|
||||
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,7 +152,6 @@ class SuspendableBindingTest {
|
||||
return Ok(2)
|
||||
}
|
||||
|
||||
return runBlockingTest {
|
||||
val result = binding<Int, BindingError> {
|
||||
val x = provideX().bind()
|
||||
val y = provideY().bind()
|
||||
@ -168,11 +159,9 @@ class SuspendableBindingTest {
|
||||
x + y.toInt() + z
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError,
|
||||
actual = result.error
|
||||
expected = Err(BindingError),
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
expected = Ok(3),
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsFirstErrIfBindingFailed(): dynamic {
|
||||
fun returnsFirstErrIfBindingFailed() = runTest {
|
||||
suspend fun provideX(): Result<Int, BindingError> {
|
||||
delay(10)
|
||||
return Ok(1)
|
||||
@ -61,7 +61,6 @@ class AsyncSuspendableBindingTest {
|
||||
return Err(BindingError.BindingErrorB)
|
||||
}
|
||||
|
||||
return runBlockingTest {
|
||||
val result = binding<Int, BindingError> {
|
||||
val x = async { provideX().bind() }
|
||||
val y = async { provideY().bind() }
|
||||
@ -69,19 +68,18 @@ class AsyncSuspendableBindingTest {
|
||||
x.await() + y.await() + z.await()
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError.BindingErrorB,
|
||||
actual = result.error
|
||||
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,7 +98,6 @@ class AsyncSuspendableBindingTest {
|
||||
return Err(BindingError.BindingErrorB)
|
||||
}
|
||||
|
||||
return runBlockingTest {
|
||||
val result = binding<Int, BindingError> {
|
||||
val x = async { provideX().bind() }
|
||||
val y = async { provideY().bind() }
|
||||
@ -109,14 +106,13 @@ class AsyncSuspendableBindingTest {
|
||||
x.await() + y.await() + z.await()
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError.BindingErrorB,
|
||||
actual = result.error
|
||||
expected = Err(BindingError.BindingErrorB),
|
||||
actual = result
|
||||
)
|
||||
|
||||
assertFalse(xStateChange)
|
||||
assertFalse(yStateChange)
|
||||
assertTrue(zStateChange)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
expected = Ok(3),
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsFirstErrIfBindingFailed() {
|
||||
fun returnsFirstErrIfBindingFailed() = runTest {
|
||||
suspend fun provideX(): Result<Int, BindingError> {
|
||||
delay(3)
|
||||
return Ok(1)
|
||||
@ -67,7 +63,6 @@ class AsyncSuspendableBindingTest {
|
||||
return Err(BindingError.BindingErrorB)
|
||||
}
|
||||
|
||||
runBlockingTest {
|
||||
val result = binding<Int, BindingError> {
|
||||
val x = async { provideX().bind() }
|
||||
val y = async { provideY().bind() }
|
||||
@ -75,16 +70,14 @@ class AsyncSuspendableBindingTest {
|
||||
x.await() + y.await() + z.await()
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError.BindingErrorB,
|
||||
actual = result.error
|
||||
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)
|
||||
|
||||
testScheduler.advanceTimeBy(2)
|
||||
testScheduler.runCurrent()
|
||||
|
||||
x.await() + y.await()
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError.BindingErrorA,
|
||||
actual = result.error
|
||||
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)
|
||||
|
||||
testScheduler.advanceTimeBy(20)
|
||||
testScheduler.runCurrent()
|
||||
|
||||
launch(dispatcherB) { provideY().bind() }
|
||||
dispatcherB.advanceTimeBy(20)
|
||||
|
||||
testScheduler.advanceTimeBy(20)
|
||||
testScheduler.runCurrent()
|
||||
|
||||
launch(dispatcherC) { provideZ().bind() }
|
||||
}
|
||||
|
||||
assertTrue(result is Err)
|
||||
assertEquals(
|
||||
expected = BindingError.BindingErrorA,
|
||||
actual = result.error
|
||||
expected = Err(BindingError.BindingErrorA),
|
||||
actual = result
|
||||
)
|
||||
|
||||
assertTrue(xStateChange)
|
||||
assertTrue(yStateChange)
|
||||
assertFalse(zStateChange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user