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