diff --git a/src/Container/RedisContainer.php b/src/Container/RedisContainer.php index cba2a4c..d568dd8 100644 --- a/src/Container/RedisContainer.php +++ b/src/Container/RedisContainer.php @@ -12,7 +12,7 @@ class RedisContainer extends GenericContainer { parent::__construct('redis:' . $version); $this->withExposedPorts(6379); - //$this->withWait(new WaitForLog('Ready to accept connections')); + $this->withWait(new WaitForLog('Ready to accept connections')); } /** diff --git a/src/Exception/ContainerWaitingTimeoutException.php b/src/Exception/ContainerWaitingTimeoutException.php new file mode 100644 index 0000000..e6d1c18 --- /dev/null +++ b/src/Exception/ContainerWaitingTimeoutException.php @@ -0,0 +1,22 @@ +containerId = $containerId; + $message ??= sprintf('Timeout reached while waiting for container %s', $containerId); + parent::__construct($message, 0, $previous); + } + + public function getContainerId(): string + { + return $this->containerId; + } +} diff --git a/src/Wait/WaitForLog.php b/src/Wait/WaitForLog.php index 8d70b15..04ef8cc 100644 --- a/src/Wait/WaitForLog.php +++ b/src/Wait/WaitForLog.php @@ -4,33 +4,53 @@ declare(strict_types=1); namespace Testcontainers\Wait; +use Docker\API\Runtime\Client\Client; use Docker\Docker; -use Symfony\Component\Process\Process; -use Testcontainers\Exception\ContainerNotReadyException; +use Testcontainers\Exception\ContainerWaitingTimeoutException; +/** + * Uses $timout and $pollInterval in milliseconds to set the parameters for waiting. + */ class WaitForLog implements WaitInterface { protected Docker $dockerClient; - public function __construct(private string $message, private bool $enableRegex = false) - { + public function __construct( + protected string $message, + protected bool $enableRegex = false, + protected int $timeout = 10000, + protected int $pollInterval = 500 + ) { $this->dockerClient = Docker::create(); } public function wait(string $id): void { - $logs = $this->dockerClient->containerLogs($id); + $startTime = microtime(true) * 1000; - $output = $logs->getBody()->getContents(); + while (true) { + $elapsedTime = (microtime(true) * 1000) - $startTime; - if ($this->enableRegex) { - if (!preg_match($this->message, $output)) { - throw new ContainerNotReadyException($id, new \RuntimeException('Message not found in logs')); + if ($elapsedTime > $this->timeout) { + throw new ContainerWaitingTimeoutException($id); } - } else { - if (!str_contains($output, $this->message)) { - throw new ContainerNotReadyException($id, new \RuntimeException('Message not found in logs')); + + $output = $this->dockerClient + ->containerLogs($id, ['stdout' => true, 'stderr' => true], Client::FETCH_RESPONSE) + ?->getBody() + ->getContents() ?? ''; + + $output = preg_replace('/[\x00-\x1F\x7F]/u', '', mb_convert_encoding($output, 'UTF-8', 'UTF-8')) ?? ''; + + if ($this->enableRegex) { + if (preg_match($this->message, $output)) { + return; + } + } elseif (str_contains($output, $this->message)) { + return; } + + usleep($this->pollInterval * 1000); } } }