net.serenitybdd.screenplay.EventualConsequence Maven / Gradle / Ivy
package net.serenitybdd.screenplay;
import net.serenitybdd.model.environment.ConfiguredEnvironment;
import net.serenitybdd.model.time.Stopwatch;
import net.serenitybdd.markers.CanBeSilent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.util.Arrays.asList;
import static net.serenitybdd.screenplay.Actor.ErrorHandlingMode.IGNORE_EXCEPTIONS;
public class EventualConsequence implements Consequence, CanBeSilent {
public static final int A_SHORT_PERIOD_BETWEEN_TRIES = 100;
private final Consequence consequenceThatMightTakeSomeTime;
private final long timeoutInMilliseconds;
private final boolean isSilent;
private Class extends Error> complaintType;
private String complaintDetails;
private AssertionError caughtAssertionError = null;
private RuntimeException caughtRuntimeException = null;
private List> exceptionsToIgnore = new ArrayList<>();
private final List setupActions = new ArrayList<>();
public EventualConsequence(Consequence consequenceThatMightTakeSomeTime, long timeoutInMilliseconds) {
this(consequenceThatMightTakeSomeTime, timeoutInMilliseconds, false);
}
public EventualConsequence(Consequence consequenceThatMightTakeSomeTime, long timeoutInMilliseconds, boolean isSilent) {
this.consequenceThatMightTakeSomeTime = consequenceThatMightTakeSomeTime;
this.timeoutInMilliseconds = timeoutInMilliseconds;
this.isSilent = isSilent;
}
public EventualConsequence(Consequence consequenceThatMightTakeSomeTime) {
this(consequenceThatMightTakeSomeTime,
ConfiguredEnvironment.getConfiguration().getElementTimeoutInSeconds() * 1000L);
}
public static EventualConsequence eventually(Consequence consequenceThatMightTakeSomeTime) {
return new EventualConsequence(consequenceThatMightTakeSomeTime);
}
public EventualConsequenceBuilder waitingForNoLongerThan(long amount) {
return new EventualConsequenceBuilder(consequenceThatMightTakeSomeTime, amount);
}
@Override
public void evaluateFor(Actor actor) {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
do {
try {
performSetupActionsAs(actor);
consequenceThatMightTakeSomeTime.evaluateFor(actor);
return;
} catch (AssertionError assertionError) {
if (!shouldIgnoreException(assertionError)) {
caughtAssertionError = assertionError;
}
} catch (RuntimeException runtimeException) {
if (!shouldIgnoreException(runtimeException)) {
caughtRuntimeException = runtimeException;
}
} catch (Throwable exception) {
if (!shouldIgnoreException(exception)) {
throw exception;
}
}
pauseBeforeNextAttempt();
} while (stopwatch.lapTime() < timeoutInMilliseconds);
throwAnyCaughtErrors();
}
private boolean shouldIgnoreException(Throwable exception) {
return exceptionsToIgnore.contains(exception.getClass());
}
private void pauseBeforeNextAttempt() {
try {
Thread.sleep(A_SHORT_PERIOD_BETWEEN_TRIES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void throwAnyCaughtErrors() {
if (caughtAssertionError != null) {
throwComplaintTypeErrorIfSpecified(caughtAssertionError);
throw caughtAssertionError;
}
if (caughtRuntimeException != null) {
throwComplaintTypeErrorIfSpecified(caughtRuntimeException);
throw caughtRuntimeException;
}
}
@Override
public String toString() {
return consequenceThatMightTakeSomeTime.toString();
}
@Override
public Consequence orComplainWith(Class extends Error> complaintType) {
this.complaintType = complaintType;
return this;
}
public Consequence ignoringExceptions(Class extends Throwable>... exceptionsToIgnore) {
this.exceptionsToIgnore = Arrays.asList(exceptionsToIgnore);
return this;
}
@Override
public Consequence orComplainWith(Class extends Error> complaintType, String complaintDetails) {
this.complaintType = complaintType;
this.complaintDetails = complaintDetails;
return this;
}
@Override
public Consequence whenAttemptingTo(Performable performable) {
return new EventualConsequence(consequenceThatMightTakeSomeTime.whenAttemptingTo(performable),
timeoutInMilliseconds, isSilent);
}
@Override
public Consequence because(String explanation) {
return new EventualConsequence(consequenceThatMightTakeSomeTime.because(explanation),
timeoutInMilliseconds, isSilent);
}
@Override
public boolean isSilent() {
return isSilent;
}
public EventualConsequence withNoReporting() {
return new EventualConsequence(consequenceThatMightTakeSomeTime, timeoutInMilliseconds, true);
}
private void throwComplaintTypeErrorIfSpecified(Throwable actualError) {
if (complaintType != null) {
throw Complaint.from(complaintType, complaintDetails, actualError);
}
}
public Consequence after(Performable... actions) {
this.setupActions.addAll(asList(actions));
return this;
}
protected void performSetupActionsAs(Actor actor) {
actor.attemptsTo(
IGNORE_EXCEPTIONS,
setupActions.toArray(new Performable[]{})
);
}
}