1 package org.junit.tests.internal.runners.statements;
2
3 import static java.lang.Long.MAX_VALUE;
4 import static java.lang.Math.atan;
5 import static java.lang.System.currentTimeMillis;
6 import static java.lang.Thread.sleep;
7 import static org.hamcrest.core.Is.is;
8 import static org.junit.Assert.assertEquals;
9 import static org.junit.Assert.assertFalse;
10 import static org.junit.Assert.assertTrue;
11 import static org.junit.Assert.fail;
12
13 import java.util.concurrent.TimeUnit;
14
15 import org.junit.Ignore;
16 import org.junit.Rule;
17 import org.junit.Test;
18 import org.junit.internal.runners.statements.FailOnTimeout;
19 import org.junit.rules.ExpectedException;
20 import org.junit.runners.model.Statement;
21 import org.junit.runners.model.TestTimedOutException;
22
23
24
25
26 public class FailOnTimeoutTest {
27 private static final int TIMEOUT = 100;
28 private static final int DURATION_THAT_EXCEEDS_TIMEOUT = 60 * 60 * 1000;
29
30 @Rule
31 public final ExpectedException thrown = ExpectedException.none();
32
33 private final TestStatement statement = new TestStatement();
34
35 private final FailOnTimeout failOnTimeout = new FailOnTimeout(statement,
36 TIMEOUT);
37
38 @Test
39 public void throwsTestTimedOutException() throws Throwable {
40 thrown.expect(TestTimedOutException.class);
41 evaluateWithWaitDuration(DURATION_THAT_EXCEEDS_TIMEOUT);
42 }
43
44 @Test
45 public void throwExceptionWithNiceMessageOnTimeout() throws Throwable {
46 thrown.expectMessage("test timed out after 100 milliseconds");
47 evaluateWithWaitDuration(DURATION_THAT_EXCEEDS_TIMEOUT);
48 }
49
50 @Test
51 public void sendUpExceptionThrownByStatement() throws Throwable {
52 RuntimeException exception = new RuntimeException();
53 thrown.expect(is(exception));
54 evaluateWithException(exception);
55 }
56
57 @Test
58 public void throwExceptionIfTheSecondCallToEvaluateNeedsTooMuchTime()
59 throws Throwable {
60 thrown.expect(TestTimedOutException.class);
61 evaluateWithWaitDuration(0);
62 evaluateWithWaitDuration(DURATION_THAT_EXCEEDS_TIMEOUT);
63 }
64
65 @Test
66 public void throwTimeoutExceptionOnSecondCallAlthoughFirstCallThrowsException()
67 throws Throwable {
68 thrown.expectMessage("test timed out after 100 milliseconds");
69 try {
70 evaluateWithException(new RuntimeException());
71 } catch (Throwable expected) {
72 }
73 evaluateWithWaitDuration(DURATION_THAT_EXCEEDS_TIMEOUT);
74 }
75
76 @Test
77 public void throwsExceptionWithTimeoutValueAndTimeUnitSet()
78 throws Throwable {
79 try {
80 evaluateWithWaitDuration(DURATION_THAT_EXCEEDS_TIMEOUT);
81 fail("No exception was thrown when test timed out");
82 } catch (TestTimedOutException e) {
83 assertEquals(TIMEOUT, e.getTimeout());
84 assertEquals(TimeUnit.MILLISECONDS, e.getTimeUnit());
85 }
86 }
87
88 private void evaluateWithException(Exception exception) throws Throwable {
89 statement.nextException = exception;
90 statement.waitDuration = 0;
91 failOnTimeout.evaluate();
92 }
93
94 private void evaluateWithWaitDuration(int waitDuration) throws Throwable {
95 statement.nextException = null;
96 statement.waitDuration = waitDuration;
97 failOnTimeout.evaluate();
98 }
99
100 private static final class TestStatement extends Statement {
101 int waitDuration;
102
103 Exception nextException;
104
105 @Override
106 public void evaluate() throws Throwable {
107 sleep(waitDuration);
108 if (nextException != null) {
109 throw nextException;
110 }
111 }
112 }
113
114 @Test
115 public void stopEndlessStatement() throws Throwable {
116 InfiniteLoopStatement infiniteLoop = new InfiniteLoopStatement();
117 FailOnTimeout infiniteLoopTimeout = new FailOnTimeout(infiniteLoop,
118 TIMEOUT);
119 try {
120 infiniteLoopTimeout.evaluate();
121 } catch (Exception timeoutException) {
122 sleep(20);
123 int firstCount = InfiniteLoopStatement.COUNT;
124 sleep(20);
125 assertTrue("Thread has not been stopped.",
126 firstCount == InfiniteLoopStatement.COUNT);
127 }
128 }
129
130 private static final class InfiniteLoopStatement extends Statement {
131 private static int COUNT = 0;
132
133 @Override
134 public void evaluate() throws Throwable {
135 while (true) {
136 sleep(10);
137 ++COUNT;
138 }
139 }
140 }
141
142 @Test
143 public void stackTraceContainsRealCauseOfTimeout() throws Throwable {
144 StuckStatement stuck = new StuckStatement();
145 FailOnTimeout stuckTimeout = new FailOnTimeout(stuck, TIMEOUT);
146 try {
147 stuckTimeout.evaluate();
148
149 fail("Expected timeout exception");
150 } catch (Exception timeoutException) {
151 StackTraceElement[] stackTrace = timeoutException.getStackTrace();
152 boolean stackTraceContainsTheRealCauseOfTheTimeout = false;
153 boolean stackTraceContainsOtherThanTheRealCauseOfTheTimeout = false;
154 for (StackTraceElement element : stackTrace) {
155 String methodName = element.getMethodName();
156 if ("theRealCauseOfTheTimeout".equals(methodName)) {
157 stackTraceContainsTheRealCauseOfTheTimeout = true;
158 }
159 if ("notTheRealCauseOfTheTimeout".equals(methodName)) {
160 stackTraceContainsOtherThanTheRealCauseOfTheTimeout = true;
161 }
162 }
163 assertTrue(
164 "Stack trace does not contain the real cause of the timeout",
165 stackTraceContainsTheRealCauseOfTheTimeout);
166 assertFalse(
167 "Stack trace contains other than the real cause of the timeout, which can be very misleading",
168 stackTraceContainsOtherThanTheRealCauseOfTheTimeout);
169 }
170 }
171
172 private static final class StuckStatement extends Statement {
173
174 @Override
175 public void evaluate() throws Throwable {
176 try {
177
178 theRealCauseOfTheTimeout();
179 } catch (InterruptedException e) {
180 } finally {
181
182 notTheRealCauseOfTheTimeout();
183 }
184 }
185
186 private void theRealCauseOfTheTimeout() throws InterruptedException {
187 sleep(MAX_VALUE);
188 }
189
190 private void notTheRealCauseOfTheTimeout() {
191 for (long now = currentTimeMillis(), eta = now + 1000L; now < eta; now = currentTimeMillis()) {
192
193 atan(now);
194 }
195 }
196 }
197 }