| package stream | |
| import ( | |
| "context" | |
| "io" | |
| "testing" | |
| "time" | |
| "gotest.tools/v3/assert" | |
| ) | |
| // Make sure when there is no I/O on a stream that the goroutines do not get blocked after the container exits. | |
| func TestAttachNoIO(t *testing.T) { | |
| t.Run("stdin only", func(t *testing.T) { | |
| stdinR, _ := io.Pipe() | |
| defer stdinR.Close() | |
| testStreamCopy(t, stdinR, nil, nil) | |
| }) | |
| t.Run("stdout only", func(t *testing.T) { | |
| _, w := io.Pipe() | |
| defer w.Close() | |
| testStreamCopy(t, nil, w, nil) | |
| }) | |
| t.Run("stderr only", func(t *testing.T) { | |
| _, w := io.Pipe() | |
| defer w.Close() | |
| testStreamCopy(t, nil, nil, w) | |
| }) | |
| t.Run("stdout+stderr", func(t *testing.T) { | |
| _, stdoutW := io.Pipe() | |
| defer stdoutW.Close() | |
| _, stderrW := io.Pipe() | |
| defer stderrW.Close() | |
| testStreamCopy(t, nil, stdoutW, stderrW) | |
| }) | |
| t.Run("stdin+stdout", func(t *testing.T) { | |
| stdin, _ := io.Pipe() | |
| defer stdin.Close() | |
| _, stdout := io.Pipe() | |
| defer stdout.Close() | |
| testStreamCopy(t, stdin, stdout, nil) | |
| }) | |
| t.Run("stdin+stderr", func(t *testing.T) { | |
| stdin, _ := io.Pipe() | |
| defer stdin.Close() | |
| _, stderr := io.Pipe() | |
| defer stderr.Close() | |
| testStreamCopy(t, stdin, nil, stderr) | |
| }) | |
| t.Run("stdin+stdout+stderr", func(t *testing.T) { | |
| stdinR, _ := io.Pipe() | |
| defer stdinR.Close() | |
| stdoutR, stdoutW := io.Pipe() | |
| defer stdoutR.Close() | |
| stderrR, stderrW := io.Pipe() | |
| defer stderrR.Close() | |
| testStreamCopy(t, stdinR, stdoutW, stderrW) | |
| }) | |
| } | |
| func testStreamCopy(t *testing.T, stdin io.ReadCloser, stdout, stderr io.WriteCloser) { | |
| cfg := AttachConfig{ | |
| UseStdin: stdin != nil, | |
| UseStdout: stdout != nil, | |
| UseStderr: stderr != nil, | |
| Stdin: stdin, | |
| Stdout: stdout, | |
| Stderr: stderr, | |
| } | |
| sc := NewConfig() | |
| sc.AttachStreams(&cfg) | |
| defer sc.CloseStreams() | |
| ctx, cancel := context.WithCancel(context.Background()) | |
| defer cancel() | |
| chErr := sc.CopyStreams(ctx, &cfg) | |
| select { | |
| case err := <-chErr: | |
| assert.NilError(t, err) | |
| default: | |
| } | |
| cancel() | |
| select { | |
| case err := <-chErr: | |
| assert.ErrorIs(t, err, context.Canceled) | |
| case <-time.After(10 * time.Second): | |
| t.Fatal("timeout waiting for CopyStreams to exit") | |
| } | |
| } |