-
Notifications
You must be signed in to change notification settings - Fork 3.3k
forked from #450 and improved #544
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
Changes from 2 commits
544e5ba
5da2d76
11039f2
337b6dc
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 |
---|---|---|
|
@@ -4,45 +4,95 @@ | |
import org.junit.runner.Description; | ||
import org.junit.runners.model.Statement; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* The Timeout Rule applies the same timeout to all test methods in a class: | ||
* | ||
* <pre> | ||
* public static class HasGlobalTimeout { | ||
* public static String log; | ||
* public static class HasGlobalLongTimeout { | ||
* | ||
* @Rule | ||
* public Timeout globalTimeout= new Timeout(20); | ||
* | ||
* @Test | ||
* public void testInfiniteLoop1() { | ||
* log+= "ran1"; | ||
* for (;;) { | ||
* } | ||
* } | ||
* public void run1() throws InterruptedException { | ||
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 think the new javadoc is more confusing than the old javadoc. We can't enumerate all the ways a test could time out, so why choose these few? 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. We have also the infinite loop, just see my last update which added run4. There are only two things which may happen in user's code:
Because this is a different scenario than loops, and nobody mentioned before. It's important that the thread is interrupted (Thread.interrupt()), therefore should be also tested in this way and we are testing it as well. 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 can of course limit the ways when interrupted code. Yes, maybe it's too much. |
||
* Thread.sleep(100); | ||
* } | ||
* | ||
* @Test | ||
* public void run2() throws InterruptedException { | ||
* reentrantLock.lockInterruptibly(); | ||
* try { | ||
* ... | ||
* } finally { | ||
* reentrantLock.unlock(); | ||
* } | ||
* } | ||
* | ||
* @Test | ||
* public synchronized void run3() throws InterruptedException { | ||
* ... | ||
* wait(); | ||
* } | ||
* | ||
* @Test | ||
* public void testInfiniteLoop2() { | ||
* log+= "ran2"; | ||
* for (;;) { | ||
* } | ||
* } | ||
* public void run4() { | ||
* //uninterrupted code | ||
* for (;;) | ||
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 think the canonical Java infinite loop is while (true) {} I think if we name the method infiniteLoop(), and use that style, we can drop the comment-within-a-javadoc below. |
||
* ; //extremely, an infinite loop | ||
* } | ||
* } | ||
* </pre> | ||
* | ||
* A test is running in an extra thread, and its execution is interrupted via {@link Thread#interrupt} | ||
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. Let's tighten the text. "Each test is run in a new thread. If the specified timeout elapses before the test completes, its execution is interrupted via Thread#interrupt. This happens in [etc]" |
||
* when the timeout elapsed. This happens in interruptable I/O and locks, and methods in {@link Object} | ||
* and {@link Thread} throwing {@link InterruptedException}. | ||
* | ||
* @since 4.7 | ||
*/ | ||
public class Timeout implements TestRule { | ||
private final int fMillis; | ||
private final long fTimeout; | ||
private final TimeUnit fTimeUnit; | ||
|
||
/** | ||
* @param millis the millisecond timeout | ||
* Create a {@code Timeout} instance with the timeout specified | ||
* in milliseconds. | ||
* This constructor is deprecated and will be removed in the next release. | ||
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. Let's take off "and will be removed in the next release". |
||
* Instead use {@link #Timeout(long)} or | ||
* {@link #Timeout(long, java.util.concurrent.TimeUnit)}. | ||
* | ||
* @param millis the maximum time in milliseconds to allow the | ||
* test to run before it should timeout | ||
*/ | ||
@Deprecated | ||
public Timeout(int millis) { | ||
fMillis = millis; | ||
this((long) millis); | ||
} | ||
|
||
/** | ||
* @param millis the millisecond timeout | ||
* @since 4.11 | ||
*/ | ||
public Timeout(long millis) { | ||
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 think we decided the last time we introduced TimeUnit to a class that the benefit to having three constructors wasn't worth the cognitive load of having to choose which constructor. Let's please not add this constructor. 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. @dsaff 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. @marcphilipp I would be very happy to remove the constructor Timeout(int), but i am worry about what would happen if i do. This will happen: Exception in thread "main" java.lang.NoSuchMethodError: org.junit.rules.Timeout.(I)V If it's the rule to update the documentation, then we should do it. If not, then i will remove this constructor. 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 think the suggestion was to remove the constructor taking a 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. See #406, which says that casting long to int is an evil due to truncation in numbers. new Timeout( (int) DateUtils.MILLIS_PER_MINUTE * 5 ) 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. Another option that I like is to have
That way there's easy, non-confusable shortcuts for the most common options. 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. @dsaff |
||
this(millis, TimeUnit.MILLISECONDS); | ||
} | ||
|
||
/** | ||
* Create a {@code Timeout} instance with the timeout specified | ||
* at the unit of granularity of the provided {@code TimeUnit}. | ||
* | ||
* @param timeout the maximum time to allow the test to run | ||
* before it should timeout | ||
* @param unit the time unit for the {@code timeout} | ||
* @since 4.11 | ||
*/ | ||
public Timeout(long timeout, TimeUnit unit) { | ||
fTimeout = timeout; | ||
fTimeUnit = unit; | ||
} | ||
|
||
public Statement apply(Statement base, Description description) { | ||
return new FailOnTimeout(base, fMillis); | ||
return new FailOnTimeout(base, fTimeout, fTimeUnit); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,121 @@ | ||
package org.junit.tests.experimental.rules; | ||
|
||
import static org.hamcrest.CoreMatchers.containsString; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertThat; | ||
|
||
import org.junit.Ignore; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TestRule; | ||
import org.junit.rules.Timeout; | ||
import org.junit.runner.JUnitCore; | ||
import org.junit.runner.Result; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.locks.ReentrantLock; | ||
|
||
import static org.hamcrest.CoreMatchers.containsString; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertThat; | ||
|
||
public class TimeoutRuleTest { | ||
public static class HasGlobalTimeout { | ||
public static String log; | ||
private static final ReentrantLock run1Lock = new ReentrantLock(); | ||
|
||
private static volatile boolean run4done = false; | ||
|
||
public static class HasGlobalLongTimeout { | ||
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. We could reduce the duplication between this class and the next one:
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. @dsaff |
||
public static final StringBuffer logger = new StringBuffer(); | ||
|
||
@Rule | ||
public TestRule globalTimeout = new Timeout(20); | ||
public final TestRule globalTimeout = new Timeout(50L); | ||
|
||
@Test | ||
public void run1() throws InterruptedException { | ||
logger.append("run1"); | ||
TimeoutRuleTest.run1Lock.lockInterruptibly(); | ||
TimeoutRuleTest.run1Lock.unlock(); | ||
} | ||
|
||
@Test | ||
public void run2() throws InterruptedException { | ||
logger.append("run2"); | ||
Thread.currentThread().join(); | ||
} | ||
|
||
@Test | ||
public synchronized void run3() throws InterruptedException { | ||
logger.append("run3"); | ||
wait(); | ||
} | ||
|
||
@Test | ||
public void testInfiniteLoop1() { | ||
log += "ran1"; | ||
for (; ; ) { | ||
public void run4() { | ||
logger.append("run4"); | ||
while (!run4done) { | ||
} | ||
} | ||
} | ||
|
||
public static class HasGlobalTimeUnitTimeout { | ||
public static final StringBuffer logger = new StringBuffer(); | ||
|
||
@Rule | ||
public final TestRule globalTimeout = new Timeout(50, TimeUnit.MILLISECONDS); | ||
|
||
@Test | ||
public void run1() throws InterruptedException { | ||
logger.append("run1"); | ||
TimeoutRuleTest.run1Lock.lockInterruptibly(); | ||
TimeoutRuleTest.run1Lock.unlock(); | ||
} | ||
|
||
@Test | ||
public void run2() throws InterruptedException { | ||
logger.append("run2"); | ||
Thread.currentThread().join(); | ||
} | ||
|
||
@Test | ||
public void testInfiniteLoop2() { | ||
log += "ran2"; | ||
for (; ; ) { | ||
public synchronized void run3() throws InterruptedException { | ||
logger.append("run3"); | ||
wait(); | ||
} | ||
|
||
@Test | ||
public void run4() { | ||
logger.append("run4"); | ||
while (!run4done) { | ||
} | ||
} | ||
} | ||
|
||
@Ignore("For gump, for now") | ||
@Test(timeout = 100) | ||
public void globalTimeoutAvoidsInfiniteLoop() { | ||
HasGlobalTimeout.log = ""; | ||
Result result = JUnitCore.runClasses(HasGlobalTimeout.class); | ||
assertEquals(2, result.getFailureCount()); | ||
assertThat(HasGlobalTimeout.log, containsString("ran1")); | ||
assertThat(HasGlobalTimeout.log, containsString("ran2")); | ||
@Before public void before() { | ||
run4done = false; | ||
run1Lock.lock(); | ||
} | ||
|
||
@After public void after() { | ||
run4done = true;//to make sure that the thread won't continue at run4() | ||
run1Lock.unlock(); | ||
} | ||
|
||
@Test | ||
public void timeUnitTimeout() throws InterruptedException { | ||
HasGlobalTimeUnitTimeout.logger.setLength(0); | ||
Result result = JUnitCore.runClasses(HasGlobalTimeUnitTimeout.class); | ||
assertEquals(4, result.getFailureCount()); | ||
assertThat(HasGlobalTimeUnitTimeout.logger.toString(), containsString("run1")); | ||
assertThat(HasGlobalTimeUnitTimeout.logger.toString(), containsString("run2")); | ||
assertThat(HasGlobalTimeUnitTimeout.logger.toString(), containsString("run3")); | ||
assertThat(HasGlobalTimeUnitTimeout.logger.toString(), containsString("run4")); | ||
} | ||
|
||
@Test | ||
public void longTimeout() throws InterruptedException { | ||
HasGlobalLongTimeout.logger.setLength(0); | ||
Result result = JUnitCore.runClasses(HasGlobalLongTimeout.class); | ||
assertEquals(4, result.getFailureCount()); | ||
assertThat(HasGlobalLongTimeout.logger.toString(), containsString("run1")); | ||
assertThat(HasGlobalLongTimeout.logger.toString(), containsString("run2")); | ||
assertThat(HasGlobalLongTimeout.logger.toString(), containsString("run3")); | ||
assertThat(HasGlobalLongTimeout.logger.toString(), containsString("run4")); | ||
} | ||
} |
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.
Why the name change?
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.
@dsaff
because it is named class according the pull i forked from