Skip to content

Commit

Permalink
feat: add ability to read container logs into Vec
Browse files Browse the repository at this point in the history
We already have these methods for `exec`, they are good shortcuts, and I added them to `Container` as well to make the interfaces consistent.
  • Loading branch information
DDtKey committed May 26, 2024
1 parent 032cc12 commit 56fe2df
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 12 deletions.
31 changes: 25 additions & 6 deletions testcontainers/src/core/containers/async_container.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::{fmt, net::IpAddr, pin::Pin, str::FromStr, sync::Arc, time::Duration};

use tokio::{io::AsyncBufRead, runtime::RuntimeFlavor};
use tokio::{
io::{AsyncBufRead, AsyncReadExt},
runtime::RuntimeFlavor,
};

use crate::{
core::{
Expand Down Expand Up @@ -281,6 +284,24 @@ where
Box::pin(tokio_util::io::StreamReader::new(stderr.into_inner()))
}

/// Returns stdout as a vector of bytes available at the moment of call (from container startup to present).
///
/// If you want to read stdout in asynchronous manner, use [`ContainerAsync::stdout`] instead.
pub async fn stdout_to_vec(&self) -> Result<Vec<u8>> {
let mut stdout = Vec::new();
self.stdout(false).read_to_end(&mut stdout).await?;
Ok(stdout)
}

/// Returns stderr as a vector of bytes available at the moment of call (from container startup to present).
///
/// If you want to read stderr in asynchronous manner, use [`ContainerAsync::stderr`] instead.
pub async fn stderr_to_vec(&self) -> Result<Vec<u8>> {
let mut stderr = Vec::new();
self.stderr(false).read_to_end(&mut stderr).await?;
Ok(stderr)
}

pub(crate) async fn block_until_ready(&self, ready_conditions: Vec<WaitFor>) -> Result<()> {
log::debug!("Waiting for container {} to be ready", self.id);
let id = self.id();
Expand Down Expand Up @@ -381,7 +402,7 @@ where
#[cfg(test)]
mod tests {

use tokio::io::{AsyncBufReadExt, AsyncReadExt};
use tokio::io::AsyncBufReadExt;

use super::*;
use crate::{images::generic::GenericImage, runners::AsyncRunner};
Expand Down Expand Up @@ -424,12 +445,10 @@ mod tests {
container.stop().await?;

// stdout is empty
let mut stdout = String::new();
container.stdout(true).read_to_string(&mut stdout).await?;
let stdout = String::from_utf8(container.stdout_to_vec().await?)?;
assert_eq!(stdout, "");
// stderr contains 6 lines
let mut stderr = String::new();
container.stderr(true).read_to_string(&mut stderr).await?;
let stderr = String::from_utf8(container.stderr_to_vec().await?)?;
assert_eq!(
stderr.lines().count(),
6,
Expand Down
8 changes: 6 additions & 2 deletions testcontainers/src/core/containers/async_container/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,19 @@ impl ExecResult {
}

/// Returns stdout as a vector of bytes.
/// If you want to read stdout in asynchronous manner, use `stdout_reader` instead.
/// Keep in mind that this will block until the command exits.
///
/// If you want to read stdout in asynchronous manner, use [`ExecResult::stdout`] instead.
pub async fn stdout_to_vec(&mut self) -> Result<Vec<u8>> {
let mut stdout = Vec::new();
self.stdout().read_to_end(&mut stdout).await?;
Ok(stdout)
}

/// Returns stderr as a vector of bytes.
/// If you want to read stderr in asynchronous manner, use `stderr_reader` instead.
/// Keep in mind that this will block until the command exits.
///
/// If you want to read stderr in asynchronous manner, use [`ExecResult::stderr`] instead.
pub async fn stderr_to_vec(&mut self) -> Result<Vec<u8>> {
let mut stderr = Vec::new();
self.stderr().read_to_end(&mut stderr).await?;
Expand Down
24 changes: 20 additions & 4 deletions testcontainers/src/core/containers/sync_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,24 @@ where
))
}

/// Returns stdout as a vector of bytes available at the moment of call (from container startup to present).
///
/// If you want to read stdout in chunks, use [`Container::stdout`] instead.
pub fn stdout_to_vec(&self) -> Result<Vec<u8>> {
let mut stdout = Vec::new();
self.stdout(false).read_to_end(&mut stdout)?;
Ok(stdout)
}

/// Returns stderr as a vector of bytes available at the moment of call (from container startup to present).
///
/// If you want to read stderr in chunks, use [`Container::stderr`] instead.
pub fn stderr_to_vec(&self) -> Result<Vec<u8>> {
let mut stderr = Vec::new();
self.stderr(false).read_to_end(&mut stderr)?;
Ok(stderr)
}

/// Returns reference to inner `Runtime`. It's safe to unwrap because it's `Some` until `Container` is dropped.
fn rt(&self) -> &Arc<tokio::runtime::Runtime> {
&self.inner.as_ref().unwrap().runtime
Expand Down Expand Up @@ -267,12 +285,10 @@ mod test {
container.stop()?;

// stdout is empty
let mut stdout = String::new();
container.stdout(true).read_to_string(&mut stdout)?;
let stdout = String::from_utf8(container.stdout_to_vec()?)?;
assert_eq!(stdout, "");
// stderr contains 6 lines
let mut stderr = String::new();
container.stderr(true).read_to_string(&mut stderr)?;
let stderr = String::from_utf8(container.stderr_to_vec()?)?;
assert_eq!(
stderr.lines().count(),
6,
Expand Down

0 comments on commit 56fe2df

Please sign in to comment.