From 6691ea37ca986107791cb01aa40aad0d8a8ea5cc Mon Sep 17 00:00:00 2001 From: Andreas Storesund Madsen Date: Thu, 19 Oct 2023 19:28:47 +0200 Subject: [PATCH] Refactor ParZip2Test from Kotest Plugin to Kotlin-test runtime (#3192) --- .../kotlin/arrow/fx/coroutines/ParZip2Test.kt | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 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..5c9282c83ae 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 @@ -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 = Atomic("") + 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() } } -) +}