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