Add suspend variant of binding function

Closes #24
This commit is contained in:
Tristan Hamilton 2020-07-17 16:43:41 +01:00 committed by Michael Bull
parent a40dcdf688
commit bd7e1244b3
5 changed files with 177 additions and 0 deletions

View File

@ -91,6 +91,7 @@ kotlin {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.7")
}
}

View File

@ -0,0 +1,26 @@
package com.github.michaelbull.result.coroutines
import com.github.michaelbull.result.BindException
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.ResultBinding
import com.github.michaelbull.result.ResultBindingImpl
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Suspending variant of [binding][com.github.michaelbull.result.binding].
*/
suspend inline fun <V, E> binding(crossinline block: suspend ResultBinding<E>.() -> V): Result<V, E> {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
val receiver = ResultBindingImpl<E>()
return try {
with(receiver) { Ok(block()) }
} catch (ex: BindException) {
receiver.error
}
}

View File

@ -0,0 +1,9 @@
package com.github.michaelbull.result
import kotlinx.coroutines.CoroutineScope
/**
* 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(block: suspend (scope : CoroutineScope) -> Unit)

View File

@ -0,0 +1,135 @@
package com.github.michaelbull.result.coroutines
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.runBlockingTest
import kotlinx.coroutines.delay
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SuspendableBindingTest {
private object BindingError
@Test
fun returnsOkIfAllBindsSuccessful() {
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
return Ok(1)
}
suspend fun provideY(): Result<Int, BindingError> {
delay(1)
return Ok(2)
}
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
)
}
}
@Test
fun returnsOkIfAllBindsOfDifferentTypeAreSuccessful() {
suspend fun provideX(): Result<String, BindingError> {
delay(1)
return Ok("1")
}
suspend fun provideY(x: Int): Result<Int, BindingError> {
delay(1)
return Ok(x + 2)
}
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
)
}
}
@Test
fun returnsFirstErrIfBindingFailed() {
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
return Ok(1)
}
suspend fun provideY(): Result<Int, BindingError> {
delay(1)
return Err(BindingError)
}
suspend fun provideZ(): Result<Int, BindingError> {
delay(1)
return Ok(2)
}
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
)
}
}
@Test
fun returnsFirstErrIfBindingsOfDifferentTypesFailed() {
suspend fun provideX(): Result<Int, BindingError> {
delay(1)
return Ok(1)
}
suspend fun provideY(): Result<String, BindingError> {
delay(1)
return Err(BindingError)
}
suspend fun provideZ(): Result<Int, BindingError> {
delay(1)
return Ok(2)
}
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
)
}
}
}

View File

@ -0,0 +1,6 @@
package com.github.michaelbull.result
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
actual fun runBlockingTest(block: suspend (scope : CoroutineScope) -> Unit) = runBlocking { block(this) }