package org.testcontainers.junit.wait;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.rnorth.ducttape.RetryCountExceededException;
import org.rnorth.visibleassertions.VisibleAssertions;
import org.testcontainers.containers.ContainerLaunchException;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.WaitStrategy;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.*;
/**
* Common test methods for {@link WaitStrategy} implementations.
*
* @author Pete Cornish {@literal <outofcoffee@gmail.com>}
*/
public abstract class AbstractWaitStrategyTest<W extends WaitStrategy> {
private static final long WAIT_TIMEOUT_MILLIS = 3000;
private static final String IMAGE_NAME = "alpine:latest";
/**
* Indicates that the WaitStrategy has completed waiting successfully.
*/
private AtomicBoolean ready;
/**
* Subclasses should return an instance of {@link W} that sets {@code ready} to {@code true},
* if the wait was successful.
*
* @param ready the AtomicBoolean on which to indicate success
* @return WaitStrategy implementation
*/
@NotNull
protected abstract W buildWaitStrategy(final AtomicBoolean ready);
@Before
public void setUp() throws Exception {
ready = new AtomicBoolean(false);
}
/**
* Starts a GenericContainer with the {@link #IMAGE_NAME} image, passing the given {@code shellCommand} as
* a parameter to {@literal sh -c} (the container CMD).
*
* @param shellCommand the shell command to execute
* @return the (unstarted) container
*/
private GenericContainer startContainerWithCommand(String shellCommand) {
final WaitStrategy waitStrategy = buildWaitStrategy(ready)
.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS));
// apply WaitStrategy to container
return new GenericContainer(IMAGE_NAME)
.withExposedPorts(8080)
.withCommand("sh", "-c", shellCommand)
.waitingFor(waitStrategy);
}
/**
* Expects that the WaitStrategy returns successfully after connection to a container with a listening port.
*
* @param shellCommand the shell command to execute
*/
protected void waitUntilReadyAndSucceed(String shellCommand) {
final GenericContainer container = startContainerWithCommand(shellCommand);
// start() blocks until successful or timeout
container.start();
assertTrue(String.format("Expected container to be ready after timeout of %sms",
WAIT_TIMEOUT_MILLIS), ready.get());
}
/**
* Expects that the WaitStrategy throws a {@link RetryCountExceededException} after unsuccessful connection
* to a container with a listening port.
*
* @param shellCommand the shell command to execute
*/
protected void waitUntilReadyAndTimeout(String shellCommand) {
final GenericContainer container = startContainerWithCommand(shellCommand);
// start() blocks until successful or timeout
VisibleAssertions.assertThrows("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)",
ContainerLaunchException.class,
container::start);
}
}