Тестирование произвольного кода
Lincheck предоставляет функцию runConcurrentTest() для тестирования произвольного конкурентного кода.
Функция runConcurrentTest() выполняет блок конкурентного кода несколько раз и использует проверку модели для исследования возможных расписаний выполнения.
Чтобы протестировать конкурентный код с помощью Lincheck:
-
Создайте тестовый класс:
class CounterTestWithInvocations { } -
Создайте тестовую функцию как функцию-член, используя
runConcurrentTest():@Test fun test() = Lincheck.runConcurrentTest(100_000) { var counter = 0 // Increments the counter concurrently val t1 = thread { counter++ } val t2 = thread { counter++ } // Waits for the threads to finish t1.join() t2.join() // Checks that both increments have been applied assertEquals(2, counter) } -
Запустите тест. Если он завершится неудачно, Lincheck создаст отчёт с расписанием выполнения, приводящим к неправильному поведению.
| ------------------------------------------------------------------------------- | | Main Thread | Thread 1 | Thread 2 | | ------------------------------------------------------------------------------- | | thread(block = Lambda#2): Thread#1 | | | | thread(block = Lambda#3): Thread#2 | | | | switch (reason: waiting for Thread 1 to finish) | | | | | | run() | | | | counter ➜ 0 | | | | switch | | | run() | | | | counter ➜ 0 | | | | counter = 1 | | | | | counter = 1 | | Thread#1.join() | | | | Thread#2.join() | | | | counter.element ➜ 1 | | | | assertEquals(2, 1): threw AssertionFailedError | | | | ------------------------------------------------------------------------------- |
Пример: тестирование функций ConcurrentHashMap
Рассмотрим этот тест для функций ConcurrentHashMap:
package org.lincheck.docs
import org.jetbrains.lincheck.*
import kotlin.test.Test
import java.util.concurrent.*
import kotlin.concurrent.*
// This test demonstrates a deadlock caused by two threads
// performing nested `computeIfAbsent` calls in opposite order.
class ConcurrentHashMapDeadlockTest {
@Test
fun test() = Lincheck.runConcurrentTest {
val map = ConcurrentHashMap<String, String>()
// Updates `key2` while locking `key1`.
val thread1 = thread {
map.computeIfAbsent("key1") {
map.computeIfAbsent("key2") { "value2" }
"value1"
}
}
// Updates `key1` while locking `key2`.
val thread2 = thread {
map.computeIfAbsent("key2") {
map.computeIfAbsent("key1") { "value1" }
"value2"
}
}
// Wait until both threads complete.
thread1.join()
thread2.join()
}
}
Тест завершается неудачно, поскольку Lincheck находит расписание выполнения, приводящее к взаимной блокировке:
Поток 2 сопоставляет
key2корзине с индексом 1, блокирует эту корзину и начинает выполнятьcomputeIfAbsent("key1"). Выполнение переключается с потока 2 на поток 1 до того, как поток 2 сопоставитkey1и заблокирует корзину сkey1.Поток 1 сопоставляет
key1корзине с индексом 0, блокирует эту корзину и начинает выполнятьcomputeIfAbsent("key2"). Поток 1 сопоставляетkey2корзине с индексом 1 и пытается заблокировать эту корзину, но она уже заблокирована потоком 2. Выполнение переключается с потока 1 на поток 2.Поток 2 пытается заблокировать корзину с
key1, но она уже заблокирована потоком 1.
Оба потока заблокированы, поэтому выполнение привело к взаимной блокировке.
Что дальше
Узнайте, как тестировать структуры данных с помощью Lincheck.
См. также
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/lincheck-testing-arbitrary-code.html