1 /*
2 * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
5 package kotlinx.coroutines.debug.junit5
6
7 import org.assertj.core.api.*
8 import org.junit.Ignore
9 import org.junit.Assert.*
10 import org.junit.Test
11 import org.junit.platform.engine.*
12 import org.junit.platform.engine.discovery.DiscoverySelectors.*
13 import org.junit.platform.testkit.engine.*
14 import org.junit.platform.testkit.engine.EventConditions.*
15 import java.io.*
16
17 // note that these tests are run using JUnit4 in order not to mix the testing systems.
18 class CoroutinesTimeoutTest {
19
20 // This test is ignored because it just checks an example.
21 @Test
22 @Ignore
testRegisterExtensionExamplenull23 fun testRegisterExtensionExample() {
24 val capturedOut = ByteArrayOutputStream()
25 eventsForSelector(selectClass(RegisterExtensionExample::class.java), capturedOut)
26 .testTimedOut("testThatHangs", 5000)
27 }
28
29 @Test
testCoroutinesTimeoutSimplenull30 fun testCoroutinesTimeoutSimple() {
31 val capturedOut = ByteArrayOutputStream()
32 eventsForSelector(selectClass(CoroutinesTimeoutSimpleTest::class.java), capturedOut)
33 .testFinishedSuccessfully("ignoresClassTimeout")
34 .testFinishedSuccessfully("fitsInClassTimeout")
35 .testTimedOut("usesClassTimeout1", 100)
36 .testTimedOut("usesMethodTimeout", 200)
37 .testTimedOut("usesClassTimeout2", 100)
38 assertEquals(capturedOut.toString(), 3, countDumps(capturedOut))
39 }
40
41 @Test
testCoroutinesTimeoutMethodnull42 fun testCoroutinesTimeoutMethod() {
43 val capturedOut = ByteArrayOutputStream()
44 eventsForSelector(selectClass(CoroutinesTimeoutMethodTest::class.java), capturedOut)
45 .testFinishedSuccessfully("fitsInMethodTimeout")
46 .testFinishedSuccessfully("noClassTimeout")
47 .testTimedOut("usesMethodTimeoutWithNoClassTimeout", 100)
48 assertEquals(capturedOut.toString(), 1, countDumps(capturedOut))
49 }
50
51 @Test
testCoroutinesTimeoutNestednull52 fun testCoroutinesTimeoutNested() {
53 val capturedOut = ByteArrayOutputStream()
54 eventsForSelector(selectClass(CoroutinesTimeoutNestedTest::class.java), capturedOut)
55 .testFinishedSuccessfully("fitsInOuterClassTimeout")
56 .testTimedOut("usesOuterClassTimeout", 200)
57 assertEquals(capturedOut.toString(), 1, countDumps(capturedOut))
58 }
59
60 @Test
testCoroutinesTimeoutInheritanceWithNoTimeoutInDerivednull61 fun testCoroutinesTimeoutInheritanceWithNoTimeoutInDerived() {
62 val capturedOut = ByteArrayOutputStream()
63 eventsForSelector(selectClass(CoroutinesTimeoutInheritanceTest.InheritedWithNoTimeout::class.java), capturedOut)
64 .testFinishedSuccessfully("methodOverridesBaseClassTimeoutWithGreaterTimeout")
65 .testTimedOut("usesBaseClassTimeout", 100)
66 .testTimedOut("methodOverridesBaseClassTimeoutWithLesserTimeout", 10)
67 assertEquals(capturedOut.toString(), 2, countDumps(capturedOut))
68 }
69
70 @Test
testCoroutinesTimeoutInheritanceWithGreaterTimeoutInDerivednull71 fun testCoroutinesTimeoutInheritanceWithGreaterTimeoutInDerived() {
72 val capturedOut = ByteArrayOutputStream()
73 eventsForSelector(
74 selectClass(CoroutinesTimeoutInheritanceTest.InheritedWithGreaterTimeout::class.java),
75 capturedOut
76 )
77 .testFinishedSuccessfully("classOverridesBaseClassTimeout1")
78 .testTimedOut("classOverridesBaseClassTimeout2", 300)
79 assertEquals(capturedOut.toString(), 1, countDumps(capturedOut))
80 }
81
82 /* Currently there's no ability to replicate [TestFailureValidation] as is for JUnit5:
83 https://github.com/junit-team/junit5/issues/506. So, the test mechanism is more ad-hoc. */
84
85 @Test
testCoroutinesTimeoutExtensionDisabledTracesnull86 fun testCoroutinesTimeoutExtensionDisabledTraces() {
87 val capturedOut = ByteArrayOutputStream()
88 eventsForSelector(selectClass(CoroutinesTimeoutExtensionTest.DisabledStackTracesTest::class.java), capturedOut)
89 .testTimedOut("hangingTest", 500)
90 assertEquals(false, capturedOut.toString().contains("Coroutine creation stacktrace"))
91 assertEquals(capturedOut.toString(), 1, countDumps(capturedOut))
92 }
93
94 @Test
testCoroutinesTimeoutExtensionEagernull95 fun testCoroutinesTimeoutExtensionEager() {
96 val capturedOut = ByteArrayOutputStream()
97 eventsForSelector(selectClass(CoroutinesTimeoutExtensionTest.EagerTest::class.java), capturedOut)
98 .testTimedOut("hangingTest", 500)
99 for (expectedPart in listOf("hangForever", "waitForHangJob", "BlockingCoroutine{Active}")) {
100 assertEquals(expectedPart, true, capturedOut.toString().contains(expectedPart))
101 }
102 assertEquals(capturedOut.toString(), 1, countDumps(capturedOut))
103 }
104
105 @Test
testCoroutinesTimeoutExtensionSimplenull106 fun testCoroutinesTimeoutExtensionSimple() {
107 val capturedOut = ByteArrayOutputStream()
108 eventsForSelector(selectClass(CoroutinesTimeoutExtensionTest.SimpleTest::class.java), capturedOut)
109 .testFinishedSuccessfully("successfulTest")
110 .testTimedOut("hangingTest", 1000)
111 .haveExactly(1, event(
112 test("throwingTest"),
113 finishedWithFailure(Condition({ it is RuntimeException}, "is RuntimeException"))
114 ))
115 for (expectedPart in listOf("suspendForever", "invokeSuspend", "BlockingCoroutine{Active}")) {
116 assertEquals(expectedPart, true, capturedOut.toString().contains(expectedPart))
117 }
118 for (nonExpectedPart in listOf("delay", "throwingTest")) {
119 assertEquals(nonExpectedPart, false, capturedOut.toString().contains(nonExpectedPart))
120 }
121 assertEquals(capturedOut.toString(), 1, countDumps(capturedOut))
122 }
123 }
124
eventsForSelectornull125 private fun eventsForSelector(selector: DiscoverySelector, capturedOut: OutputStream): ListAssert<Event> {
126 val systemOut: PrintStream = System.out
127 val systemErr: PrintStream = System.err
128 return try {
129 System.setOut(PrintStream(capturedOut))
130 System.setErr(PrintStream(capturedOut))
131 EngineTestKit.engine("junit-jupiter")
132 .selectors(selector)
133 .execute()
134 .testEvents()
135 .assertThatEvents()
136 } finally {
137 System.setOut(systemOut)
138 System.setErr(systemErr)
139 }
140 }
141
testFinishedSuccessfullynull142 private fun ListAssert<Event>.testFinishedSuccessfully(testName: String): ListAssert<Event> =
143 haveExactly(1, event(
144 test(testName),
145 finishedSuccessfully()
146 ))
147
148 private fun ListAssert<Event>.testTimedOut(testName: String, after: Long): ListAssert<Event> =
149 haveExactly(1, event(
150 test(testName),
151 finishedWithFailure(Condition({ it is CoroutinesTimeoutException && it.timeoutMs == after },
152 "is CoroutinesTimeoutException($after)"))
153 ))
154
155 /** Counts the number of occurrences of "Coroutines dump" in [capturedOut] */
countDumpsnull156 private fun countDumps(capturedOut: ByteArrayOutputStream): Int {
157 var result = 0
158 val outStr = capturedOut.toString()
159 val header = "Coroutines dump"
160 var i = 0
161 while (i < outStr.length - header.length) {
162 if (outStr.substring(i, i + header.length) == header) {
163 result += 1
164 i += header.length
165 } else {
166 i += 1
167 }
168 }
169 return result
170 }