From 6a4422528284bfce6a76832ec565428202f0238e Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Mena Date: Wed, 25 Oct 2023 09:16:29 +0200 Subject: [PATCH 01/11] Update all Gradle files to mention kotlin.test --- .../core/arrow-annotations/build.gradle.kts | 10 ------ arrow-libs/core/arrow-atomic/build.gradle.kts | 20 ++++------- .../core/arrow-core-retrofit/build.gradle.kts | 7 +++- .../arrow-core-serialization/build.gradle.kts | 9 +++-- arrow-libs/core/arrow-core/build.gradle.kts | 28 ++++++--------- .../core/arrow-functions/build.gradle.kts | 28 ++++++--------- .../fx/arrow-fx-coroutines/build.gradle.kts | 20 ++++------- arrow-libs/fx/arrow-fx-stm/build.gradle.kts | 4 +-- .../arrow-optics-ksp-plugin/build.gradle.kts | 10 +++--- .../kotlin/arrow/optics/plugin/CopyTest.kt | 2 +- .../kotlin/arrow/optics/plugin/DSLTests.kt | 2 +- .../kotlin/arrow/optics/plugin/IsoTests.kt | 2 +- .../kotlin/arrow/optics/plugin/LensTests.kt | 2 +- .../arrow/optics/plugin/OptionalTests.kt | 2 +- .../kotlin/arrow/optics/plugin/PrismTests.kt | 2 +- .../arrow-optics-reflect/build.gradle.kts | 10 +++--- .../kotlin/arrow/optics/ReflectionTest.kt | 15 ++++---- .../optics/arrow-optics/build.gradle.kts | 35 +++++-------------- .../arrow-resilience/build.gradle.kts | 9 +++-- 19 files changed, 88 insertions(+), 129 deletions(-) diff --git a/arrow-libs/core/arrow-annotations/build.gradle.kts b/arrow-libs/core/arrow-annotations/build.gradle.kts index 4b2e4fd5171..cbcaa791e5f 100644 --- a/arrow-libs/core/arrow-annotations/build.gradle.kts +++ b/arrow-libs/core/arrow-annotations/build.gradle.kts @@ -18,20 +18,10 @@ apply(from = property("ANIMALSNIFFER_MPP")) kotlin { sourceSets { commonMain { - dependencies { - implementation(libs.kotlin.stdlibCommon) - } - } - jvmMain { dependencies { implementation(libs.kotlin.stdlib) } } - jsMain { - dependencies { - implementation(libs.kotlin.stdlibJS) - } - } } jvm { diff --git a/arrow-libs/core/arrow-atomic/build.gradle.kts b/arrow-libs/core/arrow-atomic/build.gradle.kts index 4e2275757b3..f9e32f25a5d 100644 --- a/arrow-libs/core/arrow-atomic/build.gradle.kts +++ b/arrow-libs/core/arrow-atomic/build.gradle.kts @@ -22,7 +22,7 @@ kotlin { sourceSets { commonMain { dependencies { - api(libs.kotlin.stdlibCommon) + api(libs.kotlin.stdlib) } } @@ -30,21 +30,9 @@ kotlin { dependencies { implementation(projects.arrowFxCoroutines) implementation(libs.kotlin.test) + implementation(libs.coroutines.test) implementation(libs.kotest.assertionsCore) implementation(libs.kotest.property) - implementation(libs.coroutines.test) - } - } - - jvmMain { - dependencies { - implementation(libs.kotlin.stdlib) - } - } - - jsMain { - dependencies { - implementation(libs.kotlin.stdlibJS) } } } @@ -63,3 +51,7 @@ tasks.withType().configureEach { freeCompilerArgs = freeCompilerArgs + "-Xexpect-actual-classes" } } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/core/arrow-core-retrofit/build.gradle.kts b/arrow-libs/core/arrow-core-retrofit/build.gradle.kts index 92b0fa63f7c..82f1ef25e9e 100644 --- a/arrow-libs/core/arrow-core-retrofit/build.gradle.kts +++ b/arrow-libs/core/arrow-core-retrofit/build.gradle.kts @@ -4,7 +4,6 @@ plugins { id(libs.plugins.kotlin.jvm.get().pluginId) alias(libs.plugins.arrowGradleConfig.kotlin) alias(libs.plugins.arrowGradleConfig.publish) - alias(libs.plugins.kotlinx.serialization) // Needed for testing only alias(libs.plugins.kotlinx.kover) alias(libs.plugins.spotless) @@ -24,6 +23,8 @@ dependencies { compileOnly(libs.squareup.retrofit) testImplementation(projects.arrowCore) + testImplementation(libs.kotlin.test) + testImplementation(libs.coroutines.test) testImplementation(libs.kotest.frameworkEngine) testImplementation(libs.kotest.assertionsCore) testImplementation(libs.kotest.property) @@ -41,3 +42,7 @@ tasks.jar { attributes["Automatic-Module-Name"] = "arrow.core.retrofit" } } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/core/arrow-core-serialization/build.gradle.kts b/arrow-libs/core/arrow-core-serialization/build.gradle.kts index e635f691b12..58d852c31d0 100644 --- a/arrow-libs/core/arrow-core-serialization/build.gradle.kts +++ b/arrow-libs/core/arrow-core-serialization/build.gradle.kts @@ -4,7 +4,6 @@ plugins { id(libs.plugins.kotlin.multiplatform.get().pluginId) alias(libs.plugins.arrowGradleConfig.kotlin) alias(libs.plugins.arrowGradleConfig.publish) - alias(libs.plugins.kotest.multiplatform) id(libs.plugins.kotlinx.serialization.get().pluginId) } @@ -16,7 +15,7 @@ kotlin { commonMain { dependencies { api(projects.arrowCore) - api(libs.kotlin.stdlibCommon) + api(libs.kotlin.stdlib) api(libs.kotlinx.serializationCore) } } @@ -24,6 +23,8 @@ kotlin { commonTest { dependencies { implementation(libs.kotlinx.serializationJson) + implementation(libs.kotlin.test) + implementation(libs.coroutines.test) implementation(libs.kotest.frameworkEngine) implementation(libs.kotest.assertionsCore) implementation(libs.kotest.property) @@ -45,3 +46,7 @@ kotlin { } } } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/core/arrow-core/build.gradle.kts b/arrow-libs/core/arrow-core/build.gradle.kts index 3bec46f02a5..cb1e5ded6fd 100644 --- a/arrow-libs/core/arrow-core/build.gradle.kts +++ b/arrow-libs/core/arrow-core/build.gradle.kts @@ -25,7 +25,7 @@ kotlin { dependencies { api(projects.arrowAtomic) api(projects.arrowAnnotations) - api(libs.kotlin.stdlibCommon) + api(libs.kotlin.stdlib) } } @@ -33,29 +33,19 @@ kotlin { dependencies { implementation(projects.arrowFxCoroutines) implementation(projects.arrowFunctions) - implementation(libs.kotest.frameworkEngine) - implementation(libs.kotest.assertionsCore) - implementation(libs.kotest.property) - } + implementation(libs.kotlin.test) + implementation(libs.coroutines.test) + implementation(libs.kotest.frameworkEngine) + implementation(libs.kotest.assertionsCore) + implementation(libs.kotest.property) } + } jvmTest { dependencies { runtimeOnly(libs.kotest.runnerJUnit5) } } - - jvmMain { - dependencies { - implementation(libs.kotlin.stdlib) - } - } - - jsMain { - dependencies { - implementation(libs.kotlin.stdlibJS) - } - } } jvm { @@ -71,3 +61,7 @@ kotlin { tasks.named("compileTestKotlinJvm") { kotlinOptions.freeCompilerArgs += "-Xcontext-receivers" } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/core/arrow-functions/build.gradle.kts b/arrow-libs/core/arrow-functions/build.gradle.kts index acb0d490905..5437121c9ba 100644 --- a/arrow-libs/core/arrow-functions/build.gradle.kts +++ b/arrow-libs/core/arrow-functions/build.gradle.kts @@ -26,35 +26,25 @@ kotlin { dependencies { api(projects.arrowAtomic) api(projects.arrowAnnotations) - api(libs.kotlin.stdlibCommon) + api(libs.kotlin.stdlib) } } commonTest { dependencies { implementation(projects.arrowFxCoroutines) - implementation(libs.kotest.frameworkEngine) - implementation(libs.kotest.assertionsCore) - implementation(libs.kotest.property) - } + implementation(libs.kotlin.test) + implementation(libs.coroutines.test) + implementation(libs.kotest.frameworkEngine) + implementation(libs.kotest.assertionsCore) + implementation(libs.kotest.property) } + } jvmTest { dependencies { runtimeOnly(libs.kotest.runnerJUnit5) } } - - jvmMain { - dependencies { - implementation(libs.kotlin.stdlib) - } - } - - jsMain { - dependencies { - implementation(libs.kotlin.stdlibJS) - } - } } jvm { @@ -65,3 +55,7 @@ kotlin { } } } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/fx/arrow-fx-coroutines/build.gradle.kts b/arrow-libs/fx/arrow-fx-coroutines/build.gradle.kts index 7564b54ed64..dd17fa7f888 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/build.gradle.kts +++ b/arrow-libs/fx/arrow-fx-coroutines/build.gradle.kts @@ -22,7 +22,7 @@ kotlin { dependencies { api(projects.arrowCore) api(libs.coroutines.core) - implementation(libs.kotlin.stdlibCommon) + implementation(libs.kotlin.stdlib) } } @@ -30,22 +30,10 @@ kotlin { dependencies { implementation(projects.arrowCore) implementation(libs.kotlin.test) + implementation(libs.coroutines.test) implementation(libs.kotest.frameworkEngine) implementation(libs.kotest.assertionsCore) implementation(libs.kotest.property) - implementation(libs.coroutines.test) - } - } - - jvmMain { - dependencies { - implementation(libs.kotlin.stdlib) - } - } - - jsMain { - dependencies { - implementation(libs.kotlin.stdlibJS) } } } @@ -58,3 +46,7 @@ kotlin { } } } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/fx/arrow-fx-stm/build.gradle.kts b/arrow-libs/fx/arrow-fx-stm/build.gradle.kts index d8448e947e5..22f7eff35a7 100644 --- a/arrow-libs/fx/arrow-fx-stm/build.gradle.kts +++ b/arrow-libs/fx/arrow-fx-stm/build.gradle.kts @@ -28,10 +28,10 @@ kotlin { commonTest { dependencies { implementation(projects.arrowFxCoroutines) - implementation(libs.kotest.assertionsCore) - implementation(libs.kotest.property) implementation(libs.kotlin.test) implementation(libs.coroutines.test) + implementation(libs.kotest.assertionsCore) + implementation(libs.kotest.property) } } } diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/build.gradle.kts b/arrow-libs/optics/arrow-optics-ksp-plugin/build.gradle.kts index be7c960620a..cff4ecbb9e3 100644 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/build.gradle.kts +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/build.gradle.kts @@ -24,8 +24,7 @@ dependencies { implementation(libs.ksp) testImplementation(libs.kotlin.stdlib) - testImplementation(libs.junitJupiter) - testImplementation(libs.junitJupiterEngine) + testImplementation(libs.kotlin.test) testImplementation(libs.assertj) testImplementation(libs.classgraph) testImplementation(libs.kotlinCompileTesting) { @@ -45,8 +44,7 @@ dependencies { testRuntimeOnly(projects.arrowOptics) } -tasks { - withType().configureEach { - maxParallelForks = 1 - } +tasks.withType { + maxParallelForks = 1 + useJUnitPlatform() } diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/CopyTest.kt b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/CopyTest.kt index b9cf3ef208d..76084dd7ff9 100644 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/CopyTest.kt +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/CopyTest.kt @@ -1,6 +1,6 @@ package arrow.optics.plugin -import org.junit.jupiter.api.Test +import kotlin.test.Test // from https://kotlinlang.slack.com/archives/C5UPMM0A0/p1688822411819599 // and https://github.com/overfullstack/my-lab/blob/master/arrow/src/test/kotlin/ga/overfullstack/optics/OpticsLab.kt diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/DSLTests.kt b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/DSLTests.kt index 24798faaaa0..84cde1249e1 100755 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/DSLTests.kt +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/DSLTests.kt @@ -1,6 +1,6 @@ package arrow.optics.plugin -import org.junit.jupiter.api.Test +import kotlin.test.Test class DSLTests { diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/IsoTests.kt b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/IsoTests.kt index 793ce9167a5..0abf877cc4a 100644 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/IsoTests.kt +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/IsoTests.kt @@ -1,7 +1,7 @@ package arrow.optics.plugin import arrow.optics.plugin.internals.noCompanion -import org.junit.jupiter.api.Test +import kotlin.test.Test class IsoTests { diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/LensTests.kt b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/LensTests.kt index 4eb45199c16..5b4fd2fb9ba 100755 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/LensTests.kt +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/LensTests.kt @@ -1,6 +1,6 @@ package arrow.optics.plugin -import org.junit.jupiter.api.Test +import kotlin.test.Test class LensTests { diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/OptionalTests.kt b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/OptionalTests.kt index 8244cf122a0..5aa64d19b23 100755 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/OptionalTests.kt +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/OptionalTests.kt @@ -1,6 +1,6 @@ package arrow.optics.plugin -import org.junit.jupiter.api.Test +import kotlin.test.Test class OptionalTests { diff --git a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/PrismTests.kt b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/PrismTests.kt index 084865f0045..1b5dcec76d6 100755 --- a/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/PrismTests.kt +++ b/arrow-libs/optics/arrow-optics-ksp-plugin/src/test/kotlin/arrow/optics/plugin/PrismTests.kt @@ -1,6 +1,6 @@ package arrow.optics.plugin -import org.junit.jupiter.api.Test +import kotlin.test.Test class PrismTests { diff --git a/arrow-libs/optics/arrow-optics-reflect/build.gradle.kts b/arrow-libs/optics/arrow-optics-reflect/build.gradle.kts index 13e73874df1..202c9d680e7 100644 --- a/arrow-libs/optics/arrow-optics-reflect/build.gradle.kts +++ b/arrow-libs/optics/arrow-optics-reflect/build.gradle.kts @@ -23,13 +23,11 @@ dependencies { implementation(libs.kotlin.stdlib) testImplementation(libs.kotlin.stdlib) - testImplementation(libs.junitJupiterEngine) + testImplementation(libs.kotlin.test) + testImplementation(libs.coroutines.test) testImplementation(libs.kotlin.reflect) - - testImplementation(libs.kotest.frameworkEngine) testImplementation(libs.kotest.assertionsCore) testImplementation(libs.kotest.property) - testRuntimeOnly(libs.kotest.runnerJUnit5) } tasks.jar { @@ -37,3 +35,7 @@ tasks.jar { attributes["Automatic-Module-Name"] = "arrow.optics.reflect" } } + +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/optics/arrow-optics-reflect/src/test/kotlin/arrow/optics/ReflectionTest.kt b/arrow-libs/optics/arrow-optics-reflect/src/test/kotlin/arrow/optics/ReflectionTest.kt index 6aa38211fda..2fa27e1de8a 100644 --- a/arrow-libs/optics/arrow-optics-reflect/src/test/kotlin/arrow/optics/ReflectionTest.kt +++ b/arrow-libs/optics/arrow-optics-reflect/src/test/kotlin/arrow/optics/ReflectionTest.kt @@ -1,11 +1,12 @@ package arrow.optics -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.string import io.kotest.property.checkAll +import kotlinx.coroutines.test.runTest +import kotlin.test.Test data class Person(val name: String, val friends: List) @@ -13,15 +14,15 @@ sealed interface Cutlery object Fork : Cutlery object Spoon : Cutlery -object ReflectionTest : StringSpec({ - "lenses for field, get" { +class ReflectionTest { + @Test fun lensesForFieldGet() = runTest { checkAll(Arb.string(), Arb.list(Arb.string())) { nm, fs -> val p = Person(nm, fs.toMutableList()) Person::name.lens.get(p) shouldBe nm } } - "lenses for field, set" { + @Test fun lensesForFieldSet() = runTest { checkAll(Arb.string(), Arb.list(Arb.string())) { nm, fs -> val p = Person(nm, fs.toMutableList()) val m = Person::name.lens.modify(p) { it.capitalize() } @@ -29,7 +30,7 @@ object ReflectionTest : StringSpec({ } } - "traversal for list, set" { + @Test fun traversalForListSet() = runTest { checkAll(Arb.string(), Arb.list(Arb.string())) { nm, fs -> val p = Person(nm, fs) val m = Person::friends.every.modify(p) { it.capitalize() } @@ -37,11 +38,11 @@ object ReflectionTest : StringSpec({ } } - "instances" { + @Test fun instances() = runTest { val things = listOf(Fork, Spoon, Fork) val forks = Every.list() compose instance() val spoons = Every.list() compose instance() forks.size(things) shouldBe 2 spoons.size(things) shouldBe 1 } -}) +} diff --git a/arrow-libs/optics/arrow-optics/build.gradle.kts b/arrow-libs/optics/arrow-optics/build.gradle.kts index 55e7b9b05f3..e7a2843075a 100644 --- a/arrow-libs/optics/arrow-optics/build.gradle.kts +++ b/arrow-libs/optics/arrow-optics/build.gradle.kts @@ -22,38 +22,34 @@ kotlin { commonMain { dependencies { api(projects.arrowCore) - api(libs.kotlin.stdlibCommon) + api(libs.kotlin.stdlib) } } commonTest { dependencies { + implementation(libs.kotlin.test) + implementation(libs.coroutines.test) implementation(libs.kotest.frameworkEngine) implementation(libs.kotest.assertionsCore) implementation(libs.kotest.property) } } + jvmTest { dependencies { implementation(libs.kotlin.stdlib) implementation(libs.kotest.frameworkEngine) - implementation(libs.junitJupiterEngine) - implementation(libs.kotlin.reflect) - + implementation(libs.junitJupiterEngine) + implementation(libs.kotlin.reflect) } } jvmMain { dependencies { - implementation(libs.kotlin.stdlib) api(libs.kotlin.reflect) } } - jsMain { - dependencies { - implementation(libs.kotlin.stdlibJS) - } - } } jvm { @@ -65,19 +61,6 @@ kotlin { } } -//fun DependencyHandlerScope.kspTest(dependencyNotation: Any): Unit { -// val exclude = setOf("commonTest", "nativeTest") -// add("kspMetadata", dependencyNotation) -// kotlin.sourceSets -// .filter { it.name !in exclude && it.name.contains("Test") } -// .forEach { -// val task = "ksp${it.name.capitalize()}" -// configurations.findByName(task)?.let { -// add(task, dependencyNotation) -// } -// } -//} - -//dependencies { -// kspTest(projects.arrowOpticsKspPlugin) -//} +tasks.withType { + useJUnitPlatform() +} diff --git a/arrow-libs/resilience/arrow-resilience/build.gradle.kts b/arrow-libs/resilience/arrow-resilience/build.gradle.kts index ba9d3ca78a8..4517c8504a9 100644 --- a/arrow-libs/resilience/arrow-resilience/build.gradle.kts +++ b/arrow-libs/resilience/arrow-resilience/build.gradle.kts @@ -19,16 +19,15 @@ kotlin { commonMain { dependencies { api(projects.arrowCore) - compileOnly(libs.kotlin.stdlibCommon) + compileOnly(libs.kotlin.stdlib) implementation(libs.coroutines.core) - implementation("org.jetbrains.kotlin:kotlin-stdlib") } } commonTest { dependencies { implementation(projects.arrowFxCoroutines) + implementation(libs.kotlin.test) implementation(libs.coroutines.test) - implementation(kotlin("test")) } } } @@ -41,3 +40,7 @@ kotlin { } } } + +tasks.withType { + useJUnitPlatform() +} From 686e6048875ce432ef605988937bdc06cce278fe Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Wed, 25 Oct 2023 09:38:08 +0200 Subject: [PATCH 02/11] Replace uses of deprecated `kotlin.native.concurrent.AtomicReference` (#3241) --- .../kotlin/arrow.continuations/generic/AtomicRef.kt | 4 ++-- .../nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arrow-libs/core/arrow-continuations/src/nativeMain/kotlin/arrow.continuations/generic/AtomicRef.kt b/arrow-libs/core/arrow-continuations/src/nativeMain/kotlin/arrow.continuations/generic/AtomicRef.kt index 36d201b6cbc..9b3aaef946b 100644 --- a/arrow-libs/core/arrow-continuations/src/nativeMain/kotlin/arrow.continuations/generic/AtomicRef.kt +++ b/arrow-libs/core/arrow-continuations/src/nativeMain/kotlin/arrow.continuations/generic/AtomicRef.kt @@ -1,6 +1,6 @@ package arrow.continuations.generic -import kotlin.native.concurrent.AtomicReference +import kotlin.concurrent.AtomicReference import kotlin.native.concurrent.freeze import kotlin.native.concurrent.isFrozen @@ -18,7 +18,7 @@ public actual class AtomicRef actual constructor(initialValue: V) { while (true) { val cur = atom.value if (cur === value) return cur - if (atom.compareAndSwap(cur, value) === cur) return cur + if (atom.compareAndExchange(cur, value) === cur) return cur } } diff --git a/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt b/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt index bc63616ad47..952d794b751 100644 --- a/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt +++ b/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt @@ -1,6 +1,6 @@ package arrow.core.continuations -import kotlin.native.concurrent.AtomicReference +import kotlin.concurrent.AtomicReference import kotlin.native.concurrent.freeze import kotlin.native.concurrent.isFrozen @@ -17,7 +17,7 @@ public actual class AtomicRef actual constructor(initialValue: V) { while (true) { val cur = atom.value if (cur === value) return cur - if (atom.compareAndSwap(cur, value) === cur) return cur + if (atom.compareAndExchange(cur, value) === cur) return cur } } From 335bd90a045414cb3c68bedc14cc4a321ff38393 Mon Sep 17 00:00:00 2001 From: Andreas Storesund Madsen Date: Thu, 26 Oct 2023 14:23:09 +0200 Subject: [PATCH 03/11] Refactor ParZip2Test from Kotest Plugin to Kotlin-test runtime #3192 (#3222) Co-authored-by: Alejandro Serrano --- .../kotlin/arrow/fx/coroutines/ParZip2Test.kt | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip2Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip2Test.kt index 2f090154ed1..33b2b0f31e9 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip2Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip2Test.kt @@ -1,6 +1,6 @@ package arrow.fx.coroutines.parZip -import arrow.atomic.Atomic +import arrow.atomic.AtomicInt import arrow.atomic.update import arrow.atomic.value import arrow.core.Either @@ -10,7 +10,6 @@ import arrow.fx.coroutines.guaranteeCase import arrow.fx.coroutines.leftException import arrow.fx.coroutines.parZip import arrow.fx.coroutines.throwable -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.should import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeTypeOf @@ -25,93 +24,94 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.test.runTest +import kotlin.test.Test -class ParZip2Test : StringSpec({ - "parZip 2 runs in parallel" { - checkAll(Arb.int(), Arb.int()) { a, b -> - val r = Atomic("") - val modifyGate = CompletableDeferred() +class ParZip2Test { + @Test fun parZip2RunsInParallel() = runTest { + checkAll(Arb.int(), Arb.int()) { a, b -> + val r = AtomicInt(0) + val modifyGate = CompletableDeferred() - parZip( - { - modifyGate.await() - r.update { i -> "$i$a" } - }, - { - r.value = "$b" - modifyGate.complete(0) - } - ) { _a, _b -> - Pair(_a, _b) + parZip( + { + modifyGate.await() + r.update { i -> i + a } + }, + { + r.value = b + modifyGate.complete(0) } - - r.value shouldBe "$b$a" + ) { _a, _b -> + Pair(_a, _b) } + + r.value shouldBe b + a } + } - "Cancelling parZip 2 cancels all participants" { - checkAll(Arb.int(), Arb.int()) { a, b -> - val s = Channel() - val pa = CompletableDeferred>() - val pb = CompletableDeferred>() + @Test fun cancellingParZip2CancelsAllParticipants() = runTest { + checkAll(Arb.int(), Arb.int()) { a, b -> + val s = Channel() + val pa = CompletableDeferred>() + val pb = CompletableDeferred>() - val loserA: suspend CoroutineScope.() -> Int = - { guaranteeCase({ s.receive(); awaitCancellation() }) { ex -> pa.complete(Pair(a, ex)) } } - val loserB: suspend CoroutineScope.() -> Int = - { guaranteeCase({ s.receive(); awaitCancellation() }) { ex -> pb.complete(Pair(b, ex)) } } + val loserA: suspend CoroutineScope.() -> Int = + { guaranteeCase({ s.receive(); awaitCancellation() }) { ex -> pa.complete(Pair(a, ex)) } } + val loserB: suspend CoroutineScope.() -> Int = + { guaranteeCase({ s.receive(); awaitCancellation() }) { ex -> pb.complete(Pair(b, ex)) } } - val f = async { parZip(loserA, loserB) { _a, _b -> Pair(_a, _b) } } + val f = async { parZip(loserA, loserB) { _a, _b -> Pair(_a, _b) } } - s.send(Unit) // Suspend until all racers started - s.send(Unit) - f.cancel() + s.send(Unit) // Suspend until all racers started + s.send(Unit) + f.cancel() - pa.await().let { (res, exit) -> - res shouldBe a - exit.shouldBeTypeOf() - } - pb.await().let { (res, exit) -> - res shouldBe b - exit.shouldBeTypeOf() - } + pa.await().let { (res, exit) -> + res shouldBe a + exit.shouldBeTypeOf() + } + pb.await().let { (res, exit) -> + res shouldBe b + exit.shouldBeTypeOf() } } + } - "parZip 2 cancels losers if a failure occurs in one of the tasks" { - checkAll(Arb.throwable(), Arb.boolean()) { e, leftWinner -> - val s = Channel() - val pa = CompletableDeferred() - - val winner: suspend CoroutineScope.() -> Unit = { s.send(Unit); throw e } - val loserA: suspend CoroutineScope.() -> Int = - { guaranteeCase({ s.receive(); awaitCancellation() }) { ex -> pa.complete(ex) } } + @Test fun parZip2CancelsLosersIfAFailtureOccursInOneOfTheTasts() = runTest { + checkAll(Arb.throwable(), Arb.boolean()) { e, leftWinner -> + val s = Channel() + val pa = CompletableDeferred() - val r = Either.catch { - if (leftWinner) parZip(winner, loserA) { _, _ -> Unit } - else parZip(loserA, winner) { _, _ -> Unit } - } + val winner: suspend CoroutineScope.() -> Unit = { s.send(Unit); throw e } + val loserA: suspend CoroutineScope.() -> Int = + { guaranteeCase({ s.receive(); awaitCancellation() }) { ex -> pa.complete(ex) } } - pa.await().shouldBeTypeOf() - r should leftException(e) + val r = Either.catch { + if (leftWinner) parZip(winner, loserA) { _, _ -> Unit } + else parZip(loserA, winner) { _, _ -> Unit } } + + pa.await().shouldBeTypeOf() + r should leftException(e) } + } - "parZip CancellationException on right can cancel rest" { - checkAll(Arb.string()) { msg -> - val exit = CompletableDeferred() - val start = CompletableDeferred() - try { - parZip({ - awaitExitCase(start, exit) - }, { - start.await() - throw CancellationException(msg) - }) { _, _ -> } - } catch (e: CancellationException) { - e.message shouldBe msg - } - exit.await().shouldBeTypeOf() + @Test fun parZipCancellationExceptionOnRightCanCancelRest() = runTest { + checkAll(Arb.string()) { msg -> + val exit = CompletableDeferred() + val start = CompletableDeferred() + try { + parZip({ + awaitExitCase(start, exit) + }, { + start.await() + throw CancellationException(msg) + }) { _, _ -> } + } catch (e: CancellationException) { + e.message shouldBe msg } + exit.await().shouldBeTypeOf() } } -) +} From edd0dd50fce7fb143a7a3de1bf47926066f7ae57 Mon Sep 17 00:00:00 2001 From: Andreas Storesund Madsen Date: Thu, 26 Oct 2023 14:23:35 +0200 Subject: [PATCH 04/11] Refactor ParZip3Test from Kotest Plugin to Kotlin-test runtime #3193 (#3223) Co-authored-by: Alejandro Serrano --- .../kotlin/arrow/fx/coroutines/ParZip3Test.kt | 194 +++++++++--------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip3Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip3Test.kt index bf1bc1c3e5e..1e2d05ff4c0 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip3Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParZip3Test.kt @@ -9,7 +9,6 @@ import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException import arrow.fx.coroutines.parZip import arrow.fx.coroutines.throwable -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.should import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeTypeOf @@ -23,112 +22,113 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.async import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.CoroutineScope - -class ParZip3Test : StringSpec({ - "parZip 3 runs in parallel" { - checkAll(Arb.int(), Arb.int(), Arb.int()) { a, b, c -> - val r = Atomic("") - val modifyGate1 = CompletableDeferred() - val modifyGate2 = CompletableDeferred() - - parZip( - { - modifyGate2.await() - r.update { i -> "$i$a" } - }, - { - modifyGate1.await() - r.update { i -> "$i$b" } - modifyGate2.complete(Unit) - }, - { - r.value ="$c" - modifyGate1.complete(Unit) - } - ) { _a, _b, _c -> - Triple(_a, _b, _c) +import kotlinx.coroutines.test.runTest +import kotlin.test.Test + +class ParZip3Test { + @Test fun parZip3RunsInParallel() = runTest { + checkAll(Arb.int(), Arb.int(), Arb.int()) { a, b, c -> + val r = Atomic("") + val modifyGate1 = CompletableDeferred() + val modifyGate2 = CompletableDeferred() + + parZip( + { + modifyGate2.await() + r.update { i -> "$i$a" } + }, + { + modifyGate1.await() + r.update { i -> "$i$b" } + modifyGate2.complete(Unit) + }, + { + r.value ="$c" + modifyGate1.complete(Unit) } - - r.value shouldBe "$c$b$a" + ) { _a, _b, _c -> + Triple(_a, _b, _c) } - } - "Cancelling parZip 3 cancels all participants" { - val s = Channel() - val pa = CompletableDeferred() - val pb = CompletableDeferred() - val pc = CompletableDeferred() - - val loserA: suspend CoroutineScope.() -> Int = - { awaitExitCase(s, pa) } - val loserB: suspend CoroutineScope.() -> Int = - { awaitExitCase(s, pb) } - val loserC: suspend CoroutineScope.() -> Int = - { awaitExitCase(s, pc) } - - val f = async { parZip(loserA, loserB, loserC) { _a, _b, _c -> Triple(_a, _b, _c) } } - - s.send(Unit) // Suspend until all racers started - s.send(Unit) - s.send(Unit) - f.cancel() - - pa.await().shouldBeTypeOf() - pb.await().shouldBeTypeOf() - pc.await().shouldBeTypeOf() + r.value shouldBe "$c$b$a" } + } - "parZip 3 cancels losers if a failure occurs in one of the tasks" { - checkAll( - Arb.throwable(), - Arb.element(listOf(1, 2, 3)), - ) { e, winningTask -> - val s = Channel() - val pa = CompletableDeferred() - val pb = CompletableDeferred() - - val winner: suspend CoroutineScope.() -> Int = { s.send(Unit); s.send(Unit); throw e } - val loserA: suspend CoroutineScope.() -> Int = - { awaitExitCase(s, pa) } - val loserB: suspend CoroutineScope.() -> Int = - { awaitExitCase(s, pb) } - - val r = Either.catch { - when (winningTask) { - 1 -> parZip(winner, loserA, loserB) { _, _, _ -> } - 2 -> parZip(loserA, winner, loserB) { _, _, _ -> } - else -> parZip(loserA, loserB, winner) { _, _, _ -> } - } - } + @Test fun cancellingParZip3CancelsAllParticipants() = runTest { + val s = Channel() + val pa = CompletableDeferred() + val pb = CompletableDeferred() + val pc = CompletableDeferred() + + val loserA: suspend CoroutineScope.() -> Int = + { awaitExitCase(s, pa) } + val loserB: suspend CoroutineScope.() -> Int = + { awaitExitCase(s, pb) } + val loserC: suspend CoroutineScope.() -> Int = + { awaitExitCase(s, pc) } + + val f = async { parZip(loserA, loserB, loserC) { _a, _b, _c -> Triple(_a, _b, _c) } } + + s.send(Unit) // Suspend until all racers started + s.send(Unit) + s.send(Unit) + f.cancel() + + pa.await().shouldBeTypeOf() + pb.await().shouldBeTypeOf() + pc.await().shouldBeTypeOf() + } - pa.await().shouldBeTypeOf() - pb.await().shouldBeTypeOf() - r should leftException(e) + @Test fun parZip3CancelsLosersIfAFailureOccursInOneOfTheTasks() = runTest { + checkAll( + Arb.throwable(), + Arb.element(listOf(1, 2, 3)), + ) { e, winningTask -> + val s = Channel() + val pa = CompletableDeferred() + val pb = CompletableDeferred() + + val winner: suspend CoroutineScope.() -> Int = { s.send(Unit); s.send(Unit); throw e } + val loserA: suspend CoroutineScope.() -> Int = + { awaitExitCase(s, pa) } + val loserB: suspend CoroutineScope.() -> Int = + { awaitExitCase(s, pb) } + + val r = Either.catch { + when (winningTask) { + 1 -> parZip(winner, loserA, loserB) { _, _, _ -> } + 2 -> parZip(loserA, winner, loserB) { _, _, _ -> } + else -> parZip(loserA, loserB, winner) { _, _, _ -> } + } } + + pa.await().shouldBeTypeOf() + pb.await().shouldBeTypeOf() + r should leftException(e) } + } - "parZip CancellationException on right can cancel rest" { - checkAll(Arb.string(), Arb.int(1..3)) { msg, cancel -> - val s = Channel() - val pa = CompletableDeferred() - val pb = CompletableDeferred() - - val winner: suspend CoroutineScope.() -> Int = { repeat(2) { s.send(Unit) }; throw CancellationException(msg) } - val loserA: suspend CoroutineScope.() -> Int = { awaitExitCase(s, pa) } - val loserB: suspend CoroutineScope.() -> Int = { awaitExitCase(s, pb) } - - try { - when (cancel) { - 1 -> parZip(winner, loserA, loserB) { _, _, _ -> } - 2 -> parZip(loserA, winner, loserB) { _, _, _ -> } - else -> parZip(loserA, loserB, winner) { _, _, _ -> } - } - } catch (e: CancellationException) { - e.message shouldBe msg + @Test fun parZipCancellationExceptionOnRightCanCancelRest() = runTest { + checkAll(Arb.string(), Arb.int(1..3)) { msg, cancel -> + val s = Channel() + val pa = CompletableDeferred() + val pb = CompletableDeferred() + + val winner: suspend CoroutineScope.() -> Int = { repeat(2) { s.send(Unit) }; throw CancellationException(msg) } + val loserA: suspend CoroutineScope.() -> Int = { awaitExitCase(s, pa) } + val loserB: suspend CoroutineScope.() -> Int = { awaitExitCase(s, pb) } + + try { + when (cancel) { + 1 -> parZip(winner, loserA, loserB) { _, _, _ -> } + 2 -> parZip(loserA, winner, loserB) { _, _, _ -> } + else -> parZip(loserA, loserB, winner) { _, _, _ -> } } - pa.await().shouldBeTypeOf() - pb.await().shouldBeTypeOf() + } catch (e: CancellationException) { + e.message shouldBe msg } + pa.await().shouldBeTypeOf() + pb.await().shouldBeTypeOf() } } -) +} From f384c5de65e2e86a669fb118b728cc425b1ddbd1 Mon Sep 17 00:00:00 2001 From: Jonathan Lagneaux Date: Thu, 26 Oct 2023 14:24:24 +0200 Subject: [PATCH 05/11] Refactor GuaranteeCaseTest to use kotlin test (#3226) Closes #3190 --- .../arrow/fx/coroutines/GuaranteeCaseTest.kt | 83 ++++++++++--------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/GuaranteeCaseTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/GuaranteeCaseTest.kt index 2f636a7b381..1795c98fc0f 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/GuaranteeCaseTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/GuaranteeCaseTest.kt @@ -1,7 +1,6 @@ package arrow.fx.coroutines import arrow.core.Either -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf import io.kotest.property.Arb @@ -10,55 +9,59 @@ import io.kotest.property.checkAll import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.async import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.test.runTest +import kotlin.test.Test -class GuaranteeCaseTest : StringSpec({ +class GuaranteeCaseTest { - "release for success was invoked" { - checkAll(Arb.int()) { i -> - val p = CompletableDeferred() - - val res = guaranteeCase( - fa = { i }, - finalizer = { ex -> require(p.complete(ex)) } - ) - - p.await() shouldBe ExitCase.Completed - res shouldBe i - } - } + @Test + fun releaseForSuccessWasInvoked() = runTest { + checkAll(Arb.int()) { i -> + val p = CompletableDeferred() - "release for error was invoked" { - checkAll(Arb.throwable()) { e -> - val p = CompletableDeferred() - val attempted = Either.catch { - guaranteeCase( - fa = { throw e }, - finalizer = { ex -> require(p.complete(ex)) } - ) - } + val res = guaranteeCase( + fa = { i }, + finalizer = { ex -> require(p.complete(ex)) } + ) - p.await() shouldBe ExitCase.Failure(e) - attempted shouldBe Either.Left(e) - } + p.await() shouldBe ExitCase.Completed + res shouldBe i } + } - "release for never was invoked" { + @Test + fun releaseForErrorWasInvoked() = runTest { + checkAll(Arb.throwable()) { e -> val p = CompletableDeferred() - val start = CompletableDeferred() - - val fiber = async { - guaranteeCase( - fa = { - start.complete(Unit) - awaitCancellation() - }, + val attempted = Either.catch { + guaranteeCase( + fa = { throw e }, finalizer = { ex -> require(p.complete(ex)) } ) } - start.await() - fiber.cancel() - p.await().shouldBeInstanceOf() + p.await() shouldBe ExitCase.Failure(e) + attempted shouldBe Either.Left(e) } } -) + + @Test + fun releaseForNeverWasInvoked() = runTest { + val p = CompletableDeferred() + val start = CompletableDeferred() + + val fiber = async { + guaranteeCase( + fa = { + start.complete(Unit) + awaitCancellation() + }, + finalizer = { ex -> require(p.complete(ex)) } + ) + } + + start.await() + fiber.cancel() + p.await().shouldBeInstanceOf() + } +} From 67f9c0f1a89e7440111bdbb76e1e4d6e8e349441 Mon Sep 17 00:00:00 2001 From: Marcus Ilgner Date: Thu, 26 Oct 2023 14:24:42 +0200 Subject: [PATCH 06/11] refactor: migrate NotEmptySetTest to kotlin-test (#3230) Co-authored-by: Alejandro Serrano --- .../kotlin/arrow/core/NonEmptySetTest.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt index f90df187bec..9c50b8779fb 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt @@ -2,7 +2,6 @@ package arrow.core import arrow.core.test.nonEmptySet import io.kotest.assertions.withClue -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -10,36 +9,38 @@ import io.kotest.property.Arb import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.next import io.kotest.property.checkAll +import kotlin.test.Test +import kotlinx.coroutines.test.runTest -class NonEmptySetTest : StringSpec({ +class NonEmptySetTest { - "iterable.toNonEmptySetOrNull should round trip" { + @Test fun iterableToNonEmptySetOrNullShouldRoundTrip() = runTest { checkAll(Arb.nonEmptySet(Arb.int())) { nonEmptySet -> nonEmptySet.toNonEmptySetOrNull().shouldNotBeNull() shouldBe nonEmptySet } } - "iterable.toNonEmptySetOrNone should round trip" { + @Test fun iterableToNonEmptySetOrNoneShouldRoundTrip() = runTest { checkAll(Arb.nonEmptySet(Arb.int())) { nonEmptySet -> nonEmptySet.toNonEmptySetOrNone() shouldBe nonEmptySet.some() } } - "emptyList.toNonEmptySetOrNull should be null" { + @Test fun emptyListToNonEmptySetOrNullShouldBeNull() = runTest { listOf().toNonEmptySetOrNull() shouldBe null } - "emptyList.toNonEmptySetOrNone should be none" { + @Test fun emptyListToNonEmptySetOrNoneShouldBeNone() = runTest { listOf().toNonEmptySetOrNone() shouldBe none() } - "adding an element already present doesn't change the set" { + @Test fun addingAnElementAlreadyPresentDoesNotChangeTheSet() = runTest { val element = Arb.int().next() val initialSet: NonEmptySet = nonEmptySetOf(element) + Arb.nonEmptySet(Arb.int()).next() initialSet.plus(element) shouldBe initialSet } - "NonEmptySet equals Set" { + @Test fun nonEmptySetEqualsSet() = runTest { checkAll( Arb.nonEmptySet(Arb.int()) ) { nes -> @@ -51,7 +52,7 @@ class NonEmptySetTest : StringSpec({ } } - "NonEmptySet equals NonEmptySet" { + @Test fun nonEmptySetEqualsNonEmptySet() = runTest { checkAll( Arb.nonEmptySet(Arb.int()) ) { nes -> @@ -62,5 +63,5 @@ class NonEmptySetTest : StringSpec({ } } } -}) +} From 72aabc2375c425849adb79d88bb444f156424f19 Mon Sep 17 00:00:00 2001 From: Marcus Ilgner Date: Thu, 26 Oct 2023 14:25:04 +0200 Subject: [PATCH 07/11] refactor: migrate EagerEffectSpec to kotlin-test (#3233) Co-authored-by: Alejandro Serrano --- .../arrow/core/raise/EagerEffectSpec.kt | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EagerEffectSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EagerEffectSpec.kt index 1ab81b4c351..80e5881c9d3 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EagerEffectSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EagerEffectSpec.kt @@ -18,11 +18,13 @@ import io.kotest.property.arbitrary.long import io.kotest.property.arbitrary.orNull import io.kotest.property.arbitrary.string import io.kotest.property.checkAll +import kotlin.test.Test import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.test.runTest @Suppress("UNREACHABLE_CODE", "UNUSED_EXPRESSION") -class EagerEffectSpec : StringSpec({ - "try/catch - can recover from raise" { +class EagerEffectSpec { + @Test fun tryCatchCanRecoverFromRaise() = runTest { checkAll(Arb.int(), Arb.string()) { i, s -> eagerEffect { try { @@ -34,7 +36,7 @@ class EagerEffectSpec : StringSpec({ } } - "try/catch - finally works" { + @Test fun tryCatchFinallyWorks() = runTest { checkAll(Arb.string(), Arb.int()) { s, i -> val promise = CompletableDeferred() eagerEffect { @@ -49,7 +51,7 @@ class EagerEffectSpec : StringSpec({ } } - "try/catch - First raise is ignored and second is returned" { + @Test fun tryCatchFirstRaiseIsIgnoredAndSecondIsReturned() = runTest { checkAll(Arb.int(), Arb.string(), Arb.string()) { i, s, s2 -> eagerEffect { try { @@ -62,7 +64,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - catch" { + @Test fun recoverCatch() = runTest { checkAll(Arb.int(), Arb.long()) { i, l -> eagerEffect { eagerEffect { @@ -75,7 +77,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - no catch" { + @Test fun recoverNoCatch() = runTest { checkAll(Arb.int(), Arb.long()) { i, l -> eagerEffect { eagerEffect { @@ -88,7 +90,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - raise from catch" { + @Test fun recoverRaiseFromCatch() = runTest { checkAll(Arb.long(), Arb.string()) { l, error -> eagerEffect { eagerEffect { @@ -101,18 +103,18 @@ class EagerEffectSpec : StringSpec({ } } - "success" { + @Test fun success() = runTest { eagerEffect { 1 } .fold({ unreachable() }, ::identity) shouldBe 1 } - "short-circuit" { + @Test fun shortCircuit() = runTest { eagerEffect { raise("hello") }.fold(::identity) { unreachable() } shouldBe "hello" } - "Rethrows exceptions" { + @Test fun rethrowsExceptions() = runTest { val e = RuntimeException("test") Either.catch { eagerEffect { throw e } @@ -120,7 +122,7 @@ class EagerEffectSpec : StringSpec({ } shouldBe Either.Left(e) } - "ensure null in eager either computation" { + @Test fun ensureNullInEagerEitherComputation() = runTest { checkAll(Arb.boolean(), Arb.int(), Arb.string()) { predicate, success, raise -> either { ensure(predicate) { raise } @@ -129,7 +131,7 @@ class EagerEffectSpec : StringSpec({ } } - "ensureNotNull in eager either computation" { + @Test fun ensureNotNullInEagerEitherComputation() = runTest { fun square(i: Int): Int = i * i checkAll(Arb.int().orNull(), Arb.string()) { i: Int?, raise: String -> @@ -142,7 +144,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - happy path" { + @Test fun recoverHappyPath() = runTest { checkAll(Arb.string()) { str -> eagerEffect { str @@ -151,7 +153,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - error path and recover" { + @Test fun recoverErrorPathAndRecover() = runTest { checkAll(Arb.int(), Arb.string()) { int, fallback -> eagerEffect { raise(int) @@ -161,7 +163,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - error path and re-raise" { + @Test fun recoverErrorPathAndReRaise() = runTest { checkAll(Arb.int(), Arb.string()) { int, fallback -> eagerEffect { raise(int) @@ -171,7 +173,7 @@ class EagerEffectSpec : StringSpec({ } } - "recover - error path and throw" { + @Test fun recoverErrorPathAndThrow() = runTest { checkAll(Arb.int(), Arb.string()) { int, msg -> shouldThrow { eagerEffect { @@ -183,7 +185,7 @@ class EagerEffectSpec : StringSpec({ } } - "catch - happy path" { + @Test fun catchHappyPath() = runTest { checkAll(Arb.string()) { str -> eagerEffect { str @@ -192,7 +194,7 @@ class EagerEffectSpec : StringSpec({ } } - "catch - error path and recover" { + @Test fun catchErrorPathAndRecover() = runTest { checkAll(Arb.string(), Arb.string()) { msg, fallback -> eagerEffect { throw RuntimeException(msg) @@ -201,7 +203,7 @@ class EagerEffectSpec : StringSpec({ } } - "catch - error path and re-raise" { + @Test fun catchErrorPathAndReRaise() = runTest { checkAll(Arb.string(), Arb.int()) { msg, fallback -> eagerEffect { throw RuntimeException(msg) @@ -210,7 +212,7 @@ class EagerEffectSpec : StringSpec({ } } - "catch - error path and throw" { + @Test fun catchErrorPathAndThrow() = runTest { checkAll(Arb.string(), Arb.string()) { msg, msg2 -> shouldThrow { eagerEffect { @@ -221,21 +223,21 @@ class EagerEffectSpec : StringSpec({ } } - "catch - reified exception and recover" { + @Test fun catchReifiedExceptionAndRecover() = runTest { eagerEffect { throw ArithmeticException() }.catch { _: ArithmeticException -> 1 } .fold({ unreachable() }, ::identity) shouldBe 1 } - "catch - reified exception and raise" { + @Test fun catchReifiedExceptionAndRaise() = runTest { eagerEffect { throw ArithmeticException("Boom!") }.catch { e: ArithmeticException -> raise(e.message.shouldNotBeNull()) } .fold(::identity) { unreachable() } shouldBe "Boom!" } - "catch - reified exception and no match" { + @Test fun catchReifiedExceptionAndNoMatch() = runTest { shouldThrow { eagerEffect { throw RuntimeException("Boom!") @@ -244,7 +246,7 @@ class EagerEffectSpec : StringSpec({ }.message shouldBe "Boom!" } - "shift leaked results in RaiseLeakException" { + @Test fun shiftLeakedResultsInRaiseLeakException() = runTest { eagerEffect { suspend { raise("failure") } }.fold( @@ -254,7 +256,7 @@ class EagerEffectSpec : StringSpec({ { unreachable() }) { f -> f() } } - "shift leaked results in RaiseLeakException with exception" { + @Test fun shiftLeakedResultsInRaiseLeakExceptionWithException() = runTest { shouldThrow { val leak = CompletableDeferred Unit>() eagerEffect { @@ -270,7 +272,7 @@ class EagerEffectSpec : StringSpec({ }.message shouldStartWith "raise or bind was called outside of its DSL scope" } - "shift leaked results in RaiseLeakException after raise" { + @Test fun shiftLeakedResultsInRaiseLeakExceptionAfterRaise() = runTest { shouldThrow { val leak = CompletableDeferred Unit>() eagerEffect { @@ -285,7 +287,7 @@ class EagerEffectSpec : StringSpec({ }.message shouldStartWith "raise or bind was called outside of its DSL scope" } - "mapError - raise and transform error" { + @Test fun mapErrorRaiseAndTransformError() = runTest { checkAll(Arb.long(), Arb.string()) { l, s -> (eagerEffect { raise(l) @@ -296,10 +298,10 @@ class EagerEffectSpec : StringSpec({ } } - "mapError - success" { + @Test fun mapErrorSuccess() = runTest { checkAll(Arb.int()) { i -> (eagerEffect { i } mapError { unreachable() }) .get() shouldBe i } } -}) +} From 12b3ebf270caae33529504d62c03eefcaf669586 Mon Sep 17 00:00:00 2001 From: Alphonse Bendt <370821+abendt@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:25:29 +0200 Subject: [PATCH 08/11] Refactor NullableSpec from Kotest Plugin to Kotlin-test runtime (#3236) https://github.com/arrow-kt/arrow/issues/3153 Co-authored-by: Alejandro Serrano --- .../kotlin/arrow/core/raise/NullableSpec.kt | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt index 2a667f06df3..f967a1a95cb 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt @@ -2,17 +2,17 @@ package arrow.core.raise import arrow.core.Either import arrow.core.Some -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.boolean import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.orNull import io.kotest.property.checkAll +import kotlin.test.Test +import kotlinx.coroutines.test.runTest -@Suppress("UNREACHABLE_CODE") -class NullableSpec : StringSpec({ - "ensure null in nullable computation" { +class NullableSpec { + @Test fun ensureNullInNullableComputation() = runTest { checkAll(Arb.boolean(), Arb.int()) { predicate, i -> nullable { ensure(predicate) @@ -21,7 +21,7 @@ class NullableSpec : StringSpec({ } } - "ensureNotNull in nullable computation" { + @Test fun ensureNotNullInNullableComputation() = runTest { fun square(i: Int): Int = i * i checkAll(Arb.int().orNull()) { i: Int? -> nullable { @@ -31,7 +31,7 @@ class NullableSpec : StringSpec({ } } - "short circuit null" { + @Test fun shortCircuitNull() = runTest { nullable { val number: Int = "s".length (number.takeIf { it > 1 }?.toString()).bind() @@ -39,7 +39,7 @@ class NullableSpec : StringSpec({ } shouldBe null } - "ensureNotNull short circuit" { + @Test fun ensureNotNullShortCircuit() = runTest { nullable { val number: Int = "s".length ensureNotNull(number.takeIf { it > 1 }) @@ -47,13 +47,13 @@ class NullableSpec : StringSpec({ } shouldBe null } - "simple case" { + @Test fun simpleCase() = runTest { nullable { "s".length.bind() } shouldBe 1 } - "multiple types" { + @Test fun multipleTypes() = runTest { nullable { val number = "s".length val string = number.toString().bind() @@ -61,7 +61,7 @@ class NullableSpec : StringSpec({ } shouldBe "1" } - "binding option in nullable" { + @Test fun bindingOptionInNullable() = runTest { nullable { val number = Some("s".length) val string = number.map(Int::toString).bind() @@ -69,7 +69,7 @@ class NullableSpec : StringSpec({ } shouldBe "1" } - "binding either in nullable" { + @Test fun bindingEitherInNullable() = runTest { nullable { val number = Either.Right("s".length) val string = number.map(Int::toString).bind() @@ -77,7 +77,7 @@ class NullableSpec : StringSpec({ } shouldBe "1" } - "binding either in nullable, ignore errors" { + @Test fun bindingEitherInNullableIgnoreErrors() = runTest { nullable { val number = Either.Right("s".length) as Either val string = ignoreErrors { number.map(Int::toString).bind() } @@ -85,7 +85,7 @@ class NullableSpec : StringSpec({ } shouldBe "1" } - "short circuit option" { + @Test fun shortCircuitOption() = runTest { nullable { val number = Some("s".length) number.filter { it > 1 }.map(Int::toString).bind() @@ -93,7 +93,7 @@ class NullableSpec : StringSpec({ } shouldBe null } - "when expression" { + @Test fun whenExpression() = runTest { nullable { val number = "s".length.bind() val string = when (number) { @@ -104,7 +104,7 @@ class NullableSpec : StringSpec({ } shouldBe "1" } - "if expression" { + @Test fun ifExpression() = runTest { nullable { val number = "s".length.bind() val string = if (number == 1) { @@ -116,7 +116,7 @@ class NullableSpec : StringSpec({ } shouldBe "1" } - "if expression short circuit" { + @Test fun ifExpressionShortCircuit() = runTest { nullable { val number = "s".length.bind() val string = if (number != 1) { @@ -128,18 +128,18 @@ class NullableSpec : StringSpec({ } shouldBe null } - "Either can be bind" { + @Test fun eitherOfNothingAndSomethingCanBeBound() = runTest { nullable { val either: Either = Either.Right(4) either.bind() + 3 } shouldBe 7 } - "Recover works as expected" { + @Test fun recoverWorksAsExpected() = runTest { nullable { val one: Int = recover({ null.bind() }) { 1 } val two = 2.bind() one + two } shouldBe 3 } -}) +} From 402c280d58b97af5e412c189d59ca58f83c861a0 Mon Sep 17 00:00:00 2001 From: Jonathan Lagneaux Date: Thu, 26 Oct 2023 14:25:41 +0200 Subject: [PATCH 09/11] Refactor BracketCaseTest to use kotlin test (#3237) Closes #3186 Co-authored-by: Alejandro Serrano --- .../arrow/fx/coroutines/BracketCaseTest.kt | 609 +++++++++--------- 1 file changed, 315 insertions(+), 294 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/BracketCaseTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/BracketCaseTest.kt index 5892715e859..62cd84c51bb 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/BracketCaseTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/BracketCaseTest.kt @@ -1,7 +1,6 @@ package arrow.fx.coroutines import arrow.core.Either -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.should import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf @@ -12,358 +11,380 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.async import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.test.runTest +import kotlin.test.Test import kotlin.time.ExperimentalTime @ExperimentalTime -class BracketCaseTest : StringSpec({ - "Immediate acquire bracketCase finishes successfully" { - checkAll(Arb.int(), Arb.int()) { a, b -> - var once = true - bracketCase( - acquire = { a }, - use = { aa -> Pair(aa, b) }, - release = { _, _ -> - require(once) - once = false - } - ) shouldBe Pair(a, b) - } +class BracketCaseTest { + @Test + fun immediateAcquireBracketCaseFinishesSuccessfully() = runTest { + checkAll(Arb.int(), Arb.int()) { a, b -> + var once = true + bracketCase( + acquire = { a }, + use = { aa -> Pair(aa, b) }, + release = { _, _ -> + require(once) + once = false + } + ) shouldBe Pair(a, b) } + } - "Suspended acquire bracketCase finishes successfully" { - checkAll(Arb.int(), Arb.int()) { a, b -> - var once = true - bracketCase( - acquire = { a.suspend() }, - use = { aa -> Pair(aa, b) }, - release = { _, _ -> - require(once) - once = false - } - ) shouldBe Pair(a, b) - } + @Test + fun suspendedAcquireBracketCaseFinishedSuccessfully() = runTest { + checkAll(Arb.int(), Arb.int()) { a, b -> + var once = true + bracketCase( + acquire = { a.suspend() }, + use = { aa -> Pair(aa, b) }, + release = { _, _ -> + require(once) + once = false + } + ) shouldBe Pair(a, b) } + } - "Immediate error in acquire stays the same error" { - checkAll(Arb.throwable()) { e -> - Either.catch { - bracketCase( - acquire = { throw e }, - use = { 5 }, - release = { _, _ -> Unit } - ) - } should leftException(e) - } + @Test + fun immediateErrorInAcquireStaysTheSameError() = runTest { + checkAll(Arb.throwable()) { e -> + Either.catch { + bracketCase( + acquire = { throw e }, + use = { 5 }, + release = { _, _ -> Unit } + ) + } should leftException(e) } + } - "Suspend error in acquire stays the same error" { - checkAll(Arb.throwable()) { e -> - Either.catch { - bracketCase( - acquire = { e.suspend() }, - use = { 5 }, - release = { _, _ -> Unit } - ) - } should leftException(e) - } + @Test + fun suspendErrorInAcquireStaysTheSameError() = runTest { + checkAll(Arb.throwable()) { e -> + Either.catch { + bracketCase( + acquire = { e.suspend() }, + use = { 5 }, + release = { _, _ -> Unit } + ) + } should leftException(e) } + } - "Immediate use bracketCase finishes successfully" { - checkAll(Arb.int(), Arb.int()) { a, b -> - var once = true - bracketCase( - acquire = { a }, - use = { aa -> Pair(aa, b).suspend() }, - release = { _, _ -> - require(once) - once = false - } - ) shouldBe Pair(a, b) - } + @Test + fun immediateUseBracketCaseFinishedSuccessfully() = runTest { + checkAll(Arb.int(), Arb.int()) { a, b -> + var once = true + bracketCase( + acquire = { a }, + use = { aa -> Pair(aa, b).suspend() }, + release = { _, _ -> + require(once) + once = false + } + ) shouldBe Pair(a, b) } + } - "Suspended use bracketCase finishes successfully" { - checkAll(Arb.int(), Arb.int()) { a, b -> - var once = true - bracketCase( - acquire = { a }, - use = { aa -> Pair(aa, b).suspend() }, - release = { _, _ -> - require(once) - once = false + @Test + fun suspendedUseBracketCaseFinishesSuccessfully() = runTest { + checkAll(Arb.int(), Arb.int()) { a, b -> + var once = true + bracketCase( + acquire = { a }, + use = { aa -> Pair(aa, b).suspend() }, + release = { _, _ -> + require(once) + once = false + } + ) shouldBe Pair(a, b) + } + } + + @Test + fun bracketCaseMustRunReleaseTaskOnUseImmediateError() = runTest { + checkAll(Arb.int(), Arb.throwable()) { i, e -> + val promise = CompletableDeferred() + + Either.catch { + bracketCase( + acquire = { i }, + use = { throw e }, + release = { _, ex -> + require(promise.complete(ex)) { "Release should only be called once, called again with $ex" } } - ) shouldBe Pair(a, b) + ) } - } - "bracketCase must run release task on use immediate error" { - checkAll(Arb.int(), Arb.throwable()) { i, e -> - val promise = CompletableDeferred() - - Either.catch { - bracketCase( - acquire = { i }, - use = { throw e }, - release = { _, ex -> - require(promise.complete(ex)) { "Release should only be called once, called again with $ex" } - } - ) - } + promise.await() shouldBe ExitCase.Failure(e) + } + } - promise.await() shouldBe ExitCase.Failure(e) + @Test + fun bracketCaseMustRunReleaseTaskOnUseSuspendedError() = runTest { + checkAll(Arb.int(), Arb.throwable()) { x, e -> + val promise = CompletableDeferred>() + + Either.catch { + bracketCase( + acquire = { x }, + use = { e.suspend() }, + release = { xx, ex -> + require(promise.complete(Pair(xx, ex))) { "Release should only be called once, called again with $ex" } + } + ) } + + promise.await() shouldBe Pair(x, ExitCase.Failure(e)) } + } - "bracketCase must run release task on use suspended error" { - checkAll(Arb.int(), Arb.throwable()) { x, e -> - val promise = CompletableDeferred>() - - Either.catch { - bracketCase( - acquire = { x }, - use = { e.suspend() }, - release = { xx, ex -> - require(promise.complete(Pair(xx, ex))) { "Release should only be called once, called again with $ex" } - } - ) - } + @Test + fun bracketCaseMustAlwaysRunImmediateRelease() = runTest { + checkAll(Arb.int()) { x -> + val promise = CompletableDeferred>() - promise.await() shouldBe Pair(x, ExitCase.Failure(e)) + Either.catch { + bracketCase( + acquire = { x }, + use = { it }, + release = { xx, ex -> + require(promise.complete(Pair(xx, ex))) { "Release should only be called once, called again with $ex" } + } + ) } + + promise.await() shouldBe Pair(x, ExitCase.Completed) } + } - "bracketCase must always run immediate release" { - checkAll(Arb.int()) { x -> - val promise = CompletableDeferred>() - - Either.catch { - bracketCase( - acquire = { x }, - use = { it }, - release = { xx, ex -> - require(promise.complete(Pair(xx, ex))) { "Release should only be called once, called again with $ex" } - } - ) - } + @Test + fun bracketCaseMustAlwaysRunSuspendedRelease() = runTest { + checkAll(Arb.int()) { x -> + val promise = CompletableDeferred>() - promise.await() shouldBe Pair(x, ExitCase.Completed) + Either.catch { + bracketCase( + acquire = { x }, + use = { it }, + release = { xx, ex -> + require(promise.complete(Pair(xx, ex))) { "Release should only be called once, called again with $ex" } + .suspend() + } + ) } + + promise.await() shouldBe Pair(x, ExitCase.Completed) } + } - "bracketCase must always run suspended release" { - checkAll(Arb.int()) { x -> - val promise = CompletableDeferred>() - - Either.catch { - bracketCase( - acquire = { x }, - use = { it }, - release = { xx, ex -> - require(promise.complete(Pair(xx, ex))) { "Release should only be called once, called again with $ex" } - .suspend() - } - ) - } + @Test + fun bracketCaseMustAlwaysRunImmediateReleaseError() = runTest { + checkAll(Arb.int(), Arb.throwable()) { n, e -> + Either.catch { + bracketCase( + acquire = { n }, + use = { it }, + release = { _, _ -> throw e } + ) + } should leftException(e) + } + } - promise.await() shouldBe Pair(x, ExitCase.Completed) - } + @Test + fun bracketCaseMustAlwaysRunSuspendedReleaseError() = runTest { + checkAll(Arb.int(), Arb.throwable()) { n, e -> + Either.catch { + bracketCase( + acquire = { n }, + use = { it }, + release = { _, _ -> e.suspend() } + ) + } should leftException(e) } + } - "bracketCase must always run immediate release error" { - checkAll(Arb.int(), Arb.throwable()) { n, e -> - Either.catch { - bracketCase( - acquire = { n }, - use = { it }, - release = { _, _ -> throw e } - ) - } should leftException(e) - } + operator fun Throwable.plus(other: Throwable): Throwable = + apply { addSuppressed(other) } + + @Test + fun bracketCaseMustComposeImmediateUseAndImmediateReleaseError() = runTest { + checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> + Either.catch { + bracketCase( + acquire = { n }, + use = { throw e }, + release = { _, _ -> throw e2 } + ) + } shouldBe Either.Left(e + e2) } + } - "bracketCase must always run suspended release error" { - checkAll(Arb.int(), Arb.throwable()) { n, e -> - Either.catch { - bracketCase( - acquire = { n }, - use = { it }, - release = { _, _ -> e.suspend() } - ) - } should leftException(e) - } + @Test + fun bracketCaseMustComposeSuspendUseAndImmediateReleaseError() = runTest { + checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> + Either.catch { + bracketCase( + acquire = { n }, + use = { e.suspend() }, + release = { _, _ -> throw e2 } + ) + } shouldBe Either.Left(e + e2) } - - operator fun Throwable.plus(other: Throwable): Throwable = - apply { addSuppressed(other) } - - "bracketCase must compose immediate use & immediate release error" { - checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> - Either.catch { - bracketCase( - acquire = { n }, - use = { throw e }, - release = { _, _ -> throw e2 } - ) - } shouldBe Either.Left(e + e2) - } + } + + @Test + fun bracketCaseMustComposeImmediateUseAndSuspendReleaseError() = runTest { + checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> + Either.catch { + bracketCase( + acquire = { n }, + use = { throw e }, + release = { _, _ -> e2.suspend() } + ) + } shouldBe Either.Left(e + e2) } + } - "bracketCase must compose suspend use & immediate release error" { - checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> - Either.catch { - bracketCase( - acquire = { n }, - use = { e.suspend() }, - release = { _, _ -> throw e2 } - ) - } shouldBe Either.Left(e + e2) - } + @Test + fun bracketCaseMustComposeSuspendUseAndSuspendReleaseError() = runTest { + checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> + Either.catch { + bracketCase( + acquire = { n }, + use = { e.suspend() }, + release = { _, _ -> e2.suspend() } + ) + } shouldBe Either.Left(e + e2) } + } - "bracketCase must compose immediate use & suspend release error" { - checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> - Either.catch { - bracketCase( - acquire = { n }, - use = { throw e }, - release = { _, _ -> e2.suspend() } - ) - } shouldBe Either.Left(e + e2) - } + @Test + fun cancelOnBracketCaseReleaseWithImmediateAcquire() = runTest { + val start = CompletableDeferred() + val exit = CompletableDeferred() + + val f = async { + bracketCase( + acquire = { }, + use = { + // Signal that fiber is running + start.complete(Unit) + awaitCancellation() + }, + release = { _, exitCase -> + require(exit.complete(exitCase)) { "Release should only be called once, called again with $exitCase" } + } + ) } - "bracketCase must compose suspend use & suspend release error" { - checkAll(Arb.int(), Arb.throwable(), Arb.throwable()) { n, e, e2 -> - Either.catch { - bracketCase( - acquire = { n }, - use = { e.suspend() }, - release = { _, _ -> e2.suspend() } - ) - } shouldBe Either.Left(e + e2) - } + // Wait until the fiber is started before cancelling + start.await() + f.cancel() + exit.await().shouldBeInstanceOf() + } + + @Test + fun cancelOnBracketCaseReleasesWithSuspendingAcquire() = runTest { + val start = CompletableDeferred() + val exit = CompletableDeferred() + + val f = async { + bracketCase( + acquire = { Unit.suspend() }, + use = { + // Signal that fiber is running + start.complete(Unit) + awaitCancellation() + }, + release = { _, exitCase -> + require(exit.complete(exitCase)) { "Release should only be called once, called again with $exitCase" } + } + ) } - "cancel on bracketCase releases with immediate acquire" { - val start = CompletableDeferred() - val exit = CompletableDeferred() + // Wait until the fiber is started before cancelling + start.await() + f.cancel() + exit.await().shouldBeInstanceOf() + } - val f = async { - bracketCase( - acquire = { Unit }, - use = { - // Signal that fiber is running - start.complete(Unit) - awaitCancellation() - }, - release = { _, exitCase -> - require(exit.complete(exitCase)) { "Release should only be called once, called again with $exitCase" } - } - ) - } + @Test + fun cancelOnBracketCaseDoesNotInvokeAfterFinishing() = runTest { + val start = CompletableDeferred() + val exit = CompletableDeferred() + + val f = async { + bracketCase( + acquire = { Unit }, + use = { Unit.suspend() }, + release = { _, exitCase -> + require(exit.complete(exitCase)) { "Release should only be called once, called again with $exitCase" } + } + ) - // Wait until the fiber is started before cancelling - start.await() - f.cancel() - exit.await().shouldBeInstanceOf() + // Signal that fiber can be cancelled running + start.complete(Unit) + awaitCancellation() } - "cancel on bracketCase releases with suspending acquire" { - val start = CompletableDeferred() - val exit = CompletableDeferred() + // Wait until the fiber is started before cancelling + start.await() + f.cancel() + exit.await() shouldBe ExitCase.Completed + } + + @Test + fun acquireOnBracketCaseIsNotCancellable() = runTest { + checkAll(Arb.int(), Arb.int()) { x, y -> + val mVar = Channel(1).apply { send(x) } + val latch = CompletableDeferred() + val p = CompletableDeferred() - val f = async { + val fiber = async { bracketCase( - acquire = { Unit.suspend() }, - use = { - // Signal that fiber is running - start.complete(Unit) - awaitCancellation() + acquire = { + latch.complete(Unit) + // This should be uncancellable, and suspends until capacity 1 is received + mVar.send(y) }, - release = { _, exitCase -> - require(exit.complete(exitCase)) { "Release should only be called once, called again with $exitCase" } - } + use = { awaitCancellation() }, + release = { _, exitCase -> require(p.complete(exitCase)) } ) } - // Wait until the fiber is started before cancelling - start.await() - f.cancel() - exit.await().shouldBeInstanceOf() + // Wait until acquire started + latch.await() + async { fiber.cancel() } + + mVar.receive() shouldBe x + mVar.receive() shouldBe y + p.await().shouldBeInstanceOf() } + } - "cancel on bracketCase doesn't invoke after finishing" { - val start = CompletableDeferred() - val exit = CompletableDeferred() + @Test + fun releaseOnBracketCaseIsNotCancellable() = runTest { + checkAll(Arb.int(), Arb.int()) { x, y -> + val mVar = Channel(1).apply { send(x) } + val latch = CompletableDeferred() - val f = async { + val fiber = async { bracketCase( - acquire = { Unit }, - use = { Unit.suspend() }, - release = { _, exitCase -> - require(exit.complete(exitCase)) { "Release should only be called once, called again with $exitCase" } - } + acquire = { latch.complete(Unit) }, + use = { awaitCancellation() }, + release = { _, _ -> mVar.send(y) } ) - - // Signal that fiber can be cancelled running - start.complete(Unit) - awaitCancellation() - } - - // Wait until the fiber is started before cancelling - start.await() - f.cancel() - exit.await() shouldBe ExitCase.Completed - } - - "acquire on bracketCase is not cancellable" { - checkAll(Arb.int(), Arb.int()) { x, y -> - val mVar = Channel(1).apply { send(x) } - val latch = CompletableDeferred() - val p = CompletableDeferred() - - val fiber = async { - bracketCase( - acquire = { - latch.complete(Unit) - // This should be uncancellable, and suspends until capacity 1 is received - mVar.send(y) - }, - use = { awaitCancellation() }, - release = { _, exitCase -> require(p.complete(exitCase)) } - ) - } - - // Wait until acquire started - latch.await() - async { fiber.cancel() } - - mVar.receive() shouldBe x - mVar.receive() shouldBe y - p.await().shouldBeInstanceOf() } - } - "release on bracketCase is not cancellable" { - checkAll(Arb.int(), Arb.int()) { x, y -> - val mVar = Channel(1).apply { send(x) } - val latch = CompletableDeferred() - - val fiber = async { - bracketCase( - acquire = { latch.complete(Unit) }, - use = { awaitCancellation() }, - release = { _, _ -> mVar.send(y) } - ) - } - - latch.await() - async { fiber.cancel() } + latch.await() + async { fiber.cancel() } - mVar.receive() shouldBe x - // If release was cancelled this hangs since the buffer is empty - mVar.receive() shouldBe y - } + mVar.receive() shouldBe x + // If release was cancelled this hangs since the buffer is empty + mVar.receive() shouldBe y } } -) +} From 1a2927bf4f3ab22ccc527b27421f82d314546d51 Mon Sep 17 00:00:00 2001 From: Matt Jadczak Date: Thu, 26 Oct 2023 13:26:25 +0100 Subject: [PATCH 10/11] Fix .toNonEmptyXxxOrNull for nullable types (#3127) * Add failing tests For case when .toNonEmptyListOrNull and .toNonEmptySetOrNull are called on an iterable starting with null * Fix .toNonEmptyXxxOrNull for nullable types (#3123) This also ensures that we iterate the iterable only once. This also removes the Set receiver versions of .toNonEmptySetOrNull() and .toNonEmptySetOrNone() --------- Co-authored-by: Matt Jadczak Co-authored-by: Simon Vergauwen Co-authored-by: Alejandro Serrano --- arrow-libs/core/arrow-core/api/arrow-core.api | 4 ++-- .../src/commonMain/kotlin/arrow/core/NonEmptyList.kt | 7 +++++-- .../src/commonMain/kotlin/arrow/core/NonEmptySet.kt | 11 ++++++++--- .../commonTest/kotlin/arrow/core/NonEmptyListTest.kt | 11 +++++++++++ .../commonTest/kotlin/arrow/core/NonEmptySetTest.kt | 12 ++++++++++++ 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 271226603a7..f55d0269d3a 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -958,9 +958,9 @@ public final class arrow/core/NonEmptySet : arrow/core/NonEmptyCollection, java/ public final class arrow/core/NonEmptySetKt { public static final fun nonEmptySetOf (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/util/Set; public static final fun toNonEmptySetOrNone (Ljava/lang/Iterable;)Larrow/core/Option; - public static final fun toNonEmptySetOrNone (Ljava/util/Set;)Larrow/core/Option; + public static final synthetic fun toNonEmptySetOrNone (Ljava/util/Set;)Larrow/core/Option; public static final fun toNonEmptySetOrNull (Ljava/lang/Iterable;)Ljava/util/Set; - public static final fun toNonEmptySetOrNull (Ljava/util/Set;)Ljava/util/Set; + public static final synthetic fun toNonEmptySetOrNull (Ljava/util/Set;)Ljava/util/Set; } public final class arrow/core/NonFatalKt { diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 8eca2ccc773..880202ba5d7 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -562,8 +562,11 @@ public fun NonEmptyList>.sequenceOption(): Option> public fun NonEmptyList>.sequence(): Option> = traverse(::identity) -public fun Iterable.toNonEmptyListOrNull(): NonEmptyList? = - firstOrNull()?.let { NonEmptyList(it, drop(1)) } +public fun Iterable.toNonEmptyListOrNull(): NonEmptyList? { + val iter = iterator() + if (!iter.hasNext()) return null + return NonEmptyList(iter.next(), Iterable { iter }.toList()) +} public fun Iterable.toNonEmptyListOrNone(): Option> = toNonEmptyListOrNull().toOption() diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptySet.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptySet.kt index 79d9d5cf34e..78abd143452 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptySet.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptySet.kt @@ -54,14 +54,19 @@ public value class NonEmptySet private constructor( public fun nonEmptySetOf(first: A, vararg rest: A): NonEmptySet = NonEmptySet(first, rest.toSet()) -public fun Iterable.toNonEmptySetOrNull(): NonEmptySet? = - firstOrNull()?.let { NonEmptySet(it, minus(it).toSet()) } +public fun Iterable.toNonEmptySetOrNull(): NonEmptySet? { + val iter = iterator() + if (!iter.hasNext()) return null + return NonEmptySet(iter.next(), Iterable { iter }.toSet()) +} public fun Iterable.toNonEmptySetOrNone(): Option> = toNonEmptySetOrNull().toOption() +@Deprecated("Same as Iterable extension", level = DeprecationLevel.HIDDEN) public fun Set.toNonEmptySetOrNull(): NonEmptySet? = - firstOrNull()?.let { NonEmptySet(it, minus(it)) } + (this as Iterable).toNonEmptySetOrNull() +@Deprecated("Same as Iterable extension", level = DeprecationLevel.HIDDEN) public fun Set.toNonEmptySetOrNone(): Option> = toNonEmptySetOrNull().toOption() diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt index cbb7f8b68c0..2b7ef66ec8e 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt @@ -9,6 +9,7 @@ import io.kotest.assertions.withClue import io.kotest.core.spec.style.StringSpec import io.kotest.inspectors.shouldForAll import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.property.Arb @@ -27,6 +28,16 @@ class NonEmptyListTest : StringSpec({ } } + "iterable.toNonEmptyListOrNull should return null for an empty iterable" { + listOf().toNonEmptyListOrNull().shouldBeNull() + } + + "iterable.toNonEmptyListOrNull should work correctly when the iterable starts with or contains null" { + checkAll(Arb.nonEmptyList(Arb.int().orNull())) { nonEmptyList -> + nonEmptyList.all.toNonEmptyListOrNull().shouldNotBeNull() shouldBe nonEmptyList + } + } + "iterable.toNonEmptyListOrNone should round trip" { checkAll(Arb.nonEmptyList(Arb.int())) { nonEmptyList -> nonEmptyList.all.toNonEmptyListOrNone() shouldBe nonEmptyList.some() diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt index f90df187bec..df83f8a5221 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt @@ -4,11 +4,13 @@ import arrow.core.test.nonEmptySet import io.kotest.assertions.withClue import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.next +import io.kotest.property.arbitrary.orNull import io.kotest.property.checkAll class NonEmptySetTest : StringSpec({ @@ -19,6 +21,16 @@ class NonEmptySetTest : StringSpec({ } } + "iterable.toNonEmptySetOrNull should return null for an empty iterable" { + listOf().toNonEmptySetOrNull().shouldBeNull() + } + + "iterable.toNonEmptySetOrNull should work correctly when the iterable starts with or contains null" { + checkAll(Arb.nonEmptySet(Arb.int().orNull())) { nonEmptySet -> + nonEmptySet.toNonEmptySetOrNull().shouldNotBeNull() shouldBe nonEmptySet + } + } + "iterable.toNonEmptySetOrNone should round trip" { checkAll(Arb.nonEmptySet(Arb.int())) { nonEmptySet -> nonEmptySet.toNonEmptySetOrNone() shouldBe nonEmptySet.some() From 1fd3cbf91c589a26d23a704a2b07dc61e736052d Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Sat, 28 Oct 2023 09:08:40 +0200 Subject: [PATCH 11/11] Move arrow-functions tests to kotlin.test (#3243) --- .../core/arrow-functions/build.gradle.kts | 10 - .../kotlin/arrow/core/AndThenTests.kt | 27 +- .../kotlin/arrow/core/FunctionSyntaxTest.kt | 294 +++++++++--------- .../kotlin/arrow/core/KotestConfig.kt | 12 - .../kotlin/arrow/core/MemoizationTest.kt | 33 +- 5 files changed, 178 insertions(+), 198 deletions(-) delete mode 100644 arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/KotestConfig.kt diff --git a/arrow-libs/core/arrow-functions/build.gradle.kts b/arrow-libs/core/arrow-functions/build.gradle.kts index 5437121c9ba..3a20ee1e556 100644 --- a/arrow-libs/core/arrow-functions/build.gradle.kts +++ b/arrow-libs/core/arrow-functions/build.gradle.kts @@ -1,13 +1,10 @@ @file:Suppress("DSL_SCOPE_VIOLATION") -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { id(libs.plugins.kotlin.multiplatform.get().pluginId) alias(libs.plugins.arrowGradleConfig.kotlin) alias(libs.plugins.arrowGradleConfig.publish) alias(libs.plugins.kotlinx.kover) - alias(libs.plugins.kotest.multiplatform) alias(libs.plugins.spotless) } @@ -34,17 +31,10 @@ kotlin { implementation(projects.arrowFxCoroutines) implementation(libs.kotlin.test) implementation(libs.coroutines.test) - implementation(libs.kotest.frameworkEngine) implementation(libs.kotest.assertionsCore) implementation(libs.kotest.property) } } - - jvmTest { - dependencies { - runtimeOnly(libs.kotest.runnerJUnit5) - } - } } jvm { diff --git a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/AndThenTests.kt b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/AndThenTests.kt index 492d12c4aa2..d2c23ef2426 100644 --- a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/AndThenTests.kt +++ b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/AndThenTests.kt @@ -2,7 +2,6 @@ package arrow.core import io.kotest.common.Platform import io.kotest.common.platform -import io.kotest.core.spec.style.StringSpec import io.kotest.property.Arb import io.kotest.matchers.shouldBe import io.kotest.property.arbitrary.arbitrary @@ -10,18 +9,20 @@ import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.next import io.kotest.property.checkAll +import kotlinx.coroutines.test.runTest +import kotlin.test.Test -class AndThenTests : StringSpec({ - val count = when (platform) { +class AndThenTests { + private val count = when (platform) { Platform.JVM -> 200_000 else -> 1000 } - fun Arb.Companion.functionAToB(arb: Arb): Arb<(A) -> B> = arbitrary { random -> + private fun Arb.Companion.functionAToB(arb: Arb): Arb<(A) -> B> = arbitrary { random -> { _: A -> arb.next(random) }.memoize() } - "AndThen0 - compose a chain of functions with andThen should be same with AndThen" { + @Test fun andThen0ComposeChainWithAndThen() = runTest { checkAll(Arb.int(), Arb.list(Arb.functionAToB(Arb.int()))) { i, fs -> val result = fs.fold({ i }) { acc, f -> { f(acc()) } @@ -35,7 +36,7 @@ class AndThenTests : StringSpec({ } } - "AndThen0 - andThen is stack safe" { + @Test fun andThen0AndThenStackSafe() = runTest { val result = (0 until count).fold({ 0 }) { acc, _ -> acc.andThen { it + 1 } }.invoke() @@ -43,7 +44,7 @@ class AndThenTests : StringSpec({ result shouldBe count } - "AndThen1 - compose a chain of functions with andThen should be same with AndThen" { + @Test fun andThen1ComposeChainWithAndThen() = runTest { checkAll(Arb.int(), Arb.list(Arb.functionAToB(Arb.int()))) { i, fs -> val result = fs.fold({ x: Int -> x }) { acc, f -> { x: Int -> f(acc(x)) } @@ -57,7 +58,7 @@ class AndThenTests : StringSpec({ } } - "AndThen1 - compose a chain of function with compose should be same with AndThen" { + @Test fun andThen1ComposeChainWithCompose() = runTest { checkAll(Arb.int(), Arb.list(Arb.functionAToB(Arb.int()))) { i, fs -> val result = fs.fold({ x: Int -> x }) { acc, f -> { x: Int -> acc(f(x)) } @@ -71,7 +72,7 @@ class AndThenTests : StringSpec({ } } - "AndThen1 - andThen is stack safe" { + @Test fun andThen1AndThenStackSafe() = runTest { val result = (0 until count).fold({ x: Int -> x }) { acc, _ -> acc.andThen { it + 1 } }.invoke(0) @@ -79,7 +80,7 @@ class AndThenTests : StringSpec({ result shouldBe count } - "AndThen1 - compose is stack safe" { + @Test fun andThen1ComposeStackSafe() = runTest { val result = (0 until count).fold({ x: Int -> x }) { acc, _ -> acc.compose { it + 1 } }.invoke(0) @@ -87,7 +88,7 @@ class AndThenTests : StringSpec({ result shouldBe count } - "AndThen2 - compose a chain of functions with andThen should be same with AndThen" { + @Test fun andThen2ComposeChainWithAndThen() = runTest { checkAll(Arb.int(), Arb.int(), Arb.list(Arb.functionAToB(Arb.int()))) { i, j, fs -> val result = fs.fold({ x: Int, y: Int -> x + y }) { acc, f -> { x: Int, y: Int -> f(acc(x, y)) } @@ -101,11 +102,11 @@ class AndThenTests : StringSpec({ } } - "AndThen2 - andThen is stack safe" { + @Test fun andThen2AndThenStackSafe() = runTest { val result = (0 until count).fold({ x: Int, y: Int -> x + y }) { acc, _ -> acc.andThen { it + 1 } }.invoke(0, 0) result shouldBe count } -}) +} diff --git a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/FunctionSyntaxTest.kt b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/FunctionSyntaxTest.kt index 204cb0f7863..6ceca3327bc 100644 --- a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/FunctionSyntaxTest.kt +++ b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/FunctionSyntaxTest.kt @@ -1,158 +1,158 @@ package arrow.core -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import kotlinx.coroutines.test.runTest +import kotlin.test.Test -class FunctionSyntaxTest : StringSpec({ +class FunctionSyntaxTest { val sum = { i1: Int, i2: Int -> i1 + i2 } val add5 = { i: Int -> i + 5 } val multiplyBy2 = { i: Int -> i * 2 } - "it should compose function correctly (andThen)" { - val potato = "potato" - val ninja = "ninja" - val get = { potato } - val map = { word: String -> ninja + word } - (get andThen map)() - (ninja + potato) shouldBe (get andThen map)() + @Test fun andThenComposesCorrectly() = runTest { + val potato = "potato" + val ninja = "ninja" + val get = { potato } + val map = { word: String -> ninja + word } + (get andThen map)() + (ninja + potato) shouldBe (get andThen map)() + } + + @Test fun andThen1() = runTest { + val add5andMultiplyBy2 = add5 andThen multiplyBy2 + add5andMultiplyBy2(2) shouldBe 14 + } + + @Test fun andThen2() = runTest { + val sumAndMultiplyBy2 = sum andThen multiplyBy2 + sumAndMultiplyBy2(5, 2) shouldBe 14 + } + + @Test fun compose() = runTest { + val multiplyBy2andAdd5 = add5 compose multiplyBy2 + multiplyBy2andAdd5(2) shouldBe 9 + } + + @Test fun currying() = runTest { + val sum2ints = { x: Int, y: Int -> x + y } + val curried = sum2ints.curried() + curried(2)(4) shouldBe 6 + val addFive = curried(5) + addFive(7) shouldBe 12 + } + + @Test fun uncurrying() = runTest { + val sum2ints: (Int, Int) -> Int = { x, y -> x + y } + val curried: (Int) -> (Int) -> Int = sum2ints.curried() + curried(2)(4) shouldBe 6 + // same type as sum2ints, + curried.uncurried()(2, 4) shouldBe 6 + sum2ints(2, 4) shouldBe 6 + } + + @Test fun curryingEffect() = runTest { + val sum2ints: suspend (Int, Int) -> Int = { x: Int, y: Int -> x + y } + val curried: (Int) -> suspend (Int) -> Int = sum2ints.curried() + curried(2)(4) shouldBe 6 + val addFive: suspend (Int) -> Int = curried(5) + addFive(7) shouldBe 12 + } + + @Test fun uncurryingEffect() = runTest { + val sum2ints: suspend (Int, Int) -> Int = { x, y -> x + y } + val curried: (Int) -> suspend (Int) -> Int = sum2ints.curried() + curried(2)(4) shouldBe 6 + // same type as sum2ints, + curried.uncurried()(2, 4) shouldBe 6 + sum2ints(2, 4) shouldBe 6 + } + + @Test fun memoize() = runTest { + var counterA = 0 + var counterB = 0 + + val a = { _: Int -> counterA++ } + val b = { _: Int -> counterB++ }.memoize() + + repeat(5) { a(1) } + repeat(5) { b(1) } + + counterA shouldBe 5 + counterB shouldBe 1 // calling several times a memoized function with the same parameter is computed just once + } + + @Test fun memoizeEmpty() = runTest { + var counterA = 0 + var counterB = 0 + + val a = { counterA++ } + val b = { counterB++ }.memoize() + + repeat(5) { a() } + repeat(5) { b() } + + counterA shouldBe 5 + counterB shouldBe 1 // calling several times a memoized function with the same parameter is computed just once + } + + @Test fun partially() = runTest { + val sum5ints = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } + val sum4intsTo10 = sum5ints.partially5(10) + val sum3intsTo15 = sum4intsTo10.partially4(5) + val sum2intsTo17 = sum3intsTo15.partially3(2) + sum2intsTo17(1, 2) shouldBe 20 + + val prefixAndPostfix = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } + + val helloX = prefixAndPostfix.partially1("Hello, ").partially2("!") + helloX("Arrow") shouldBe "Hello, Arrow!" + } + + @Test fun suspendPartially() = runTest { + val sum5ints: suspend (Int, Int, Int, Int, Int) -> Int = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } + val sum4intsTo10 = sum5ints.partially5(10) + val sum3intsTo15 = sum4intsTo10.partially4(5) + val sum2intsTo17 = sum3intsTo15.partially3(2) + sum2intsTo17(1, 2) shouldBe 20 + + val prefixAndPostfix: suspend (String, String, String) -> String = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } + + val helloX = prefixAndPostfix.partially1("Hello, ").partially2("!") + helloX("Arrow") shouldBe "Hello, Arrow!" + } + + @Test fun partials() = runTest { + val sum5ints = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } + val sum4intsTo10: (Int, Int, Int, Int) -> Int = sum5ints.partially5(10) + val sum3intsTo15: (Int, Int, Int) -> Int = sum4intsTo10.partially4(5) + val sum2intsTo17: (Int, Int) -> Int = sum3intsTo15.partially3(2) + sum2intsTo17(1, 2) shouldBe 20 + val prefixAndPostfix = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } + val helloX: (String) -> String = prefixAndPostfix.partially1("Hello, ").partially2("!") + helloX("Arrow") shouldBe "Hello, Arrow!" + } + + @Test fun suspendPartials() = runTest { + val sum5ints: suspend (Int, Int, Int, Int, Int) -> Int = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } + val sum4intsTo10: suspend (Int, Int, Int, Int) -> Int = sum5ints.partially5(10) + val sum3intsTo15: suspend (Int, Int, Int) -> Int = sum4intsTo10.partially4(5) + val sum2intsTo17: suspend (Int, Int) -> Int = sum3intsTo15.partially3(2) + sum2intsTo17(1, 2) shouldBe 20 + val prefixAndPostfix: suspend (String, String, String) -> String = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } + val helloX: suspend (String) -> String = prefixAndPostfix.partially1("Hello, ").partially2("!") + helloX("Arrow") shouldBe "Hello, Arrow!" + } + + @Test fun bind() = runTest { + var i = 0 + fun inc(a: Int) { + i += a } - "testAndThen" { - val add5andMultiplyBy2 = add5 andThen multiplyBy2 - add5andMultiplyBy2(2) shouldBe 14 - } - - "testAndThen2" { - val sumAndMultiplyBy2 = sum andThen multiplyBy2 - sumAndMultiplyBy2(5, 2) shouldBe 14 - } - - "testCompose" { - val multiplyBy2andAdd5 = add5 compose multiplyBy2 - multiplyBy2andAdd5(2) shouldBe 9 - } - - "testCurrying" { - val sum2ints = { x: Int, y: Int -> x + y } - val curried = sum2ints.curried() - curried(2)(4) shouldBe 6 - val addFive = curried(5) - addFive(7) shouldBe 12 - } - - "testUncurrying" { - val sum2ints: (Int, Int) -> Int = { x, y -> x + y } - val curried: (Int) -> (Int) -> Int = sum2ints.curried() - curried(2)(4) shouldBe 6 - // same type as sum2ints, - curried.uncurried()(2, 4) shouldBe 6 - sum2ints(2, 4) shouldBe 6 - } - - "testCurryingEffect" { - val sum2ints: suspend (Int, Int) -> Int = { x: Int, y: Int -> x + y } - val curried: (Int) -> suspend (Int) -> Int = sum2ints.curried() - curried(2)(4) shouldBe 6 - val addFive: suspend (Int) -> Int = curried(5) - addFive(7) shouldBe 12 - } - - "testUncurryingEffect" { - val sum2ints: suspend (Int, Int) -> Int = { x, y -> x + y } - val curried: (Int) -> suspend (Int) -> Int = sum2ints.curried() - curried(2)(4) shouldBe 6 - // same type as sum2ints, - curried.uncurried()(2, 4) shouldBe 6 - sum2ints(2, 4) shouldBe 6 - } - - "memoize" { - var counterA = 0 - var counterB = 0 - - val a = { _: Int -> counterA++ } - val b = { _: Int -> counterB++ }.memoize() - - repeat(5) { a(1) } - repeat(5) { b(1) } - - counterA shouldBe 5 - counterB shouldBe 1 // calling several times a memoized function with the same parameter is computed just once - } - - "memoizeEmpty" { - var counterA = 0 - var counterB = 0 - - val a = { counterA++ } - val b = { counterB++ }.memoize() - - repeat(5) { a() } - repeat(5) { b() } - - counterA shouldBe 5 - counterB shouldBe 1 // calling several times a memoized function with the same parameter is computed just once - } - - "partially" { - val sum5ints = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } - val sum4intsTo10 = sum5ints.partially5(10) - val sum3intsTo15 = sum4intsTo10.partially4(5) - val sum2intsTo17 = sum3intsTo15.partially3(2) - sum2intsTo17(1, 2) shouldBe 20 - - val prefixAndPostfix = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } - - val helloX = prefixAndPostfix.partially1("Hello, ").partially2("!") - helloX("Arrow") shouldBe "Hello, Arrow!" - } - - "suspend partially" { - val sum5ints: suspend (Int, Int, Int, Int, Int) -> Int = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } - val sum4intsTo10 = sum5ints.partially5(10) - val sum3intsTo15 = sum4intsTo10.partially4(5) - val sum2intsTo17 = sum3intsTo15.partially3(2) - sum2intsTo17(1, 2) shouldBe 20 - - val prefixAndPostfix: suspend (String, String, String) -> String = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } - - val helloX = prefixAndPostfix.partially1("Hello, ").partially2("!") - helloX("Arrow") shouldBe "Hello, Arrow!" - } - - "partials" { - val sum5ints = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } - val sum4intsTo10: (Int, Int, Int, Int) -> Int = sum5ints.partially5(10) - val sum3intsTo15: (Int, Int, Int) -> Int = sum4intsTo10.partially4(5) - val sum2intsTo17: (Int, Int) -> Int = sum3intsTo15.partially3(2) - sum2intsTo17(1, 2) shouldBe 20 - val prefixAndPostfix = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } - val helloX: (String) -> String = prefixAndPostfix.partially1("Hello, ").partially2("!") - helloX("Arrow") shouldBe "Hello, Arrow!" - } - - "suspend partials" { - val sum5ints: suspend (Int, Int, Int, Int, Int) -> Int = { a: Int, b: Int, c: Int, d: Int, e: Int -> a + b + c + d + e } - val sum4intsTo10: suspend (Int, Int, Int, Int) -> Int = sum5ints.partially5(10) - val sum3intsTo15: suspend (Int, Int, Int) -> Int = sum4intsTo10.partially4(5) - val sum2intsTo17: suspend (Int, Int) -> Int = sum3intsTo15.partially3(2) - sum2intsTo17(1, 2) shouldBe 20 - val prefixAndPostfix: suspend (String, String, String) -> String = { prefix: String, x: String, postfix: String -> "$prefix$x$postfix" } - val helloX: suspend (String) -> String = prefixAndPostfix.partially1("Hello, ").partially2("!") - helloX("Arrow") shouldBe "Hello, Arrow!" - } - - "bind" { - var i = 0 - fun inc(a: Int) { - i += a - } - - val binded = ::inc.partially1(5) - i shouldBe 0 - binded() - i shouldBe 5 - } - -}) + val binded = ::inc.partially1(5) + i shouldBe 0 + binded() + i shouldBe 5 + } +} diff --git a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/KotestConfig.kt b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/KotestConfig.kt deleted file mode 100644 index ce7f493fad2..00000000000 --- a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/KotestConfig.kt +++ /dev/null @@ -1,12 +0,0 @@ -package arrow.core - -import io.kotest.core.config.AbstractProjectConfig -import io.kotest.property.PropertyTesting -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -class KotestConfig : AbstractProjectConfig() { - override suspend fun beforeProject() { - PropertyTesting.defaultIterationCount = 250 - } -} diff --git a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/MemoizationTest.kt b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/MemoizationTest.kt index 24478adfc21..2c99471e987 100644 --- a/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/MemoizationTest.kt +++ b/arrow-libs/core/arrow-functions/src/commonTest/kotlin/arrow/core/MemoizationTest.kt @@ -1,14 +1,15 @@ package arrow.core -import io.kotest.core.spec.style.StringSpec import io.kotest.property.checkAll import io.kotest.matchers.shouldBe import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlin.random.Random +import kotlinx.coroutines.test.runTest +import kotlin.test.Test -class MemoizationTest : StringSpec({ - "Memoize races" { +class MemoizationTest { + @Test fun memoizeRaces() = runTest { checkAll { fun sum(): Int = Random.nextInt(Int.MAX_VALUE) @@ -24,7 +25,7 @@ class MemoizationTest : StringSpec({ } } - "Memoize P0 only first execution runs" { + @Test fun memoize0OnlyFirst() = runTest { var runs = 0 fun sum(): Int { runs++ @@ -38,7 +39,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 1 } - "Memoize P0 nullable" { + @Test fun memoize0Nullable() = runTest { var runs = 0 fun sum(): Int? { runs++ @@ -52,7 +53,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 1 } - "Memoize P1 only first execution runs" { + @Test fun memoize1OnlyFirst() = runTest { var runs = 0 fun sum(n: Int): Int { runs++ @@ -70,7 +71,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 3 } - "Memoize P1 nullable" { + @Test fun memoize1Nullable() = runTest { var runs = 0 fun sum(n: Int): Int? { runs++ @@ -84,7 +85,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 1 } - "Memoize P2 only first execution runs" { + @Test fun memoize2OnlyFirst() = runTest { var runs = 0 fun sum(n1: Int, n2: Int): Int { runs++ @@ -103,7 +104,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 3 } - "Memoize P2 nullable" { + @Test fun memoize2Nullable() = runTest { var runs = 0 fun sum(n: Int, m: Int): Int? { runs++ @@ -117,7 +118,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 1 } - "Memoize P3 only first execution runs" { + @Test fun memoize3OnlyFirst() = runTest { var runs = 0 fun sum(n1: Int, n2: Int, n3: Int): Int { runs++ @@ -136,7 +137,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 3 } - "Memoize P3 nullable" { + @Test fun memoize3Nullable() = runTest { var runs = 0 fun sum(a: Int, b: Int, c: Int): Int? { runs++ @@ -150,7 +151,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 1 } - "Memoize P4 only first execution runs" { + @Test fun memoize4OnlyFirst() = runTest { var runs = 0 fun sum(n1: Int, n2: Int, n3: Int, n4: Int): Int { runs++ @@ -169,7 +170,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 3 } - "Memoize P4 nullable" { + @Test fun memoize4Nullable() = runTest { var runs = 0 fun sum(a: Int, b: Int, c: Int, d: Int): Int? { runs++ @@ -183,7 +184,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 1 } - "Memoize P5 only first execution runs" { + @Test fun memoize5OnlyFirst() = runTest { var runs = 0 fun sum(n1: Int, n2: Int, n3: Int, n4: Int, n5: Int): Int { runs++ @@ -202,7 +203,7 @@ class MemoizationTest : StringSpec({ runs shouldBe 3 } - "Memoize P5 nullable" { + @Test fun memoize5Nullable() = runTest { var runs = 0 fun sum(a: Int, b: Int, c: Int, d: Int, e: Int): Int? { runs++ @@ -215,6 +216,6 @@ class MemoizationTest : StringSpec({ memoized(1, 2, 3, 4, 5) shouldBe null runs shouldBe 1 } -}) +} private fun consecSumResult(n: Int): Int = (n * (n + 1)) / 2