• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }