From 0f90bb8b90c4fa224207ac40858856bce02b94fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=BCller?= Date: Sun, 29 Nov 2020 22:34:08 +0100 Subject: [PATCH] Add JS targets for Kotlin Multiplatform (#36) --- build.gradle.kts | 5 + kotlin-result-coroutines/build.gradle.kts | 6 + .../binding/SuspendableBindingTest.kt | 8 +- .../RunBlockingTest.kt | 10 ++ .../binding/AsyncSuspendableBindingTests.kt | 122 ++++++++++++++++++ kotlin-result/build.gradle.kts | 8 ++ .../michaelbull/result/BindException.kt | 3 + 7 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/RunBlockingTest.kt create mode 100644 kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/binding/AsyncSuspendableBindingTests.kt create mode 100644 kotlin-result/src/jsMain/kotlin/com/github/michaelbull/result/BindException.kt diff --git a/build.gradle.kts b/build.gradle.kts index 98fac66..cdbd7a5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,6 +62,11 @@ subprojects { artifact(javadocJar.get()) } } + + js { + browser() + nodejs() + } } } diff --git a/kotlin-result-coroutines/build.gradle.kts b/kotlin-result-coroutines/build.gradle.kts index 9863f24..d1b7999 100644 --- a/kotlin-result-coroutines/build.gradle.kts +++ b/kotlin-result-coroutines/build.gradle.kts @@ -33,6 +33,12 @@ kotlin { implementation(kotlin("test")) } } + + val jsTest by getting { + dependencies { + implementation(kotlin("test-js")) + } + } } } diff --git a/kotlin-result-coroutines/src/commonTest/kotlin/com.github.michaelbull.result.coroutines/binding/SuspendableBindingTest.kt b/kotlin-result-coroutines/src/commonTest/kotlin/com.github.michaelbull.result.coroutines/binding/SuspendableBindingTest.kt index f9430e1..a1f3b66 100644 --- a/kotlin-result-coroutines/src/commonTest/kotlin/com.github.michaelbull.result.coroutines/binding/SuspendableBindingTest.kt +++ b/kotlin-result-coroutines/src/commonTest/kotlin/com.github.michaelbull.result.coroutines/binding/SuspendableBindingTest.kt @@ -26,7 +26,7 @@ class SuspendableBindingTest { return Ok(2) } - runBlockingTest { + return runBlockingTest { val result = binding { val x = provideX().bind() val y = provideY().bind() @@ -53,7 +53,7 @@ class SuspendableBindingTest { return Ok(x + 2) } - runBlockingTest { + return runBlockingTest { val result = binding { val x = provideX().bind() val y = provideY(x.toInt()).bind() @@ -85,7 +85,7 @@ class SuspendableBindingTest { return Ok(2) } - runBlockingTest { + return runBlockingTest { val result = binding { val x = provideX().bind() val y = provideY().bind() @@ -160,7 +160,7 @@ class SuspendableBindingTest { return Ok(2) } - runBlockingTest { + return runBlockingTest { val result = binding { val x = provideX().bind() val y = provideY().bind() diff --git a/kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/RunBlockingTest.kt b/kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/RunBlockingTest.kt new file mode 100644 index 0000000..4e6524f --- /dev/null +++ b/kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/RunBlockingTest.kt @@ -0,0 +1,10 @@ +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 = + GlobalScope.promise(context) { testBody(this) } diff --git a/kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/binding/AsyncSuspendableBindingTests.kt b/kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/binding/AsyncSuspendableBindingTests.kt new file mode 100644 index 0000000..24b4004 --- /dev/null +++ b/kotlin-result-coroutines/src/jsTest/kotlin/com.github.michaelbull.result.coroutines/binding/AsyncSuspendableBindingTests.kt @@ -0,0 +1,122 @@ +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.async +import kotlinx.coroutines.delay +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class AsyncSuspendableBindingTest { + + private sealed class BindingError { + object BindingErrorA : BindingError() + object BindingErrorB : BindingError() + } + + @Test + fun returnsOkIfAllBindsSuccessful(): dynamic { + suspend fun provideX(): Result { + delay(100) + return Ok(1) + } + + suspend fun provideY(): Result { + delay(100) + return Ok(2) + } + + return runBlockingTest { + val result = binding { + val x = async { provideX().bind() } + val y = async { provideY().bind() } + x.await() + y.await() + } + assertTrue(result is Ok) + assertEquals( + expected = 3, + actual = result.value + ) + } + } + + @Test + fun returnsFirstErrIfBindingFailed(): dynamic { + suspend fun provideX(): Result { + delay(10) + return Ok(1) + } + + suspend fun provideY(): Result { + delay(20) + return Err(BindingError.BindingErrorA) + } + + suspend fun provideZ(): Result { + delay(1) + return Err(BindingError.BindingErrorB) + } + + return runBlockingTest { + val result = binding { + 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 + ) + } + } + + @Test + fun returnsStateChangedForOnlyTheFirstAsyncBindFailWhenEagerlyCancellingBinding(): dynamic { + var xStateChange = false + var yStateChange = false + var zStateChange = false + suspend fun provideX(): Result { + delay(20) + xStateChange = true + return Ok(1) + } + + suspend fun provideY(): Result { + delay(10) + yStateChange = true + return Err(BindingError.BindingErrorA) + } + + suspend fun provideZ(): Result { + delay(1) + zStateChange = true + return Err(BindingError.BindingErrorB) + } + + return runBlockingTest { + val result = binding { + 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) + } + } +} diff --git a/kotlin-result/build.gradle.kts b/kotlin-result/build.gradle.kts index 09c226e..4dc9a4f 100644 --- a/kotlin-result/build.gradle.kts +++ b/kotlin-result/build.gradle.kts @@ -60,6 +60,14 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:${Versions.kotlinBenchmark}") } } + + val jsMain by getting + + val jsTest by getting { + dependencies { + implementation(kotlin("test-js")) + } + } } } diff --git a/kotlin-result/src/jsMain/kotlin/com/github/michaelbull/result/BindException.kt b/kotlin-result/src/jsMain/kotlin/com/github/michaelbull/result/BindException.kt new file mode 100644 index 0000000..1ea8893 --- /dev/null +++ b/kotlin-result/src/jsMain/kotlin/com/github/michaelbull/result/BindException.kt @@ -0,0 +1,3 @@ +package com.github.michaelbull.result + +internal actual object BindException : Exception()