0
# Concurrency Matchers
1
2
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.
3
4
## Capabilities
5
6
### CompletableFuture Matchers
7
8
Matchers for validating CompletableFuture behavior including completion, timing, and result validation.
9
10
```kotlin { .api }
11
/**
12
* Assert that CompletableFuture completes within specified duration
13
* @param duration Maximum time to wait for completion
14
* @return The original CompletableFuture for chaining
15
*/
16
fun <T> CompletableFuture<T>.shouldCompleteWithin(duration: Duration): CompletableFuture<T>
17
18
/**
19
* Assert that CompletableFuture does not complete within specified duration
20
* @param duration Time to wait before asserting non-completion
21
* @return The original CompletableFuture for chaining
22
*/
23
fun <T> CompletableFuture<T>.shouldNotCompleteWithin(duration: Duration): CompletableFuture<T>
24
25
/**
26
* Assert that CompletableFuture completes successfully (not exceptionally)
27
* @return The original CompletableFuture for chaining
28
*/
29
fun <T> CompletableFuture<T>.shouldCompleteSuccessfully(): CompletableFuture<T>
30
31
/**
32
* Assert that CompletableFuture completes exceptionally
33
* @return The original CompletableFuture for chaining
34
*/
35
fun <T> CompletableFuture<T>.shouldCompleteExceptionally(): CompletableFuture<T>
36
37
/**
38
* Assert that CompletableFuture completes with specific value
39
* @param expectedValue The value the future should complete with
40
* @return The original CompletableFuture for chaining
41
*/
42
infix fun <T> CompletableFuture<T>.shouldCompleteWith(expectedValue: T): CompletableFuture<T>
43
44
/**
45
* Assert that CompletableFuture completes with exception of specific type
46
* @param T The exception type expected
47
* @return The original CompletableFuture for chaining
48
*/
49
inline fun <reified T : Throwable> CompletableFuture<*>.shouldCompleteWithException(): CompletableFuture<*>
50
51
/**
52
* Assert that CompletableFuture is already done (completed or failed)
53
* @return The original CompletableFuture for chaining
54
*/
55
fun <T> CompletableFuture<T>.shouldBeDone(): CompletableFuture<T>
56
57
/**
58
* Assert that CompletableFuture is not yet done
59
* @return The original CompletableFuture for chaining
60
*/
61
fun <T> CompletableFuture<T>.shouldNotBeDone(): CompletableFuture<T>
62
63
/**
64
* Assert that CompletableFuture is cancelled
65
* @return The original CompletableFuture for chaining
66
*/
67
fun <T> CompletableFuture<T>.shouldBeCancelled(): CompletableFuture<T>
68
69
/**
70
* Create matcher for future completion within duration
71
* @param duration Maximum time to wait for completion
72
* @return Matcher that passes when future completes within time
73
*/
74
fun <T> completeWithin(duration: Duration): Matcher<CompletableFuture<T>>
75
76
/**
77
* Create matcher for successful completion
78
* @return Matcher that passes when future completes successfully
79
*/
80
fun <T> completeSuccessfully(): Matcher<CompletableFuture<T>>
81
82
/**
83
* Create matcher for exceptional completion
84
* @return Matcher that passes when future completes exceptionally
85
*/
86
fun <T> completeExceptionally(): Matcher<CompletableFuture<T>>
87
88
/**
89
* Create matcher for completion with specific value
90
* @param expectedValue The expected completion value
91
* @return Matcher that passes when future completes with expected value
92
*/
93
fun <T> completeWith(expectedValue: T): Matcher<CompletableFuture<T>>
94
95
/**
96
* Create matcher for completion with exception type
97
* @param T The expected exception type
98
* @return Matcher that passes when future completes with specified exception
99
*/
100
inline fun <reified T : Throwable> completeWithException(): Matcher<CompletableFuture<*>>
101
```
102
103
**Usage Examples:**
104
105
```kotlin
106
import io.kotest.matchers.concurrent.*
107
import java.util.concurrent.CompletableFuture
108
import java.time.Duration
109
110
val quickTask = CompletableFuture.supplyAsync { "result" }
111
val slowTask = CompletableFuture.supplyAsync {
112
Thread.sleep(1000)
113
"slow result"
114
}
115
val failingTask = CompletableFuture.supplyAsync<String> {
116
throw RuntimeException("Task failed")
117
}
118
119
// Completion timing
120
quickTask.shouldCompleteWithin(Duration.ofSeconds(1))
121
slowTask.shouldNotCompleteWithin(Duration.ofMillis(100))
122
123
// Completion outcomes
124
quickTask.shouldCompleteSuccessfully()
125
failingTask.shouldCompleteExceptionally()
126
quickTask shouldCompleteWith "result"
127
failingTask.shouldCompleteWithException<RuntimeException>()
128
129
// State validation
130
quickTask.shouldBeDone() // after completion
131
val pendingTask = CompletableFuture<String>()
132
pendingTask.shouldNotBeDone()
133
134
// Using matcher syntax
135
quickTask should completeSuccessfully()
136
failingTask should completeExceptionally()
137
slowTask should completeWithin(Duration.ofSeconds(2))
138
```
139
140
### Atomic Reference Matchers
141
142
Matchers for validating atomic reference types and their thread-safe operations.
143
144
```kotlin { .api }
145
/**
146
* Assert that AtomicReference has specific value
147
* @param expected The expected value
148
* @return The original AtomicReference for chaining
149
*/
150
fun <T> AtomicReference<T>.shouldHaveValue(expected: T): AtomicReference<T>
151
152
/**
153
* Assert that AtomicReference does not have specific value
154
* @param unexpected The value that should not be present
155
* @return The original AtomicReference for chaining
156
*/
157
fun <T> AtomicReference<T>.shouldNotHaveValue(unexpected: T): AtomicReference<T>
158
159
/**
160
* Assert that AtomicInteger has specific value
161
* @param expected The expected integer value
162
* @return The original AtomicInteger for chaining
163
*/
164
infix fun AtomicInteger.shouldHaveValue(expected: Int): AtomicInteger
165
166
/**
167
* Assert that AtomicLong has specific value
168
* @param expected The expected long value
169
* @return The original AtomicLong for chaining
170
*/
171
infix fun AtomicLong.shouldHaveValue(expected: Long): AtomicLong
172
173
/**
174
* Assert that AtomicBoolean has specific value
175
* @param expected The expected boolean value
176
* @return The original AtomicBoolean for chaining
177
*/
178
infix fun AtomicBoolean.shouldHaveValue(expected: Boolean): AtomicBoolean
179
180
/**
181
* Assert that AtomicInteger is greater than specified value
182
* @param value The minimum value (exclusive)
183
* @return The original AtomicInteger for chaining
184
*/
185
infix fun AtomicInteger.shouldBeGreaterThan(value: Int): AtomicInteger
186
187
/**
188
* Assert that AtomicInteger is less than specified value
189
* @param value The maximum value (exclusive)
190
* @return The original AtomicInteger for chaining
191
*/
192
infix fun AtomicInteger.shouldBeLessThan(value: Int): AtomicInteger
193
194
/**
195
* Create matcher for atomic reference value validation
196
* @param expected The expected value
197
* @return Matcher that passes when atomic reference has expected value
198
*/
199
fun <T> haveValue(expected: T): Matcher<AtomicReference<T>>
200
201
/**
202
* Create matcher for atomic integer comparison (greater than)
203
* @param value The minimum value (exclusive)
204
* @return Matcher that passes when atomic integer is greater
205
*/
206
fun beGreaterThan(value: Int): Matcher<AtomicInteger>
207
208
/**
209
* Create matcher for atomic boolean validation
210
* @param expected The expected boolean value
211
* @return Matcher that passes when atomic boolean has expected value
212
*/
213
fun haveValue(expected: Boolean): Matcher<AtomicBoolean>
214
```
215
216
### Channel Matchers
217
218
Matchers for Kotlin coroutines channels including send/receive operations and channel states.
219
220
```kotlin { .api }
221
/**
222
* Assert that channel receives element within specified duration
223
* @param duration Maximum time to wait for receive
224
* @return The received element
225
*/
226
suspend fun <T> Channel<T>.shouldReceiveWithin(duration: Duration): T
227
228
/**
229
* Assert that channel does not receive element within specified duration
230
* @param duration Time to wait before asserting no receive
231
* @return The original Channel for chaining
232
*/
233
suspend fun <T> Channel<T>.shouldNotReceiveWithin(duration: Duration): Channel<T>
234
235
/**
236
* Assert that channel receives specific element within duration
237
* @param expected The expected element
238
* @param duration Maximum time to wait for receive
239
* @return The original Channel for chaining
240
*/
241
suspend fun <T> Channel<T>.shouldReceive(expected: T, duration: Duration): Channel<T>
242
243
/**
244
* Assert that channel is closed
245
* @return The original Channel for chaining
246
*/
247
fun <T> Channel<T>.shouldBeClosed(): Channel<T>
248
249
/**
250
* Assert that channel is not closed
251
* @return The original Channel for chaining
252
*/
253
fun <T> Channel<T>.shouldNotBeClosed(): Channel<T>
254
255
/**
256
* Assert that channel is empty (no elements available)
257
* @return The original Channel for chaining
258
*/
259
fun <T> Channel<T>.shouldBeEmpty(): Channel<T>
260
261
/**
262
* Assert that channel is not empty (has elements available)
263
* @return The original Channel for chaining
264
*/
265
fun <T> Channel<T>.shouldNotBeEmpty(): Channel<T>
266
267
/**
268
* Assert that channel send operation completes within duration
269
* @param element The element to send
270
* @param duration Maximum time to wait for send completion
271
* @return The original Channel for chaining
272
*/
273
suspend fun <T> Channel<T>.shouldSendWithin(element: T, duration: Duration): Channel<T>
274
275
/**
276
* Create matcher for channel receive within duration
277
* @param duration Maximum time to wait
278
* @return Matcher that passes when channel receives within time
279
*/
280
fun <T> receiveWithin(duration: Duration): Matcher<Channel<T>>
281
282
/**
283
* Create matcher for closed channel validation
284
* @return Matcher that passes for closed channels
285
*/
286
fun <T> beClosed(): Matcher<Channel<T>>
287
288
/**
289
* Create matcher for empty channel validation
290
* @return Matcher that passes for empty channels
291
*/
292
fun <T> beEmpty(): Matcher<Channel<T>>
293
```
294
295
**Usage Examples:**
296
297
```kotlin
298
import io.kotest.matchers.concurrent.*
299
import kotlinx.coroutines.*
300
import kotlinx.coroutines.channels.*
301
import java.util.concurrent.atomic.*
302
import java.time.Duration
303
304
// Atomic reference validation
305
val atomicRef = AtomicReference("initial")
306
val atomicInt = AtomicInteger(42)
307
val atomicBool = AtomicBoolean(true)
308
309
atomicRef.shouldHaveValue("initial")
310
atomicInt shouldHaveValue 42
311
atomicBool shouldHaveValue true
312
atomicInt shouldBeGreaterThan 40
313
314
// Channel operations
315
runBlocking {
316
val channel = Channel<String>(Channel.UNLIMITED)
317
318
// Send and receive operations
319
channel.send("message")
320
channel.shouldReceiveWithin(Duration.ofSeconds(1)) shouldBe "message"
321
322
// Channel state validation
323
channel.shouldNotBeClosed()
324
channel.close()
325
channel.shouldBeClosed()
326
327
// Empty channel validation
328
val emptyChannel = Channel<Int>()
329
emptyChannel.shouldBeEmpty()
330
}
331
332
// Using matcher syntax
333
atomicRef should haveValue("initial")
334
atomicInt should beGreaterThan(30)
335
```
336
337
### Thread and Executor Matchers
338
339
Matchers for validating thread states and executor service operations.
340
341
```kotlin { .api }
342
/**
343
* Assert that thread is alive (not terminated)
344
* @return The original Thread for chaining
345
*/
346
fun Thread.shouldBeAlive(): Thread
347
348
/**
349
* Assert that thread is not alive (terminated)
350
* @return The original Thread for chaining
351
*/
352
fun Thread.shouldNotBeAlive(): Thread
353
354
/**
355
* Assert that thread is in specific state
356
* @param state The expected thread state
357
* @return The original Thread for chaining
358
*/
359
infix fun Thread.shouldBeInState(state: Thread.State): Thread
360
361
/**
362
* Assert that thread should not be in specific state
363
* @param state The thread state that should not match
364
* @return The original Thread for chaining
365
*/
366
infix fun Thread.shouldNotBeInState(state: Thread.State): Thread
367
368
/**
369
* Assert that thread is daemon thread
370
* @return The original Thread for chaining
371
*/
372
fun Thread.shouldBeDaemon(): Thread
373
374
/**
375
* Assert that thread is not daemon thread
376
* @return The original Thread for chaining
377
*/
378
fun Thread.shouldNotBeDaemon(): Thread
379
380
/**
381
* Assert that ExecutorService is shutdown
382
* @return The original ExecutorService for chaining
383
*/
384
fun ExecutorService.shouldBeShutdown(): ExecutorService
385
386
/**
387
* Assert that ExecutorService is not shutdown
388
* @return The original ExecutorService for chaining
389
*/
390
fun ExecutorService.shouldNotBeShutdown(): ExecutorService
391
392
/**
393
* Assert that ExecutorService is terminated
394
* @return The original ExecutorService for chaining
395
*/
396
fun ExecutorService.shouldBeTerminated(): ExecutorService
397
398
/**
399
* Assert that ExecutorService terminates within specified duration
400
* @param duration Maximum time to wait for termination
401
* @return The original ExecutorService for chaining
402
*/
403
fun ExecutorService.shouldTerminateWithin(duration: Duration): ExecutorService
404
405
/**
406
* Create matcher for thread state validation
407
* @param state The expected thread state
408
* @return Matcher that passes when thread is in specified state
409
*/
410
fun beInState(state: Thread.State): Matcher<Thread>
411
412
/**
413
* Create matcher for alive thread validation
414
* @return Matcher that passes for alive threads
415
*/
416
fun beAlive(): Matcher<Thread>
417
418
/**
419
* Create matcher for daemon thread validation
420
* @return Matcher that passes for daemon threads
421
*/
422
fun beDaemon(): Matcher<Thread>
423
424
/**
425
* Create matcher for shutdown executor validation
426
* @return Matcher that passes for shutdown executors
427
*/
428
fun beShutdown(): Matcher<ExecutorService>
429
430
/**
431
* Create matcher for executor termination validation
432
* @param duration Maximum time to wait for termination
433
* @return Matcher that passes when executor terminates within duration
434
*/
435
fun terminateWithin(duration: Duration): Matcher<ExecutorService>
436
```
437
438
### Lock and Synchronization Matchers
439
440
Matchers for validating locks, semaphores, and other synchronization primitives.
441
442
```kotlin { .api }
443
/**
444
* Assert that ReentrantLock is locked
445
* @return The original ReentrantLock for chaining
446
*/
447
fun ReentrantLock.shouldBeLocked(): ReentrantLock
448
449
/**
450
* Assert that ReentrantLock is not locked (unlocked)
451
* @return The original ReentrantLock for chaining
452
*/
453
fun ReentrantLock.shouldNotBeLocked(): ReentrantLock
454
455
/**
456
* Assert that ReentrantLock is held by current thread
457
* @return The original ReentrantLock for chaining
458
*/
459
fun ReentrantLock.shouldBeHeldByCurrentThread(): ReentrantLock
460
461
/**
462
* Assert that ReentrantLock is not held by current thread
463
* @return The original ReentrantLock for chaining
464
*/
465
fun ReentrantLock.shouldNotBeHeldByCurrentThread(): ReentrantLock
466
467
/**
468
* Assert that ReentrantLock has specific hold count
469
* @param count Expected number of times lock is held by current thread
470
* @return The original ReentrantLock for chaining
471
*/
472
infix fun ReentrantLock.shouldHaveHoldCount(count: Int): ReentrantLock
473
474
/**
475
* Assert that Semaphore has specific number of available permits
476
* @param permits Expected number of available permits
477
* @return The original Semaphore for chaining
478
*/
479
infix fun Semaphore.shouldHaveAvailablePermits(permits: Int): Semaphore
480
481
/**
482
* Assert that Semaphore has no available permits
483
* @return The original Semaphore for chaining
484
*/
485
fun Semaphore.shouldHaveNoAvailablePermits(): Semaphore
486
487
/**
488
* Assert that CountDownLatch count is zero (released)
489
* @return The original CountDownLatch for chaining
490
*/
491
fun CountDownLatch.shouldBeReleased(): CountDownLatch
492
493
/**
494
* Assert that CountDownLatch count is not zero (not released)
495
* @return The original CountDownLatch for chaining
496
*/
497
fun CountDownLatch.shouldNotBeReleased(): CountDownLatch
498
499
/**
500
* Assert that CountDownLatch has specific count
501
* @param count Expected latch count
502
* @return The original CountDownLatch for chaining
503
*/
504
infix fun CountDownLatch.shouldHaveCount(count: Long): CountDownLatch
505
506
/**
507
* Create matcher for locked state validation
508
* @return Matcher that passes for locked locks
509
*/
510
fun beLocked(): Matcher<ReentrantLock>
511
512
/**
513
* Create matcher for current thread lock ownership
514
* @return Matcher that passes when current thread holds lock
515
*/
516
fun beHeldByCurrentThread(): Matcher<ReentrantLock>
517
518
/**
519
* Create matcher for lock hold count validation
520
* @param count Expected hold count
521
* @return Matcher that passes when lock has expected hold count
522
*/
523
fun haveHoldCount(count: Int): Matcher<ReentrantLock>
524
525
/**
526
* Create matcher for semaphore permits validation
527
* @param permits Expected available permits
528
* @return Matcher that passes when semaphore has expected permits
529
*/
530
fun haveAvailablePermits(permits: Int): Matcher<Semaphore>
531
532
/**
533
* Create matcher for latch release validation
534
* @return Matcher that passes for released latches
535
*/
536
fun beReleased(): Matcher<CountDownLatch>
537
```
538
539
**Usage Examples:**
540
541
```kotlin
542
import io.kotest.matchers.concurrent.*
543
import java.util.concurrent.*
544
import java.util.concurrent.locks.ReentrantLock
545
import java.time.Duration
546
547
// Thread validation
548
val thread = Thread {
549
Thread.sleep(1000)
550
}
551
thread.start()
552
553
thread.shouldBeAlive()
554
thread shouldBeInState Thread.State.RUNNABLE
555
thread.shouldNotBeDaemon()
556
557
// Executor service validation
558
val executor = Executors.newFixedThreadPool(2)
559
executor.shouldNotBeShutdown()
560
561
executor.shutdown()
562
executor.shouldBeShutdown()
563
executor.shouldTerminateWithin(Duration.ofSeconds(5))
564
565
// Lock validation
566
val lock = ReentrantLock()
567
lock.shouldNotBeLocked()
568
569
lock.lock()
570
try {
571
lock.shouldBeLocked()
572
lock.shouldBeHeldByCurrentThread()
573
lock shouldHaveHoldCount 1
574
} finally {
575
lock.unlock()
576
}
577
578
// Semaphore validation
579
val semaphore = Semaphore(3)
580
semaphore shouldHaveAvailablePermits 3
581
582
semaphore.acquire()
583
semaphore shouldHaveAvailablePermits 2
584
585
// CountDownLatch validation
586
val latch = CountDownLatch(2)
587
latch.shouldNotBeReleased()
588
latch shouldHaveCount 2L
589
590
latch.countDown()
591
latch shouldHaveCount 1L
592
593
latch.countDown()
594
latch.shouldBeReleased()
595
596
// Using matcher syntax
597
thread should beAlive()
598
lock should beLocked()
599
semaphore should haveAvailablePermits(2)
600
latch should beReleased()
601
```
602
603
### Concurrent Collection Matchers
604
605
Matchers for thread-safe collections and their concurrent properties.
606
607
```kotlin { .api }
608
/**
609
* Assert that ConcurrentMap contains key-value pair atomically
610
* @param key The expected key
611
* @param value The expected value
612
* @return The original ConcurrentMap for chaining
613
*/
614
fun <K, V> ConcurrentMap<K, V>.shouldContainAtomically(key: K, value: V): ConcurrentMap<K, V>
615
616
/**
617
* Assert that ConcurrentHashMap is empty atomically
618
* @return The original ConcurrentHashMap for chaining
619
*/
620
fun <K, V> ConcurrentHashMap<K, V>.shouldBeEmptyAtomically(): ConcurrentHashMap<K, V>
621
622
/**
623
* Assert that BlockingQueue contains specific element
624
* @param element The element that should be present
625
* @return The original BlockingQueue for chaining
626
*/
627
fun <T> BlockingQueue<T>.shouldContain(element: T): BlockingQueue<T>
628
629
/**
630
* Assert that BlockingQueue is empty
631
* @return The original BlockingQueue for chaining
632
*/
633
fun <T> BlockingQueue<T>.shouldBeEmpty(): BlockingQueue<T>
634
635
/**
636
* Assert that BlockingQueue has specific size
637
* @param size Expected queue size
638
* @return The original BlockingQueue for chaining
639
*/
640
infix fun <T> BlockingQueue<T>.shouldHaveSize(size: Int): BlockingQueue<T>
641
642
/**
643
* Assert that BlockingQueue offers element within duration
644
* @param element The element to offer
645
* @param duration Maximum time to wait for offer
646
* @return The original BlockingQueue for chaining
647
*/
648
suspend fun <T> BlockingQueue<T>.shouldOfferWithin(element: T, duration: Duration): BlockingQueue<T>
649
650
/**
651
* Create matcher for concurrent map containment
652
* @param key Expected key
653
* @param value Expected value
654
* @return Matcher that passes when map contains key-value pair
655
*/
656
fun <K, V> containAtomically(key: K, value: V): Matcher<ConcurrentMap<K, V>>
657
658
/**
659
* Create matcher for blocking queue element validation
660
* @param element Expected element
661
* @return Matcher that passes when queue contains element
662
*/
663
fun <T> contain(element: T): Matcher<BlockingQueue<T>>
664
665
/**
666
* Create matcher for queue size validation
667
* @param size Expected size
668
* @return Matcher that passes when queue has expected size
669
*/
670
fun <T> haveSize(size: Int): Matcher<BlockingQueue<T>>
671
```
672
673
**Usage Examples:**
674
675
```kotlin
676
import io.kotest.matchers.concurrent.*
677
import java.util.concurrent.*
678
679
// Concurrent collections
680
val concurrentMap = ConcurrentHashMap<String, Int>()
681
val blockingQueue = LinkedBlockingQueue<String>()
682
683
// Map operations
684
concurrentMap["key"] = 42
685
concurrentMap.shouldContainAtomically("key", 42)
686
687
// Queue operations
688
blockingQueue.offer("item1")
689
blockingQueue.offer("item2")
690
691
blockingQueue.shouldContain("item1")
692
blockingQueue shouldHaveSize 2
693
blockingQueue.shouldNotBeEmpty()
694
695
// Clear and verify
696
blockingQueue.clear()
697
blockingQueue.shouldBeEmpty()
698
699
// Using matcher syntax
700
concurrentMap should containAtomically("key", 42)
701
blockingQueue should haveSize(0)
702
```
703
704
## Error Handling
705
706
Concurrency matchers provide detailed error information for assertion failures:
707
708
- **Timing failures**: Show actual vs expected durations with precise timing measurements
709
- **State failures**: Indicate expected vs actual thread/executor states with transition information
710
- **Completion failures**: Details about future completion status and any exceptions thrown
711
- **Atomic operation failures**: Show expected vs actual atomic values with concurrent access context
712
- **Channel failures**: Information about channel capacity, buffering, and element availability
713
- **Lock failures**: Details about lock ownership, hold counts, and contention information
714
715
All concurrency matchers handle race conditions appropriately and provide thread-safe assertions with meaningful error messages that help debug concurrent code issues.