package com.klarna.hiverunner;
import com.klarna.hiverunner.config.HiveRunnerConfig;
import org.apache.commons.lang.time.StopWatch;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ThrowOnTimeout extends Statement {
private static final Logger LOGGER = LoggerFactory.getLogger(ThrowOnTimeout.class);
private final Statement originalStatement;
private final HiveRunnerConfig config;
private final Object target;
private Throwable statementException;
private boolean finished = false;
public ThrowOnTimeout(Statement originalStatement, HiveRunnerConfig config, Object target) {
this.originalStatement = originalStatement;
this.config = config;
this.target = target;
}
@Override
public void evaluate() throws Throwable {
final StopWatch stopWatch = new StopWatch();
if (config.isTimeoutEnabled()) {
LOGGER.info("Starting timeout monitoring ({}s) of test case {}.", config.getTimeoutSeconds(), target);
}
Thread statementThread = new Thread(new Runnable() {
@Override
public void run() {
try {
stopWatch.start();
originalStatement.evaluate();
finished = true;
} catch (InterruptedException e) {
// Ignore the InterruptedException
LOGGER.debug(e.getMessage(), e);
} catch (Throwable e) {
synchronized (target) {
statementException = e;
}
}
}
});
statementThread.start();
statementThread.join(config.getTimeoutSeconds() * 1000);
synchronized (target) {
if (statementException != null) {
throw statementException;
} else if (!finished) {
if (config.isTimeoutEnabled()) {
statementThread.interrupt();
throw new TimeoutException(
String.format("test timed out after %d seconds", config.getTimeoutSeconds()));
} else {
LOGGER.warn("Test ran for {} seconds. Timeout disabled. See class {} for configuration options.",
stopWatch.getTime() / 1000, HiveRunnerConfig.class.getName());
}
}
}
statementThread.join();
if (statementException != null) {
throw statementException;
}
}
public static TestRule create(final HiveRunnerConfig config, final Object target) {
return new TestRule() {
@Override
public Statement apply(Statement base, Description description) {
return new ThrowOnTimeout(base, config, target);
}
};
}
}