blob: f1512141be23c629ac91e2c6243b356480f5b36b [file] [log] [blame]
package brooklyn.test
import static org.testng.Assert.*
import groovy.time.TimeDuration
import java.util.concurrent.Callable
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import org.codehaus.groovy.runtime.InvokerInvocationException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import brooklyn.entity.Entity
import brooklyn.event.AttributeSensor
import brooklyn.util.time.Duration;
import com.google.common.base.Predicate
import com.google.common.base.Supplier
import com.google.common.collect.Iterables
/**
* Helper functions for tests of Tomcat, JBoss and others.
*
* Note that methods will migrate from here to {@link Asserts} in future releases.
*/
public class TestUtils {
private static final Logger log = LoggerFactory.getLogger(TestUtils.class)
private TestUtils() { }
/**
* True if two attempts to connect to the port succeed.
*
* @deprecated since 0.5; use {@link brooklyn.util.NetworkUtils#isPortAvailable(int)}
*/
@Deprecated
public static boolean isPortInUse(int port, long retryAfterMillis=0) {
try {
def s = new Socket("localhost", port)
s.close()
if (retryAfterMillis>0) {
log.debug "port {} still open, waiting 1s for it to really close", port
//give it 1s to close
Thread.sleep retryAfterMillis
s = new Socket("localhost", port)
s.close()
}
log.debug "port {} still open (conclusive)", port
return true
} catch (ConnectException e) {
return false
}
}
/**
* Connects to the given HTTP URL and asserts that the response had status code 200.
* @deprecated Use HttpTestUtils.getHttpStatusCode(url) == 200
*/
@Deprecated
public static boolean urlRespondsWithStatusCode200(String url) {
int status = HttpTestUtils.getHttpStatusCode(url);
log.debug "connection to {} gives {}", url, status
if (status == 404)
throw new Exception("Connection to $url gave 404");
return status == 200
}
/**
* Connects to the given HTTP URL and asserts that the response had status code 200.
* @deprecated Use HttpTestUtils.getHttpStatusCode(url)
*/
@Deprecated
public static int urlRespondsStatusCode(String url) {
return HttpTestUtils.getHttpStatusCode(url);
}
/**
* Connects to the given url and returns the connection.
* @deprecated Use HttpTestUtils.connectToUrl(url)
*/
@Deprecated
public static URLConnection connectToURL(String url) {
return HttpTestUtils.connectToUrl(url);
}
// calling groovy from java doesn't cope with generics here; stripping them from here :-(
// <T> void assertEventually(Map flags=[:], Supplier<? extends T> supplier, Predicate<T> predicate)
/**
* @deprecated since 0.5; use {@link Asserts#eventually(Map, Supplier, Predicate)}
*/
@Deprecated
public static void assertEventually(Map flags=[:], Supplier supplier, Predicate predicate) {
Asserts.eventually(flags, supplier, predicate);
}
/**
* @deprecated since 0.5; use {@link Asserts#eventually(Map, Supplier, Predicate, String)}
*/
@Deprecated
public static <T> void assertEventually(Map flags=[:], Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg) {
Asserts.eventually(flags, supplier, predicate, errMsg);
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(java.util.Map, Callable)}
*/
@Deprecated
public static void assertEventually(Map flags=[:], Callable c) {
executeUntilSucceeds(flags, c);
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Runnable)}
*/
@Deprecated
public static void assertEventually(Map flags=[:], Runnable c) {
executeUntilSucceeds(flags, c);
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}
*/
@Deprecated
public static void executeUntilSucceeds(Map flags=[:], Closure c) {
Asserts.succeedsEventually(flags, c);
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}
*/
@Deprecated
public static void executeUntilSucceeds(Map flags=[:], Callable c) {
Asserts.succeedsEventually(flags, c);
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Runnable)}
*/
@Deprecated
public static void executeUntilSucceeds(Map flags=[:], Runnable r) {
if (r in Callable)
executeUntilSucceedsWithFinallyBlock(flags, {return ((Callable)r).call();}, { })
else if (r in Closure) // Closure check probably not necessary, just was trying to fix a server build which had a screwy problem
executeUntilSucceedsWithFinallyBlock(flags, {return ((Closure)r).call();}, { })
else
executeUntilSucceedsWithFinallyBlock(flags, {r.run(); return true}, { })
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}, and tear-down with {@link AfterMethod}.
*/
@Deprecated
public static void executeUntilSucceedsElseShutdown(Map flags=[:], Entity entity, Closure c) {
try {
executeUntilSucceedsWithFinallyBlock(flags, c) { }
} catch (Throwable t) {
entity.stop()
throw t
}
}
/**
* convenience for entities to ensure they shutdown afterwards.
*
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}, and tear-down with {@link AfterMethod}.
*/
@Deprecated
public static void executeUntilSucceedsWithShutdown(Map flags=[:], Entity entity, Closure c) {
executeUntilSucceedsWithFinallyBlock(flags, c) { entity.stop() }
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}, and tear-down with {@link AfterMethod}.
*/
@Deprecated
public static void executeUntilSucceedsWithFinallyBlock(Map flags=[:], Closure c, Closure finallyBlock={}) {
executeUntilSucceedsWithFinallyBlockInternal(flags, c, finallyBlock)
}
/**
* Convenience method for cases where we need to test until something is true.
*
* The runnable will be invoked periodically until it succesfully concludes.
* Additionally, a finally block can be supplied.
* <p>
* The following flags are supported:
* <ul>
* <li>abortOnError (boolean, default true)
* <li>abortOnException - (boolean, default false)
* <li>useGroovyTruth - (defaults to false; any result code apart from 'false' will be treated as success including null; ignored for Runnables which aren't Callables)
* <li>timeout - (a Duration or an integer in millis, defaults to 30*SECONDS)
* <li>period - (a Duration or an integer in millis, for fixed retry time; if not set, defaults to exponentially increasing from 1 to 500ms)
* <li>minPeriod - (a Duration or an integer in millis; only used if period not explicitly set; the minimum period when exponentially increasing; defaults to 1ms)
* <li>maxPeriod - (a Duration or an integer in millis; only used if period not explicitly set; the maximum period when exponentially increasing; defaults to 500ms)
* <li>maxAttempts - (integer, Integer.MAX_VALUE)
* </ul>
*
* @param flags, accepts the flags listed above
* @param r
* @param finallyBlock
*
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}, and tear-down with {@link AfterMethod}.
*/
@Deprecated
public static void executeUntilSucceedsWithFinallyBlock(Map flags=[:], Callable<?> c, Closure finallyBlock={}) {
executeUntilSucceedsWithFinallyBlockInternal(flags, c, finallyBlock);
}
/**
* the "real" implementation, renamed to allow multiple entry points (depending whether closure cast to callable)
*
* @deprecated since 0.5; use {@link Asserts#succeedsEventually(Map, Callable)}, and tear-down with {@link AfterMethod}.
*/
@Deprecated
private static void executeUntilSucceedsWithFinallyBlockInternal(Map flags=[:], Callable<?> c, Closure finallyBlock={}) {
// log.trace "abortOnError = {}", flags.abortOnError
boolean abortOnException = flags.abortOnException ?: false
boolean abortOnError = flags.abortOnError ?: false
boolean useGroovyTruth = flags.useGroovyTruth ?: false
boolean logException = flags.logException ?: true
// To speed up tests, default is for the period to start small and increase...
Duration duration = Duration.of(flags.timeout) ?: Duration.THIRTY_SECONDS;
Duration fixedPeriod = Duration.of(flags.period) ?: null
Duration minPeriod = fixedPeriod ?: Duration.of(flags.minPeriod) ?: Duration.millis(1)
Duration maxPeriod = fixedPeriod ?: Duration.of(flags.maxPeriod) ?: Duration.millis(500)
int maxAttempts = flags.maxAttempts ?: Integer.MAX_VALUE;
int attempt = 0;
long startTime = System.currentTimeMillis();
try {
Throwable lastException = null;
Object result;
long lastAttemptTime = 0;
long expireTime = startTime+duration.toMilliseconds();
long sleepTimeBetweenAttempts = minPeriod.toMilliseconds();
while (attempt<maxAttempts && lastAttemptTime<expireTime) {
try {
attempt++
lastAttemptTime = System.currentTimeMillis()
result = c.call()
log.trace "Attempt {} after {} ms: {}", attempt, System.currentTimeMillis() - startTime, result
if (useGroovyTruth) {
if (result) return;
} else if (result != false) {
if (result instanceof BooleanWithMessage)
log.warn "Test returned an instance of BooleanWithMessage but useGroovyTruth is not set! " +
"The result of this probably isn't what you intended."
return;
}
lastException = null
} catch(Throwable e) {
lastException = e
log.trace "Attempt {} after {} ms: {}", attempt, System.currentTimeMillis() - startTime, e.message
if (abortOnException) throw e
if (abortOnError && e in Error) throw e
}
long sleepTime = Math.min(sleepTimeBetweenAttempts, expireTime-System.currentTimeMillis())
if (sleepTime > 0) Thread.sleep(sleepTime)
sleepTimeBetweenAttempts = Math.min(sleepTimeBetweenAttempts*2, maxPeriod.toMilliseconds())
}
log.debug "TestUtils.executeUntilSucceedsWithFinallyBlockInternal exceeded max attempts or timeout - {} attempts lasting {} ms", attempt, System.currentTimeMillis()-startTime
if (lastException != null)
throw lastException
fail "invalid result: $result"
} catch (Throwable t) {
if (logException) log.info("failed execute-until-succeeds, "+attempt+" attempts, "+
(System.currentTimeMillis()-startTime)+"ms elapsed "+
"(rethrowing): "+t);
throw t
} finally {
finallyBlock.call()
}
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsContinually(Map, Runnable)}
*/
@Deprecated
public static <T> void assertSucceedsContinually(Map flags=[:], Runnable job) {
assertSucceedsContinually(flags, Executors.callable(job));
}
/**
* @deprecated since 0.5; use {@link Asserts#succeedsContinually(Map, Callable)}
*/
@Deprecated
public static void assertSucceedsContinually(Map flags=[:], Callable<?> job) {
Duration duration = Duration.of(flags.timeout) ?: Duration.ONE_SECOND
Duration period = Duration.of(flags.period) ?: Duration.millis(10)
long periodMs = period.toMilliseconds()
long startTime = System.currentTimeMillis()
long expireTime = startTime+duration.toMilliseconds()
boolean first = true;
while (first || System.currentTimeMillis() <= expireTime) {
job.call();
if (periodMs > 0) sleep(periodMs);
first = false;
}
}
/**
* @deprecated since 0.5; use {@link Asserts#continually(Map, Supplier, Predicate)}
*/
@Deprecated
// FIXME When calling from java, the generics declared in groovy messing things up!
public static void assertContinuallyFromJava(Map flags=[:], Supplier<?> supplier, Predicate<?> predicate) {
Asserts.continually(flags, supplier, predicate);
}
/**
* @deprecated since 0.5; use {@link Asserts#continually(Map, Supplier, Predicate)}
*/
@Deprecated
public static <T> void assertContinually(Map flags=[:], Supplier<? extends T> supplier, Predicate<T> predicate) {
Asserts.continually(flags, supplier, predicate, (String)null);
}
/**
* @deprecated since 0.5; use {@link Asserts#continually(Map, Supplier, Predicate, String)}
*/
@Deprecated
public static <T> void assertContinually(Map flags=[:], Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg, long durationMs) {
flags.put("duration", Duration.millis(durationMs));
Asserts.continually(flags, supplier, predicate, errMsg);
}
/**
* @deprecated since 0.5; use {@link Asserts#continually(Map, Supplier, Predicate, String)}
*/
@Deprecated
public static <T> void assertContinually(Map flags=[:], Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg) {
Asserts.continually(flags, supplier, predicate, errMsg);
}
public static class BooleanWithMessage {
boolean value; String message;
public BooleanWithMessage(boolean value, String message) {
this.value = value; this.message = message;
}
public boolean asBoolean() {
return value
}
public String toString() {
return message
}
}
/**
* @deprecated since 0.5; use {@link brooklyn.util.ResourceUtils}
*/
@Deprecated
public static File getResource(String path, ClassLoader loader) {
URL resource = loader.getResource(path)
if (resource==null)
throw new IllegalArgumentException("cannot find required entity '"+path+"'");
return new File(resource.path)
}
/**
* @deprecated since 0.5; use long and {@link TimeUnit}
*/
@Deprecated
public static TimeDuration toTimeDuration(Object duration) {
return toTimeDuration(duration, null);
}
/**
* @deprecated since 0.5; use long and {@link TimeUnit}
*/
@Deprecated
public static TimeDuration toTimeDuration(Object duration, TimeDuration defaultVal) {
if (duration == null) {
return defaultVal;
} else if (duration instanceof TimeDuration) {
return (TimeDuration) duration
} else if (duration instanceof Number) {
return new TimeDuration(0,0,0,(int)duration)
// TODO would be nice to have this, but we need to sort out utils / test-utils dependency
// } else if (duration instanceof String) {
// return Time.parseTimeString((String)duration);
} else {
throw new IllegalArgumentException("Cannot convert $duration of type ${duration.class.name} to a TimeDuration")
}
}
public static Throwable unwrapThrowable(Throwable t) {
if (t.getCause() == null) {
return t;
} else if (t instanceof ExecutionException) {
return unwrapThrowable(t.getCause())
} else if (t instanceof InvokerInvocationException) {
return unwrapThrowable(t.getCause())
} else {
return t
}
}
/**
* @deprecated since 0.5; use {@link EntityTestUtils#assertAttributeEqualsEventually(Entity, AttributeSensor, Object)}
*/
@Deprecated
public static <T> void assertAttributeEventually(Entity entity, AttributeSensor<T> attribute, T expected) {
executeUntilSucceeds() {
assertEquals(entity.getAttribute(attribute), expected);
}
}
/**
* @deprecated since 0.5; use {@link EntityTestUtils#assertAttributeEqualsContinually(Entity, AttributeSensor, Object)}
*/
@Deprecated
public static <T> void assertAttributeContinually(Entity entity, AttributeSensor<T> attribute, T expected) {
assertSucceedsContinually() {
assertEquals(entity.getAttribute(attribute), expected);
}
}
/**
* @deprecated since 0.5; use {@link HttpTestUtils#assertHttpStatusCodeEquals(String, int)}
*/
@Deprecated
public static void assertUrlStatusCodeEventually(final String url, final int expected) {
executeUntilSucceeds() {
assertEquals(urlRespondsStatusCode(url), expected);
}
}
/**
* @deprecated since 0.5; use {@link Asserts#assertFails(Runnable)}
*/
@Deprecated
public static void assertFails(Runnable c) {
assertFailsWith(c, (Predicate)null);
}
/**
* @deprecated since 0.5; use {@link Asserts#assertFailsWith(Closure)}
*/
@Deprecated
public static void assertFailsWith(Runnable c, Closure exceptionChecker) {
assertFailsWith(c, exceptionChecker as Predicate);
}
/**
* @deprecated since 0.5; use {@link Asserts#assertFailsWith(Runnable, Class, Class...)}
*/
@Deprecated
public static void assertFailsWith(Runnable c, final Class<? extends Throwable> validException, final Class<? extends Throwable> ...otherValidExceptions) {
assertFailsWith(c, { e ->
if (validException.isInstance(e)) return true;
if (otherValidExceptions.find {it.isInstance(e)}) return true;
List expectedTypes = [validException];
expectedTypes.addAll(Arrays.asList(otherValidExceptions));
fail("Test threw exception of unexpected type "+e.getClass()+"; expecting "+expectedTypes);
});
}
/**
* @deprecated since 0.5; use {@link Asserts#assertFailsWith(Runnable, Predicate)}
*/
@Deprecated
public static void assertFailsWith(Runnable c, Predicate<Throwable> exceptionChecker) {
boolean failed = false;
try {
c.run();
} catch (Throwable e) {
failed = true;
if (exceptionChecker!=null) {
if (!exceptionChecker.apply(e)) {
fail("Test threw invalid exception: "+e);
}
}
log.debug("Test for exception successful ("+e+")");
}
if (!failed) fail("Test code should have thrown exception but did not");
}
public static void assertSetsEqual(Collection c1, Collection c2) {
Set s = new LinkedHashSet();
s.addAll(c1); s.removeAll(c2);
if (!s.isEmpty()) fail("First argument contains additional contents: "+s);
s.clear(); s.addAll(c2); s.removeAll(c1);
if (!s.isEmpty()) fail("Second argument contains additional contents: "+s);
}
/**
* @deprecated since 0.5; use {@code assertFalse(Iterables.isEmpty(c))}
*/
@Deprecated
public static <T> void assertNonEmpty(Iterable<T> c) {
if (c.iterator().hasNext()) return;
fail("Expected non-empty set");
}
/**
* @deprecated since 0.5; use {@code assertEquals(Iterables.size(c), expectedSize)}
*/
@Deprecated
public static <T> void assertSize(Iterable<T> c, int expectedSize) {
int actualSize = Iterables.size(c);
if (actualSize==expectedSize) return;
fail("Expected collection of size "+expectedSize+" but got size "+actualSize+": "+c);
}
public static void assertStringContainsLiteral(String string, String substring) {
if (string==null) fail("String is null");
if (substring==null) fail("Substring is null");
if (string.indexOf(substring)>=0) return;
fail("String '"+string+"' does not contain expected pattern '"+substring+"'");
}
}