Skip to content

Commit

Permalink
fix(exec): updates the Multiplexed opt to combine stdout and stderr (
Browse files Browse the repository at this point in the history
…#2452)

* fix(exec): updates the `Multiplexed` opt to combine stdout and stderr

Updates the `Multiplexed` option to combine the output of stdout and stderr,
changing the previous behavior where stderr was preferred over stdout.
Now, both streams are properly combined into a single stream.

Additionally, this commit enhance and adjust existing test cases for
the `Multiplexed` option:

* Modifies `TestExecWithMultiplexedResponse` to ensure that stdout and
  stderr are on the same stream, testing the new behavior of the
  `Multiplexed` option.
* Refactors `TestExecWithNonMultiplexedResponse` to use `stdcopy.StdCopy`
  for ensuring that `io.Reader` contains a separated stdout and stderr.

* chore: no need to pass the provider type

---------

Co-authored-by: Manuel de la Peña <mdelapenya@gmail.com>
  • Loading branch information
gustavosbarreto and mdelapenya committed Apr 3, 2024
1 parent db61369 commit 88622f0
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 47 deletions.
59 changes: 17 additions & 42 deletions docker_exec_test.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
package testcontainers

import (
"bytes"
"context"
"io"
"strings"
"testing"

"github.com/docker/docker/pkg/stdcopy"
"github.com/stretchr/testify/require"

tcexec "github.com/testcontainers/testcontainers-go/exec"
)

func TestExecWithMultiplexedResponse(t *testing.T) {
ctx := context.Background()
req := ContainerRequest{
Image: nginxAlpineImage,
}

container, err := GenericContainer(ctx, GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: req,
Started: true,
})

require.NoError(t, err)
terminateContainerOnEnd(t, ctx, container)

code, reader, err := container.Exec(ctx, []string{"ls", "/usr/share/nginx"}, tcexec.Multiplexed())
require.NoError(t, err)
require.Zero(t, code)
require.NotNil(t, reader)

b, err := io.ReadAll(reader)
require.NoError(t, err)
require.NotNil(t, b)

str := string(b)
require.Equal(t, "html\n", str)
}

func TestExecWithOptions(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -79,7 +51,6 @@ func TestExecWithOptions(t *testing.T) {
}

container, err := GenericContainer(ctx, GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: req,
Started: true,
})
Expand All @@ -106,32 +77,32 @@ func TestExecWithOptions(t *testing.T) {
}
}

func TestExecWithMultiplexedStderrResponse(t *testing.T) {
func TestExecWithMultiplexedResponse(t *testing.T) {
ctx := context.Background()
req := ContainerRequest{
Image: nginxAlpineImage,
}

container, err := GenericContainer(ctx, GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: req,
Started: true,
})

require.NoError(t, err)
terminateContainerOnEnd(t, ctx, container)

code, reader, err := container.Exec(ctx, []string{"ls", "/non-existing-directory"}, tcexec.Multiplexed())
code, reader, err := container.Exec(ctx, []string{"sh", "-c", "echo stdout; echo stderr >&2"}, tcexec.Multiplexed())
require.NoError(t, err)
require.NotZero(t, code)
require.Zero(t, code)
require.NotNil(t, reader)

b, err := io.ReadAll(reader)
require.NoError(t, err)
require.NotNil(t, b)

str := string(b)
require.Contains(t, str, "No such file or directory")
require.Contains(t, str, "stdout")
require.Contains(t, str, "stderr")
}

func TestExecWithNonMultiplexedResponse(t *testing.T) {
Expand All @@ -141,23 +112,27 @@ func TestExecWithNonMultiplexedResponse(t *testing.T) {
}

container, err := GenericContainer(ctx, GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: req,
Started: true,
})

require.NoError(t, err)
terminateContainerOnEnd(t, ctx, container)

code, reader, err := container.Exec(ctx, []string{"ls", "/usr/share/nginx"})
code, reader, err := container.Exec(ctx, []string{"sh", "-c", "echo stdout; echo stderr >&2"})
require.NoError(t, err)
require.Zero(t, code)
require.NotNil(t, reader)

b, err := io.ReadAll(reader)
var stdout bytes.Buffer
var stderr bytes.Buffer

written, err := stdcopy.StdCopy(&stdout, &stderr, reader)
require.NoError(t, err)
require.NotNil(t, b)
require.NotZero(t, written)
require.NotNil(t, stdout)
require.NotNil(t, stderr)

str := string(b)
require.True(t, strings.HasSuffix(str, "html\n"))
require.Equal(t, stdout.String(), "stdout\n")
require.Equal(t, stderr.String(), "stderr\n")
}
6 changes: 1 addition & 5 deletions exec/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ func Multiplexed() ProcessOption {

<-done

if errBuff.Bytes() != nil {
opts.Reader = &errBuff
} else {
opts.Reader = &outBuff
}
opts.Reader = io.MultiReader(&outBuff, &errBuff)
})
}

0 comments on commit 88622f0

Please sign in to comment.