package org.testcontainers.containers;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.startupcheck.StartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;

import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class GenericContainerTest {

    @Test
    public void shouldReportOOMAfterWait() {
        try (
            GenericContainer container = new GenericContainer<>()
                .withStartupCheckStrategy(new NoopStartupCheckStrategy())
                .waitingFor(new WaitForState(ContainerState::getOOMKilled))
                .withCreateContainerCmdModifier(it -> {
                    it.getHostConfig().withMemory(4 * FileUtils.ONE_MB);
                })
                .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done")
        ) {
            assertThatThrownBy(container::start)
                .hasStackTraceContaining("Container crashed with out-of-memory");
        }
    }

    @Test
    public void shouldReportErrorAfterWait() {
        try (
            GenericContainer container = new GenericContainer<>()
                .withStartupCheckStrategy(new NoopStartupCheckStrategy())
                .waitingFor(new WaitForState(state -> state.getExitCode() > 0))
                .withCommand("sh", "-c", "usleep 100; exit 123")
        ) {
            assertThatThrownBy(container::start)
                .hasStackTraceContaining("Container exited with code 123");
        }
    }

    static class NoopStartupCheckStrategy extends StartupCheckStrategy {

        @Override
        public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) {
            return StartupStatus.SUCCESSFUL;
        }
    }

    @RequiredArgsConstructor
    @FieldDefaults(makeFinal = true)
    static class WaitForState extends AbstractWaitStrategy {

        Predicate<ContainerState> predicate;

        @Override
        @SneakyThrows
        protected void waitUntilReady() {
            Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, () -> {
                ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState();
                return predicate.test(state);
            });

            throw new IllegalStateException("Nope!");
        }
    }
}

@@ -271,16 +271,50 @@
 faults;%0A
+import lombok.extern.slf4j.Slf4j;%0A
 import o
@@ -345,16 +345,16 @@
 eUtils;%0A
-
 import o
@@ -990,24 +990,30 @@
 (new WaitFor
+Exited
 State(Contai
@@ -1689,24 +1689,30 @@
 (new WaitFor
+Exited
 State(state 
@@ -2275,16 +2275,27 @@
 = true)%0A
+    @Slf4j%0A
     stat
@@ -2310,16 +2310,22 @@
  WaitFor
+Exited
 State ex
@@ -2546,24 +2546,24 @@
 DS, () -%3E %7B%0A
-
             
@@ -2645,16 +2645,232 @@
 State();
+%0A%0A                log.debug(%22Current state: %7B%7D%22, state);%0A                if (!%22exited%22.equalsIgnoreCase(state.getStatus())) %7B%0A                    Thread.sleep(100);%0A                    return false;%0A                %7D
 %0A       

debug `GenericContainerTest`