|
4 | 4 |
|
5 | 5 | package kotlinx.coroutines.debug.junit4
|
6 | 6 |
|
7 |
| -import kotlinx.coroutines.* |
8 | 7 | import kotlinx.coroutines.debug.*
|
9 | 8 | import org.junit.runner.*
|
10 | 9 | import org.junit.runners.model.*
|
11 | 10 | import java.util.concurrent.*
|
12 | 11 |
|
13 | 12 | internal class CoroutinesTimeoutStatement(
|
14 |
| - private val testStatement: Statement, private val testDescription: Description, |
| 13 | + testStatement: Statement, |
| 14 | + private val testDescription: Description, |
15 | 15 | private val testTimeoutMs: Long,
|
16 | 16 | private val cancelOnTimeout: Boolean = false
|
17 | 17 | ) : Statement() {
|
18 | 18 |
|
19 |
| - private val testExecutor = Executors.newSingleThreadExecutor { |
20 |
| - Thread(it).apply { |
21 |
| - name = "Timeout test executor" |
22 |
| - isDaemon = true |
23 |
| - } |
| 19 | + private val testStartedLatch = CountDownLatch(1) |
| 20 | + |
| 21 | + private val testResult = FutureTask<Unit> { |
| 22 | + testStartedLatch.countDown() |
| 23 | + testStatement.evaluate() |
24 | 24 | }
|
25 | 25 |
|
26 |
| - // Thread to dump stack from, captured by testExecutor |
27 |
| - private lateinit var testThread: Thread |
| 26 | + /* |
| 27 | + * We are using hand-rolled thread instead of single thread executor |
| 28 | + * in order to be able to safely interrupt thread in the end of a test |
| 29 | + */ |
| 30 | + private val testThread = Thread(testResult, "Timeout test thread").apply { isDaemon = true } |
28 | 31 |
|
29 | 32 | override fun evaluate() {
|
30 |
| - DebugProbes.install() // Fail-fast if probes are unavailable |
31 |
| - val latch = CountDownLatch(1) |
32 |
| - val testFuture = CompletableFuture.runAsync(Runnable { |
33 |
| - testThread = Thread.currentThread() |
34 |
| - latch.countDown() |
35 |
| - testStatement.evaluate() |
36 |
| - }, testExecutor) |
37 |
| - |
38 |
| - latch.await() // Await until test is started |
| 33 | + DebugProbes.install() |
| 34 | + testThread.start() |
| 35 | + // Await until test is started to take only test execution time into account |
| 36 | + testStartedLatch.await() |
39 | 37 | try {
|
40 |
| - testFuture.get(testTimeoutMs, TimeUnit.MILLISECONDS) |
| 38 | + testResult.get(testTimeoutMs, TimeUnit.MILLISECONDS) |
41 | 39 | return
|
42 | 40 | } catch (e: TimeoutException) {
|
43 | 41 | handleTimeout(testDescription)
|
44 | 42 | } catch (e: ExecutionException) {
|
45 | 43 | throw e.cause ?: e
|
46 | 44 | } finally {
|
47 | 45 | DebugProbes.uninstall()
|
48 |
| - testExecutor.shutdown() |
49 | 46 | }
|
50 | 47 | }
|
51 | 48 |
|
52 | 49 | private fun handleTimeout(description: Description) {
|
53 | 50 | val units =
|
54 |
| - if (testTimeoutMs % 1000L == 0L) |
| 51 | + if (testTimeoutMs % 1000 == 0L) |
55 | 52 | "${testTimeoutMs / 1000} seconds"
|
56 | 53 | else "$testTimeoutMs milliseconds"
|
57 | 54 |
|
|
0 commit comments