-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Completely custom timeout approach as suggested by dsaff in #727 #797
Changes from all commits
20ab42b
d86f263
1da06bb
a16df7a
b494e3b
dedfbe8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package org.junit.tests.running.methods; | ||
|
||
import static org.hamcrest.CoreMatchers.containsString; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertThat; | ||
|
||
import java.io.PrintWriter; | ||
import java.io.StringWriter; | ||
import java.io.Writer; | ||
import java.util.List; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.internal.runners.statements.FailOnTimeout; | ||
import org.junit.rules.TestRule; | ||
import org.junit.rules.Timeout; | ||
import org.junit.runner.Description; | ||
import org.junit.runner.JUnitCore; | ||
import org.junit.runner.Result; | ||
import org.junit.runners.model.Statement; | ||
import org.junit.tests.running.methods.TimeoutTest.InfiniteLoopMultithreaded; | ||
|
||
public class CustomTimeoutTest { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test seems unrelated to the change. I understand that you want to preserve your original code somewhere, but I'm not sure if we should be maintaining that in a test in the JUnit code base. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you refer to the However, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dare to make one last attempt to change your mind, before I would actually delete
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@reinholdfuereder Then I think we should write tests to verify those changes. In other words, have the tests verify the contract that your subclass is expecting.
I agree, but we need to make sure that the tests are maintainable, and that it's clear what's being tested (so if a refactoring breaks a test, we can tell if the problem is with the refactoring or with the test). I like tests, too. I think what you have here is bigger than it needs to be, that's all. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kcooney I hope that the recent change to shorten the test and getting rid of the whole full thread dump creation + logging is sufficient. (Please note I am not fully happy about |
||
|
||
public static class CustomTimeoutHandler { | ||
|
||
public Exception handleTimeout(Thread thread) { | ||
String prefix = "[" + getClass().getSimpleName() + "] "; | ||
return new Exception(prefix + "Appears to be stuck due to running into timeout. Here could be some more custom failure context..."); | ||
} | ||
|
||
} | ||
|
||
|
||
public static class CustomFailOnTimeout extends FailOnTimeout { | ||
|
||
private CustomTimeoutHandler handler = new CustomTimeoutHandler(); | ||
|
||
public CustomFailOnTimeout(Statement base, long timeout, | ||
TimeUnit unit, boolean lookForStuckThread) { | ||
super(base, timeout, unit, lookForStuckThread); | ||
} | ||
|
||
@Override | ||
protected List<Throwable> createAdditionalTimeoutExceptions( | ||
Thread thread) { | ||
List<Throwable> exceptions = super.createAdditionalTimeoutExceptions(thread); | ||
Throwable handleTimeout = handler.handleTimeout(thread); | ||
exceptions.add(handleTimeout); | ||
return exceptions; | ||
} | ||
|
||
} | ||
|
||
public static class CustomTimeout extends Timeout { | ||
|
||
public CustomTimeout(int timeout, TimeUnit unit) { | ||
this(timeout, unit, false); | ||
} | ||
|
||
public CustomTimeout(int timeout, TimeUnit unit, boolean lookForStuckThread) { | ||
super (new Timeout(timeout, unit), lookForStuckThread); | ||
} | ||
|
||
@Override | ||
public Statement apply(Statement base, Description description) { | ||
return new CustomFailOnTimeout(base, getTimeout(), getTimeUnit(), isLookForStuckThread()); | ||
} | ||
} | ||
|
||
|
||
public static class InfiniteLoopTest { | ||
|
||
@Rule | ||
public TestRule globalTimeout = new CustomTimeout(100, TimeUnit.MILLISECONDS); | ||
|
||
@Test | ||
public void failure() { | ||
infiniteLoop(); | ||
} | ||
|
||
private void infiniteLoop() { | ||
for (; ; ) { | ||
try { | ||
Thread.sleep(10); | ||
} catch (InterruptedException e) { | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Test | ||
public void infiniteLoop() throws Exception { | ||
JUnitCore core = new JUnitCore(); | ||
Result result = core.run(InfiniteLoopTest.class); | ||
assertEquals(1, result.getRunCount()); | ||
assertEquals(2, result.getFailureCount()); | ||
Throwable exception[] = new Throwable[2]; | ||
for (int i = 0; i < 2; i++) | ||
exception[i] = result.getFailures().get(i).getException(); | ||
assertThat(exception[0].getMessage(), containsString("test timed out after 100 milliseconds")); | ||
assertThat(exception[1].getMessage(), containsString("[CustomTimeoutHandler] Appears to be stuck due to running into timeout. Here could be some more custom failure context...")); | ||
} | ||
|
||
|
||
// -------------------------------------------------------------------------------------------------- | ||
// Below is the additional code for test scenario when the timeout is also looking for stuck threads: | ||
|
||
public static class InfiniteLoopWithLookForStuckThreadTest { | ||
|
||
@Rule | ||
public TestRule globalTimeout = new CustomTimeout(100, TimeUnit.MILLISECONDS, true); | ||
|
||
@Test | ||
public void failure() throws Exception { | ||
(new InfiniteLoopMultithreaded()).failure(false); | ||
} | ||
|
||
} | ||
|
||
@Test | ||
public void infiniteLoopWithLookForStuckThread() throws Exception { | ||
JUnitCore core = new JUnitCore(); | ||
Result result = core.run(InfiniteLoopWithLookForStuckThreadTest.class); | ||
assertEquals(1, result.getRunCount()); | ||
assertEquals(3, result.getFailureCount()); | ||
Throwable exception[] = new Throwable[3]; | ||
for (int i = 0; i < 3; i++) | ||
exception[i] = result.getFailures().get(i).getException(); | ||
assertThat(exception[0].getMessage(), containsString("test timed out after 100 milliseconds")); | ||
assertThat(stackForException(exception[0]), containsString("Thread.join")); | ||
assertThat(exception[1].getMessage(), containsString("Appears to be stuck in thread timeout-thr2")); | ||
assertThat(exception[2].getMessage(), containsString("[CustomTimeoutHandler] Appears to be stuck due to running into timeout. Here could be some more custom failure context...")); | ||
} | ||
|
||
private String stackForException(Throwable exception) { | ||
Writer buffer = new StringWriter(); | ||
PrintWriter writer = new PrintWriter(buffer); | ||
exception.printStackTrace(writer); | ||
return buffer.toString(); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified to MultipleFailureException.assertEmpty, for what it's worth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I miss the point: but can you have a second look at that?
My code change above leads to the same behaviour as before my change: which was to collect and return the timeout exception(s): either the single exception or the multiplelfailureexception in case of more than one.
And therefore I don't know where you would like to use
MultipleFailureException.assertEmpty