-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create TimeoutRule as replacement for junit Timeout issue#3829
org.junit.rules.Timeout is incompatible with Robolectric's scheduler because it spawns a new thread for the test. This commit introduces TimeoutRule which behaves like `@Test(timeout = )` but for all tests in the file
- Loading branch information
Showing
3 changed files
with
121 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
robolectric/src/main/java/org/robolectric/junit/rules/TimeoutRule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package org.robolectric.junit.rules; | ||
|
||
import androidx.annotation.NonNull; | ||
import java.util.concurrent.TimeUnit; | ||
import org.junit.rules.TestRule; | ||
import org.junit.runner.Description; | ||
import org.junit.runners.model.Statement; | ||
import org.robolectric.internal.TimeLimitedStatement; | ||
|
||
/** | ||
* Robolectric's replacement for JUnit's {@link org.junit.rules.Timeout Timeout}. | ||
* | ||
* <p>{@link org.junit.rules.Timeout Timeout} spawns a new thread, which is not compatible with | ||
* Robolectric's Scheduler. Instead this Rule uses {@link | ||
* org.robolectric.internal.TimeLimitedStatement TimeLimitedStatement} like {@code @Test(timeout=)} | ||
* does. | ||
* | ||
* <p>Example usage: | ||
* | ||
* <pre> | ||
* {@literal @}Rule public final TimeoutRule timeoutRule = TimeoutRule.seconds(40); | ||
* | ||
* {@literal @}Test | ||
* public void testWhichShouldFinishIn40Seconds() { | ||
* // ... | ||
* } | ||
* </pre> | ||
*/ | ||
public class TimeoutRule implements TestRule { | ||
|
||
private final long timeout; | ||
@NonNull private final TimeUnit timeUnit; | ||
|
||
/** | ||
* Create a {@code TimeoutRule} instance with the timeout specified at the timeUnit of granularity | ||
* of the provided {@code TimeUnit}. | ||
* | ||
* @param timeout the maximum time to allow the test to run before it should timeout | ||
* @param timeUnit the time unit for the {@code timeout} | ||
*/ | ||
public TimeoutRule(long timeout, @NonNull TimeUnit timeUnit) { | ||
this.timeout = timeout; | ||
this.timeUnit = timeUnit; | ||
} | ||
|
||
@Override | ||
public Statement apply(Statement base, Description description) { | ||
return new TimeLimitedStatement(timeUnit.toMillis(timeout), base); | ||
} | ||
|
||
/** | ||
* Creates a {@link org.robolectric.junit.rules.TimeoutRule TimeoutRule} that will timeout a test | ||
* after the given duration, in milliseconds. | ||
*/ | ||
public static TimeoutRule millis(long millis) { | ||
return new TimeoutRule(millis, TimeUnit.MILLISECONDS); | ||
} | ||
|
||
/** | ||
* Creates a {@link org.robolectric.junit.rules.TimeoutRule TimeoutRule} that will timeout a test | ||
* after the given duration, in seconds. | ||
*/ | ||
public static TimeoutRule seconds(long seconds) { | ||
return new TimeoutRule(seconds, TimeUnit.SECONDS); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
robolectric/src/test/java/org/robolectric/junit/rules/TimeoutRuleTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package org.robolectric.junit.rules; | ||
|
||
import androidx.test.ext.junit.runners.AndroidJUnit4; | ||
import org.junit.Assert; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.RuleChain; | ||
import org.junit.runner.Description; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.model.Statement; | ||
import org.junit.runners.model.TestTimedOutException; | ||
|
||
/** Tests for {@link TimeoutRule}. */ | ||
@RunWith(AndroidJUnit4.class) | ||
public final class TimeoutRuleTest { | ||
|
||
private final TimeoutRule rule = TimeoutRule.millis(200); | ||
|
||
@Rule public RuleChain chain = RuleChain.outerRule(rule); | ||
|
||
@Test | ||
public void testNotTimingOutFinishes() throws InterruptedException { | ||
Thread.sleep(50); | ||
} | ||
|
||
@Test | ||
public void testTimingOutIsInterrupted() { | ||
Assert.assertThrows( | ||
InterruptedException.class, | ||
() -> { | ||
Thread.sleep(1000); | ||
throw new IllegalArgumentException(); | ||
}); | ||
} | ||
|
||
@Test | ||
public void verifyErrorMessage() { | ||
final TimeoutRule timeoutRule = TimeoutRule.millis(50); | ||
final Statement threadSleepStatement = | ||
new Statement() { | ||
@Override | ||
public void evaluate() throws Throwable { | ||
Thread.sleep(1000); | ||
} | ||
}; | ||
final Statement wrappedStatement = timeoutRule.apply(threadSleepStatement, Description.EMPTY); | ||
|
||
final TestTimedOutException exception = | ||
Assert.assertThrows(TestTimedOutException.class, wrappedStatement::evaluate); | ||
Assert.assertEquals("test timed out after 50 milliseconds", exception.getMessage()); | ||
} | ||
} |