geb.waiting.Wait.groovy Maven / Gradle / Ivy
Show all versions of geb-waiting Show documentation
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package geb.waiting
/**
* Represents a particular configuration of waiting, but does not encompass what is to be waited on.
*
* Generally not used by user code, but used internally by {@link geb.Configuration} and {@link geb.waiting.WaitingSupport}.
*/
class Wait {
/**
* 5 seconds
*/
static public final Double DEFAULT_TIMEOUT = 5
/**
* 100 milliseconds
*/
static public final Double DEFAULT_RETRY_INTERVAL = 0.1
private static final int HASHCODE_MULTIPLIER = 31
/**
* The maximum amount of seconds that something can be waited on.
*/
final Double timeout
/**
* How many seconds to wait before trying something again while waiting.
*/
final Double retryInterval
/**
* Whether we should append cause strings to the returned exception message or not
*/
final boolean includeCauseInExceptionMessage
String customMessage
Wait(Double timeout = DEFAULT_TIMEOUT, Double retryInterval = DEFAULT_RETRY_INTERVAL, boolean includeCauseInExceptionMessage = false) {
this.timeout = timeout
this.retryInterval = [timeout, retryInterval].min()
this.includeCauseInExceptionMessage = includeCauseInExceptionMessage
}
String toString() {
"Wait[timeout: $timeout, retryInterval: $retryInterval]"
}
boolean equals(other) {
if (this.is(other)) {
true
} else if (!(other instanceof Wait)) {
false
} else {
this.timeout == other.timeout && this.retryInterval == other.retryInterval && this.includeCauseInExceptionMessage == other.includeCauseInExceptionMessage
}
}
int hashCode() {
int code = 41
code = HASHCODE_MULTIPLIER * code + timeout.hashCode()
code = HASHCODE_MULTIPLIER * code + retryInterval.hashCode()
code = HASHCODE_MULTIPLIER * code + includeCauseInExceptionMessage.hashCode()
code
}
Date calculateTimeoutFromNow() {
calculateTimeoutFrom(new Date())
}
Date calculateTimeoutFrom(Date start) {
def calendar = Calendar.instance
calendar.time = start
calendar.add(Calendar.MILLISECOND, Math.ceil(toMiliseconds(timeout)) as int)
calendar.time
}
private double toMiliseconds(Double seconds) {
seconds * 1000
}
/**
* Invokes the given {@code block} every {@code retryInterval} seconds until it returns
* a true value according to the Groovy Truth. If {@code block} does not return a truish value
* within {@code timeout} seconds then a {@link geb.waiting.WaitTimeoutException} will be thrown.
*
* If the given block is executing at the time when the timeout is reached, it will not be interrupted. This means that
* this method may take longer than the specified {@code timeout}. For example, if the {@code block} takes 5 seconds
* to complete but the timeout is 2 seconds, the wait is always going to take at least 5 seconds.
*
* If {@code block} throws any {@link Throwable}, it is treated as a failure and the {@code block} will be tried
* again after the {@code retryInterval} has expired. If the last invocation of {@code block} throws an exception
* it will be the cause of the {@link geb.waiting.WaitTimeoutException} that will be thrown.
*/
@SuppressWarnings("UnnecessaryPublicModifier")
public T waitFor(Closure block) {
def stopAt = calculateTimeoutFromNow()
def pass
def thrown = null
try {
pass = block()
} catch (Throwable e) {
pass = new UnknownWaitForEvaluationResult(e)
thrown = e
}
def timedOut = new Date() > stopAt
while (!pass && !timedOut) {
sleepForRetryInterval()
try {
pass = block()
thrown = null
} catch (Throwable e) {
pass = new UnknownWaitForEvaluationResult(e)
thrown = e
} finally {
timedOut = new Date() > stopAt
}
}
if (!pass && timedOut) {
throw new WaitTimeoutException(this, thrown, pass)
}
pass as T
}
/**
* Blocks the caller for the retryInterval
*/
void sleepForRetryInterval() {
Thread.sleep(toMiliseconds(retryInterval) as long)
}
}