Skip to content

Commit

Permalink
Use CompletableDeferred in tests and add shouldHaveCompleted
Browse files Browse the repository at this point in the history
  • Loading branch information
kyay10 committed Nov 6, 2024
1 parent 184927b commit bdda97a
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package arrow
import arrow.atomic.AtomicBoolean
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.toList
import kotlinx.coroutines.test.runTest
Expand All @@ -13,50 +14,50 @@ class AutoCloseTest {

@Test
fun canInstallResource() = runTest {
var throwable: Throwable? = RuntimeException("Dummy exception")
var wasActive = false
val promise = CompletableDeferred<Throwable?>()
val wasActive = CompletableDeferred<Boolean>()
val res = Resource()

autoCloseScope {
val r = autoClose({ res }) { r, e ->
throwable = e
require(promise.complete(e))
r.shutdown()
}
wasActive = r.isActive()
require(wasActive.complete(r.isActive()))
}

throwable shouldBe null
wasActive shouldBe true
promise.shouldHaveCompleted() shouldBe null
wasActive.shouldHaveCompleted() shouldBe true
res.isActive() shouldBe false
}

@Test
fun canHandleWithFailingAutoClose() = runTest {
var throwable: Throwable? = RuntimeException("Dummy exception")
var wasActive = false
val promise = CompletableDeferred<Throwable?>()
val wasActive = CompletableDeferred<Boolean>()
val error = RuntimeException("BOOM!")
val res = Resource()

shouldThrow<RuntimeException> {
autoCloseScope {
val r = autoClose({ res }) { r, e ->
throwable = e
require(promise.complete(e))
r.shutdown()
}
wasActive = r.isActive()
require(wasActive.complete(r.isActive()))
throw error
}
} shouldBe error

throwable shouldBe error
wasActive shouldBe true
promise.shouldHaveCompleted() shouldBe error
wasActive.shouldHaveCompleted() shouldBe true
res.isActive() shouldBe false
}

@Test
fun addsSuppressedErrors() = runTest {
var throwable: Throwable? = RuntimeException("Dummy exception")
var wasActive = false
val promise = CompletableDeferred<Throwable?>()
val wasActive = CompletableDeferred<Boolean>()
val error = RuntimeException("BOOM!")
val error2 = RuntimeException("BOOM 2!")
val error3 = RuntimeException("BOOM 3!")
Expand All @@ -65,33 +66,33 @@ class AutoCloseTest {
val e = shouldThrow<RuntimeException> {
autoCloseScope {
val r = autoClose({ res }) { r, e ->
throwable = e
require(promise.complete(e))
r.shutdown()
throw error2
}
autoClose({ Resource() }) { _, _ -> throw error3 }
wasActive = r.isActive()
require(wasActive.complete(r.isActive()))
throw error
}
}

e shouldBe error
e.suppressedExceptions shouldBe listOf(error3, error2)
throwable shouldBe error
wasActive shouldBe true
promise.shouldHaveCompleted() shouldBe error
wasActive.shouldHaveCompleted() shouldBe true
res.isActive() shouldBe false
}

@Test
fun handlesAcquireFailure() = runTest {
var throwable: Throwable? = RuntimeException("Dummy exception")
val promise = CompletableDeferred<Throwable?>()
val error = RuntimeException("BOOM!")
val error2 = RuntimeException("BOOM 2!")

val e = shouldThrow<RuntimeException> {
autoCloseScope {
autoClose({ Resource() }) { r, e ->
throwable = e
require(promise.complete(e))
r.shutdown()
throw error2
}
Expand All @@ -100,54 +101,54 @@ class AutoCloseTest {
}
e shouldBe error
e.suppressedExceptions shouldBe listOf(error2)
throwable shouldBe error
promise.shouldHaveCompleted() shouldBe error
}

@Test
fun canInstallAutoCloseable() = runTest {
var wasActive = false
val wasActive = CompletableDeferred<Boolean>()
val res = Resource()

autoCloseScope {
val r = install(res)
wasActive = r.isActive()
require(wasActive.complete(r.isActive()))
}

wasActive shouldBe true
wasActive.shouldHaveCompleted() shouldBe true
res.isActive() shouldBe false
}

@Test
fun closeTheAutoScopeOnCancellation() = runTest {
var wasActive = false
val wasActive = CompletableDeferred<Boolean>()
val res = Resource()

shouldThrow<CancellationException> {
autoCloseScope {
val r = install(res)
wasActive = r.isActive()
require(wasActive.complete(r.isActive()))
throw CancellationException("BOOM!")
}
}.message shouldBe "BOOM!"

wasActive shouldBe true
wasActive.shouldHaveCompleted() shouldBe true
res.isActive() shouldBe false
}

@Test
fun closeTheAutoScopeOnNonLocalReturn() = runTest {
var wasActive = false
val wasActive = CompletableDeferred<Boolean>()
val res = Resource()

run {
autoCloseScope {
val r = install(res)
wasActive = r.isActive()
require(wasActive.complete(r.isActive()))
return@run
}
}

wasActive shouldBe true
wasActive.shouldHaveCompleted() shouldBe true
res.isActive() shouldBe false
}

Expand Down Expand Up @@ -202,4 +203,9 @@ class AutoCloseTest {
shutdown()
}
}

private suspend fun <T> CompletableDeferred<T>.shouldHaveCompleted(): T {
isCompleted shouldBe true
return await()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.kotest.assertions.fail
import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.equalityMatcher
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.choice
import io.kotest.property.arbitrary.choose
Expand Down Expand Up @@ -154,3 +155,8 @@ inline fun <A> assertThrowable(executable: () -> A): Throwable {

return if (a is Throwable) a else fail("Expected an exception but found: $a")
}

suspend fun <T> CompletableDeferred<T>.shouldHaveCompleted(): T {
isCompleted shouldBe true
return await()
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ class ResourceAutoCloseTest {
@Test
fun autoClosableIsNonCancellable() = runTest {
val t = AutoCloseableTest()
lateinit var exit: ExitCase
val exit = CompletableDeferred<ExitCase>()
val waitingToBeCancelled = CompletableDeferred<Unit>()
val cancelled = CompletableDeferred<Unit>()

val job = launch {
resourceScope {
onRelease { exit = it }
onRelease { require(exit.complete(it)) }
autoCloseable {
waitingToBeCancelled.complete(Unit)
cancelled.await()
Expand All @@ -72,7 +72,7 @@ class ResourceAutoCloseTest {
job.join()

t.didClose.get() shouldBe true
exit
exit.shouldHaveCompleted()
.shouldBeTypeOf<ExitCase.Cancelled>()
.exception
.message shouldBe "BOOM!"
Expand Down
Loading

0 comments on commit bdda97a

Please sign in to comment.