CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-kotest--kotest-assertions-core-jvm

Core assertion and matcher library for Kotest testing framework

Pending
Overview
Eval results
Files

concurrency.mddocs/

Concurrency Matchers

JVM concurrency testing support for futures, atomic types, channels, thread-safe collections, and synchronization primitives with comprehensive assertions for concurrent operations and thread safety validation.

Capabilities

CompletableFuture Matchers

Matchers for validating CompletableFuture behavior including completion, timing, and result validation.

/**
 * Assert that CompletableFuture completes within specified duration
 * @param duration Maximum time to wait for completion
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldCompleteWithin(duration: Duration): CompletableFuture<T>

/**
 * Assert that CompletableFuture does not complete within specified duration
 * @param duration Time to wait before asserting non-completion
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldNotCompleteWithin(duration: Duration): CompletableFuture<T>

/**
 * Assert that CompletableFuture completes successfully (not exceptionally)
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldCompleteSuccessfully(): CompletableFuture<T>

/**
 * Assert that CompletableFuture completes exceptionally
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldCompleteExceptionally(): CompletableFuture<T>

/**
 * Assert that CompletableFuture completes with specific value
 * @param expectedValue The value the future should complete with
 * @return The original CompletableFuture for chaining
 */
infix fun <T> CompletableFuture<T>.shouldCompleteWith(expectedValue: T): CompletableFuture<T>

/**
 * Assert that CompletableFuture completes with exception of specific type
 * @param T The exception type expected
 * @return The original CompletableFuture for chaining
 */
inline fun <reified T : Throwable> CompletableFuture<*>.shouldCompleteWithException(): CompletableFuture<*>

/**
 * Assert that CompletableFuture is already done (completed or failed)
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldBeDone(): CompletableFuture<T>

/**
 * Assert that CompletableFuture is not yet done
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldNotBeDone(): CompletableFuture<T>

/**
 * Assert that CompletableFuture is cancelled
 * @return The original CompletableFuture for chaining
 */
fun <T> CompletableFuture<T>.shouldBeCancelled(): CompletableFuture<T>

/**
 * Create matcher for future completion within duration
 * @param duration Maximum time to wait for completion
 * @return Matcher that passes when future completes within time
 */
fun <T> completeWithin(duration: Duration): Matcher<CompletableFuture<T>>

/**
 * Create matcher for successful completion
 * @return Matcher that passes when future completes successfully
 */
fun <T> completeSuccessfully(): Matcher<CompletableFuture<T>>

/**
 * Create matcher for exceptional completion
 * @return Matcher that passes when future completes exceptionally
 */
fun <T> completeExceptionally(): Matcher<CompletableFuture<T>>

/**
 * Create matcher for completion with specific value
 * @param expectedValue The expected completion value
 * @return Matcher that passes when future completes with expected value
 */
fun <T> completeWith(expectedValue: T): Matcher<CompletableFuture<T>>

/**
 * Create matcher for completion with exception type
 * @param T The expected exception type
 * @return Matcher that passes when future completes with specified exception
 */
inline fun <reified T : Throwable> completeWithException(): Matcher<CompletableFuture<*>>

Usage Examples:

import io.kotest.matchers.concurrent.*
import java.util.concurrent.CompletableFuture
import java.time.Duration

val quickTask = CompletableFuture.supplyAsync { "result" }
val slowTask = CompletableFuture.supplyAsync { 
    Thread.sleep(1000)
    "slow result" 
}
val failingTask = CompletableFuture.supplyAsync<String> { 
    throw RuntimeException("Task failed") 
}

// Completion timing
quickTask.shouldCompleteWithin(Duration.ofSeconds(1))
slowTask.shouldNotCompleteWithin(Duration.ofMillis(100))

// Completion outcomes
quickTask.shouldCompleteSuccessfully()
failingTask.shouldCompleteExceptionally()
quickTask shouldCompleteWith "result"
failingTask.shouldCompleteWithException<RuntimeException>()

// State validation
quickTask.shouldBeDone() // after completion
val pendingTask = CompletableFuture<String>()
pendingTask.shouldNotBeDone()

// Using matcher syntax
quickTask should completeSuccessfully()
failingTask should completeExceptionally()
slowTask should completeWithin(Duration.ofSeconds(2))

Atomic Reference Matchers

Matchers for validating atomic reference types and their thread-safe operations.

/**
 * Assert that AtomicReference has specific value
 * @param expected The expected value
 * @return The original AtomicReference for chaining
 */
fun <T> AtomicReference<T>.shouldHaveValue(expected: T): AtomicReference<T>

/**
 * Assert that AtomicReference does not have specific value
 * @param unexpected The value that should not be present
 * @return The original AtomicReference for chaining
 */
fun <T> AtomicReference<T>.shouldNotHaveValue(unexpected: T): AtomicReference<T>

/**
 * Assert that AtomicInteger has specific value
 * @param expected The expected integer value
 * @return The original AtomicInteger for chaining
 */
infix fun AtomicInteger.shouldHaveValue(expected: Int): AtomicInteger

/**
 * Assert that AtomicLong has specific value
 * @param expected The expected long value
 * @return The original AtomicLong for chaining
 */
infix fun AtomicLong.shouldHaveValue(expected: Long): AtomicLong

/**
 * Assert that AtomicBoolean has specific value
 * @param expected The expected boolean value
 * @return The original AtomicBoolean for chaining
 */
infix fun AtomicBoolean.shouldHaveValue(expected: Boolean): AtomicBoolean

/**
 * Assert that AtomicInteger is greater than specified value
 * @param value The minimum value (exclusive)
 * @return The original AtomicInteger for chaining
 */
infix fun AtomicInteger.shouldBeGreaterThan(value: Int): AtomicInteger

/**
 * Assert that AtomicInteger is less than specified value
 * @param value The maximum value (exclusive)
 * @return The original AtomicInteger for chaining
 */
infix fun AtomicInteger.shouldBeLessThan(value: Int): AtomicInteger

/**
 * Create matcher for atomic reference value validation
 * @param expected The expected value
 * @return Matcher that passes when atomic reference has expected value
 */
fun <T> haveValue(expected: T): Matcher<AtomicReference<T>>

/**
 * Create matcher for atomic integer comparison (greater than)
 * @param value The minimum value (exclusive)
 * @return Matcher that passes when atomic integer is greater
 */
fun beGreaterThan(value: Int): Matcher<AtomicInteger>

/**
 * Create matcher for atomic boolean validation
 * @param expected The expected boolean value
 * @return Matcher that passes when atomic boolean has expected value
 */
fun haveValue(expected: Boolean): Matcher<AtomicBoolean>

Channel Matchers

Matchers for Kotlin coroutines channels including send/receive operations and channel states.

/**
 * Assert that channel receives element within specified duration
 * @param duration Maximum time to wait for receive
 * @return The received element
 */
suspend fun <T> Channel<T>.shouldReceiveWithin(duration: Duration): T

/**
 * Assert that channel does not receive element within specified duration
 * @param duration Time to wait before asserting no receive
 * @return The original Channel for chaining
 */
suspend fun <T> Channel<T>.shouldNotReceiveWithin(duration: Duration): Channel<T>

/**
 * Assert that channel receives specific element within duration
 * @param expected The expected element
 * @param duration Maximum time to wait for receive
 * @return The original Channel for chaining
 */
suspend fun <T> Channel<T>.shouldReceive(expected: T, duration: Duration): Channel<T>

/**
 * Assert that channel is closed
 * @return The original Channel for chaining
 */
fun <T> Channel<T>.shouldBeClosed(): Channel<T>

/**
 * Assert that channel is not closed
 * @return The original Channel for chaining
 */
fun <T> Channel<T>.shouldNotBeClosed(): Channel<T>

/**
 * Assert that channel is empty (no elements available)
 * @return The original Channel for chaining
 */
fun <T> Channel<T>.shouldBeEmpty(): Channel<T>

/**
 * Assert that channel is not empty (has elements available)
 * @return The original Channel for chaining
 */
fun <T> Channel<T>.shouldNotBeEmpty(): Channel<T>

/**
 * Assert that channel send operation completes within duration
 * @param element The element to send
 * @param duration Maximum time to wait for send completion
 * @return The original Channel for chaining
 */
suspend fun <T> Channel<T>.shouldSendWithin(element: T, duration: Duration): Channel<T>

/**
 * Create matcher for channel receive within duration
 * @param duration Maximum time to wait
 * @return Matcher that passes when channel receives within time
 */
fun <T> receiveWithin(duration: Duration): Matcher<Channel<T>>

/**
 * Create matcher for closed channel validation
 * @return Matcher that passes for closed channels
 */
fun <T> beClosed(): Matcher<Channel<T>>

/**
 * Create matcher for empty channel validation
 * @return Matcher that passes for empty channels
 */
fun <T> beEmpty(): Matcher<Channel<T>>

Usage Examples:

import io.kotest.matchers.concurrent.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import java.util.concurrent.atomic.*
import java.time.Duration

// Atomic reference validation
val atomicRef = AtomicReference("initial")
val atomicInt = AtomicInteger(42)
val atomicBool = AtomicBoolean(true)

atomicRef.shouldHaveValue("initial")
atomicInt shouldHaveValue 42
atomicBool shouldHaveValue true
atomicInt shouldBeGreaterThan 40

// Channel operations
runBlocking {
    val channel = Channel<String>(Channel.UNLIMITED)
    
    // Send and receive operations
    channel.send("message")
    channel.shouldReceiveWithin(Duration.ofSeconds(1)) shouldBe "message"
    
    // Channel state validation
    channel.shouldNotBeClosed()
    channel.close()
    channel.shouldBeClosed()
    
    // Empty channel validation
    val emptyChannel = Channel<Int>()
    emptyChannel.shouldBeEmpty()
}

// Using matcher syntax
atomicRef should haveValue("initial")
atomicInt should beGreaterThan(30)

Thread and Executor Matchers

Matchers for validating thread states and executor service operations.

/**
 * Assert that thread is alive (not terminated)
 * @return The original Thread for chaining
 */
fun Thread.shouldBeAlive(): Thread

/**
 * Assert that thread is not alive (terminated)
 * @return The original Thread for chaining
 */
fun Thread.shouldNotBeAlive(): Thread

/**
 * Assert that thread is in specific state
 * @param state The expected thread state
 * @return The original Thread for chaining
 */
infix fun Thread.shouldBeInState(state: Thread.State): Thread

/**
 * Assert that thread should not be in specific state
 * @param state The thread state that should not match
 * @return The original Thread for chaining
 */
infix fun Thread.shouldNotBeInState(state: Thread.State): Thread

/**
 * Assert that thread is daemon thread
 * @return The original Thread for chaining
 */
fun Thread.shouldBeDaemon(): Thread

/**
 * Assert that thread is not daemon thread
 * @return The original Thread for chaining
 */
fun Thread.shouldNotBeDaemon(): Thread

/**
 * Assert that ExecutorService is shutdown
 * @return The original ExecutorService for chaining
 */
fun ExecutorService.shouldBeShutdown(): ExecutorService

/**
 * Assert that ExecutorService is not shutdown
 * @return The original ExecutorService for chaining
 */
fun ExecutorService.shouldNotBeShutdown(): ExecutorService

/**
 * Assert that ExecutorService is terminated
 * @return The original ExecutorService for chaining
 */
fun ExecutorService.shouldBeTerminated(): ExecutorService

/**
 * Assert that ExecutorService terminates within specified duration
 * @param duration Maximum time to wait for termination
 * @return The original ExecutorService for chaining
 */
fun ExecutorService.shouldTerminateWithin(duration: Duration): ExecutorService

/**
 * Create matcher for thread state validation
 * @param state The expected thread state
 * @return Matcher that passes when thread is in specified state
 */
fun beInState(state: Thread.State): Matcher<Thread>

/**
 * Create matcher for alive thread validation
 * @return Matcher that passes for alive threads
 */
fun beAlive(): Matcher<Thread>

/**
 * Create matcher for daemon thread validation
 * @return Matcher that passes for daemon threads
 */
fun beDaemon(): Matcher<Thread>

/**
 * Create matcher for shutdown executor validation
 * @return Matcher that passes for shutdown executors
 */
fun beShutdown(): Matcher<ExecutorService>

/**
 * Create matcher for executor termination validation
 * @param duration Maximum time to wait for termination
 * @return Matcher that passes when executor terminates within duration
 */
fun terminateWithin(duration: Duration): Matcher<ExecutorService>

Lock and Synchronization Matchers

Matchers for validating locks, semaphores, and other synchronization primitives.

/**
 * Assert that ReentrantLock is locked
 * @return The original ReentrantLock for chaining
 */
fun ReentrantLock.shouldBeLocked(): ReentrantLock

/**
 * Assert that ReentrantLock is not locked (unlocked)
 * @return The original ReentrantLock for chaining
 */
fun ReentrantLock.shouldNotBeLocked(): ReentrantLock

/**
 * Assert that ReentrantLock is held by current thread
 * @return The original ReentrantLock for chaining
 */
fun ReentrantLock.shouldBeHeldByCurrentThread(): ReentrantLock

/**
 * Assert that ReentrantLock is not held by current thread
 * @return The original ReentrantLock for chaining
 */
fun ReentrantLock.shouldNotBeHeldByCurrentThread(): ReentrantLock

/**
 * Assert that ReentrantLock has specific hold count
 * @param count Expected number of times lock is held by current thread
 * @return The original ReentrantLock for chaining
 */
infix fun ReentrantLock.shouldHaveHoldCount(count: Int): ReentrantLock

/**
 * Assert that Semaphore has specific number of available permits
 * @param permits Expected number of available permits
 * @return The original Semaphore for chaining
 */
infix fun Semaphore.shouldHaveAvailablePermits(permits: Int): Semaphore

/**
 * Assert that Semaphore has no available permits
 * @return The original Semaphore for chaining
 */
fun Semaphore.shouldHaveNoAvailablePermits(): Semaphore

/**
 * Assert that CountDownLatch count is zero (released)
 * @return The original CountDownLatch for chaining
 */
fun CountDownLatch.shouldBeReleased(): CountDownLatch

/**
 * Assert that CountDownLatch count is not zero (not released)
 * @return The original CountDownLatch for chaining
 */
fun CountDownLatch.shouldNotBeReleased(): CountDownLatch

/**
 * Assert that CountDownLatch has specific count
 * @param count Expected latch count
 * @return The original CountDownLatch for chaining
 */
infix fun CountDownLatch.shouldHaveCount(count: Long): CountDownLatch

/**
 * Create matcher for locked state validation
 * @return Matcher that passes for locked locks
 */
fun beLocked(): Matcher<ReentrantLock>

/**
 * Create matcher for current thread lock ownership
 * @return Matcher that passes when current thread holds lock
 */
fun beHeldByCurrentThread(): Matcher<ReentrantLock>

/**
 * Create matcher for lock hold count validation
 * @param count Expected hold count
 * @return Matcher that passes when lock has expected hold count
 */
fun haveHoldCount(count: Int): Matcher<ReentrantLock>

/**
 * Create matcher for semaphore permits validation
 * @param permits Expected available permits
 * @return Matcher that passes when semaphore has expected permits
 */
fun haveAvailablePermits(permits: Int): Matcher<Semaphore>

/**
 * Create matcher for latch release validation
 * @return Matcher that passes for released latches
 */
fun beReleased(): Matcher<CountDownLatch>

Usage Examples:

import io.kotest.matchers.concurrent.*
import java.util.concurrent.*
import java.util.concurrent.locks.ReentrantLock
import java.time.Duration

// Thread validation
val thread = Thread {
    Thread.sleep(1000)
}
thread.start()

thread.shouldBeAlive()
thread shouldBeInState Thread.State.RUNNABLE
thread.shouldNotBeDaemon()

// Executor service validation
val executor = Executors.newFixedThreadPool(2)
executor.shouldNotBeShutdown()

executor.shutdown()
executor.shouldBeShutdown()
executor.shouldTerminateWithin(Duration.ofSeconds(5))

// Lock validation
val lock = ReentrantLock()
lock.shouldNotBeLocked()

lock.lock()
try {
    lock.shouldBeLocked()
    lock.shouldBeHeldByCurrentThread()
    lock shouldHaveHoldCount 1
} finally {
    lock.unlock()
}

// Semaphore validation
val semaphore = Semaphore(3)
semaphore shouldHaveAvailablePermits 3

semaphore.acquire()
semaphore shouldHaveAvailablePermits 2

// CountDownLatch validation
val latch = CountDownLatch(2)
latch.shouldNotBeReleased()
latch shouldHaveCount 2L

latch.countDown()
latch shouldHaveCount 1L

latch.countDown()
latch.shouldBeReleased()

// Using matcher syntax
thread should beAlive()
lock should beLocked()
semaphore should haveAvailablePermits(2)
latch should beReleased()

Concurrent Collection Matchers

Matchers for thread-safe collections and their concurrent properties.

/**
 * Assert that ConcurrentMap contains key-value pair atomically
 * @param key The expected key
 * @param value The expected value
 * @return The original ConcurrentMap for chaining
 */
fun <K, V> ConcurrentMap<K, V>.shouldContainAtomically(key: K, value: V): ConcurrentMap<K, V>

/**
 * Assert that ConcurrentHashMap is empty atomically
 * @return The original ConcurrentHashMap for chaining
 */
fun <K, V> ConcurrentHashMap<K, V>.shouldBeEmptyAtomically(): ConcurrentHashMap<K, V>

/**
 * Assert that BlockingQueue contains specific element
 * @param element The element that should be present
 * @return The original BlockingQueue for chaining
 */
fun <T> BlockingQueue<T>.shouldContain(element: T): BlockingQueue<T>

/**
 * Assert that BlockingQueue is empty
 * @return The original BlockingQueue for chaining
 */
fun <T> BlockingQueue<T>.shouldBeEmpty(): BlockingQueue<T>

/**
 * Assert that BlockingQueue has specific size
 * @param size Expected queue size
 * @return The original BlockingQueue for chaining
 */
infix fun <T> BlockingQueue<T>.shouldHaveSize(size: Int): BlockingQueue<T>

/**
 * Assert that BlockingQueue offers element within duration
 * @param element The element to offer
 * @param duration Maximum time to wait for offer
 * @return The original BlockingQueue for chaining
 */
suspend fun <T> BlockingQueue<T>.shouldOfferWithin(element: T, duration: Duration): BlockingQueue<T>

/**
 * Create matcher for concurrent map containment
 * @param key Expected key
 * @param value Expected value
 * @return Matcher that passes when map contains key-value pair
 */
fun <K, V> containAtomically(key: K, value: V): Matcher<ConcurrentMap<K, V>>

/**
 * Create matcher for blocking queue element validation
 * @param element Expected element
 * @return Matcher that passes when queue contains element
 */
fun <T> contain(element: T): Matcher<BlockingQueue<T>>

/**
 * Create matcher for queue size validation
 * @param size Expected size
 * @return Matcher that passes when queue has expected size
 */
fun <T> haveSize(size: Int): Matcher<BlockingQueue<T>>

Usage Examples:

import io.kotest.matchers.concurrent.*
import java.util.concurrent.*

// Concurrent collections
val concurrentMap = ConcurrentHashMap<String, Int>()
val blockingQueue = LinkedBlockingQueue<String>()

// Map operations
concurrentMap["key"] = 42
concurrentMap.shouldContainAtomically("key", 42)

// Queue operations  
blockingQueue.offer("item1")
blockingQueue.offer("item2")

blockingQueue.shouldContain("item1")
blockingQueue shouldHaveSize 2
blockingQueue.shouldNotBeEmpty()

// Clear and verify
blockingQueue.clear()
blockingQueue.shouldBeEmpty()

// Using matcher syntax
concurrentMap should containAtomically("key", 42)
blockingQueue should haveSize(0)

Error Handling

Concurrency matchers provide detailed error information for assertion failures:

  • Timing failures: Show actual vs expected durations with precise timing measurements
  • State failures: Indicate expected vs actual thread/executor states with transition information
  • Completion failures: Details about future completion status and any exceptions thrown
  • Atomic operation failures: Show expected vs actual atomic values with concurrent access context
  • Channel failures: Information about channel capacity, buffering, and element availability
  • Lock failures: Details about lock ownership, hold counts, and contention information

All concurrency matchers handle race conditions appropriately and provide thread-safe assertions with meaningful error messages that help debug concurrent code issues.

Install with Tessl CLI

npx tessl i tessl/maven-io-kotest--kotest-assertions-core-jvm

docs

collections.md

concurrency.md

core-dsl.md

datetime.md

filesystem.md

index.md

nondeterministic.md

primitives.md

reflection.md

result.md

strings.md

throwable.md

tuples.md

types.md

tile.json