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