Skip to content

Instantly share code, notes, and snippets.

@Romeh
Created December 3, 2017 17:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Romeh/c237f8cc52a65ef265f530b3f36da06d to your computer and use it in GitHub Desktop.
Save Romeh/c237f8cc52a65ef265f530b3f36da06d to your computer and use it in GitHub Desktop.
public final class RetryRule implements TestRule {
@NotNull
private Throwable[] errors = new Throwable[0];
private int currentAttempt = 0;
@Override
public Statement apply(final Statement base, final Description description) {
final Retry retryAnnotation = description.getAnnotation(Retry.class);
if (retryAnnotation == null) {
return base;
}
final int times = retryAnnotation.times();
if (times <= 0) {
throw new IllegalArgumentException(
"@" + Retry.class.getSimpleName() + " cannot be used with a \"times\" parameter less than 1"
);
}
final long timeout = retryAnnotation.timeout();
if (timeout < 0) {
throw new IllegalArgumentException(
"@" + Retry.class.getSimpleName() + " cannot be used with a \"timeout\" parameter less than 0"
);
}
errors = new Throwable[times];
return new Statement() {
@Override
public void evaluate() throws Throwable {
while (currentAttempt < times) {
try {
base.evaluate();
return;
} catch (Throwable t) {
errors[currentAttempt] = t;
currentAttempt++;
Thread.sleep(timeout);
}
}
throw RetryException.from(errors);
}
};
}
/**
* @return an array representing the errors that have been encountered so far. {@code errors()[0]} corresponds to the
* Throwable encountered when running the test-case for the first time, {@code errors()[1]} corresponds to the
* Throwable encountered when running the test-case for the second time, and so on.
*/
@NotNull
public Throwable[] errors() {
return Arrays.copyOfRange(errors, 0, currentAttempt);
}
/**
* A convenience method to return the {@link Throwable} that was encountered on the last invocation of this test-case.
* Returns {@code null} if this is the first invocation of the test-case.
*/
public Throwable lastError() {
final int currentAttempt = currentAttempt();
final Throwable[] errors = errors();
if (currentAttempt == 0) {
return null;
}
return errors[currentAttempt - 1];
}
/**
* @return the current attempt (0-indexed). 0 is the very first attempt, 1 is the next one, and so on.
*/
public int currentAttempt() {
return currentAttempt;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment