| package stdcopy // import "github.com/docker/docker/pkg/stdcopy" | |
| import ( | |
| "bytes" | |
| "errors" | |
| "io" | |
| "strings" | |
| "testing" | |
| ) | |
| func TestNewStdWriter(t *testing.T) { | |
| writer := NewStdWriter(io.Discard, Stdout) | |
| if writer == nil { | |
| t.Fatalf("NewStdWriter with an invalid StdType should not return nil.") | |
| } | |
| } | |
| func TestWriteWithUninitializedStdWriter(t *testing.T) { | |
| writer := stdWriter{ | |
| Writer: nil, | |
| prefix: byte(Stdout), | |
| } | |
| n, err := writer.Write([]byte("Something here")) | |
| if n != 0 || err == nil { | |
| t.Fatalf("Should fail when given an incomplete or uninitialized StdWriter") | |
| } | |
| } | |
| func TestWriteWithNilBytes(t *testing.T) { | |
| writer := NewStdWriter(io.Discard, Stdout) | |
| n, err := writer.Write(nil) | |
| if err != nil { | |
| t.Fatalf("Shouldn't have fail when given no data") | |
| } | |
| if n > 0 { | |
| t.Fatalf("Write should have written 0 byte, but has written %d", n) | |
| } | |
| } | |
| func TestWrite(t *testing.T) { | |
| writer := NewStdWriter(io.Discard, Stdout) | |
| data := []byte("Test StdWrite.Write") | |
| n, err := writer.Write(data) | |
| if err != nil { | |
| t.Fatalf("Error while writing with StdWrite") | |
| } | |
| if n != len(data) { | |
| t.Fatalf("Write should have written %d byte but wrote %d.", len(data), n) | |
| } | |
| } | |
| type errWriter struct { | |
| n int | |
| err error | |
| } | |
| func (f *errWriter) Write(buf []byte) (int, error) { | |
| return f.n, f.err | |
| } | |
| func TestWriteWithWriterError(t *testing.T) { | |
| expectedError := errors.New("expected") | |
| expectedReturnedBytes := 10 | |
| writer := NewStdWriter(&errWriter{ | |
| n: stdWriterPrefixLen + expectedReturnedBytes, | |
| err: expectedError, | |
| }, Stdout) | |
| data := []byte("This won't get written, sigh") | |
| n, err := writer.Write(data) | |
| if err != expectedError { | |
| t.Fatalf("Didn't get expected error.") | |
| } | |
| if n != expectedReturnedBytes { | |
| t.Fatalf("Didn't get expected written bytes %d, got %d.", | |
| expectedReturnedBytes, n) | |
| } | |
| } | |
| func TestWriteDoesNotReturnNegativeWrittenBytes(t *testing.T) { | |
| writer := NewStdWriter(&errWriter{n: -1}, Stdout) | |
| data := []byte("This won't get written, sigh") | |
| actual, _ := writer.Write(data) | |
| if actual != 0 { | |
| t.Fatalf("Expected returned written bytes equal to 0, got %d", actual) | |
| } | |
| } | |
| func getSrcBuffer(stdOutBytes, stdErrBytes []byte) (buffer *bytes.Buffer, err error) { | |
| buffer = new(bytes.Buffer) | |
| dstOut := NewStdWriter(buffer, Stdout) | |
| _, err = dstOut.Write(stdOutBytes) | |
| if err != nil { | |
| return | |
| } | |
| dstErr := NewStdWriter(buffer, Stderr) | |
| _, err = dstErr.Write(stdErrBytes) | |
| return | |
| } | |
| func TestStdCopyWriteAndRead(t *testing.T) { | |
| stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) | |
| stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) | |
| buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| written, err := StdCopy(io.Discard, io.Discard, buffer) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| expectedTotalWritten := len(stdOutBytes) + len(stdErrBytes) | |
| if written != int64(expectedTotalWritten) { | |
| t.Fatalf("Expected to have total of %d bytes written, got %d", expectedTotalWritten, written) | |
| } | |
| } | |
| type customReader struct { | |
| n int | |
| err error | |
| totalCalls int | |
| correctCalls int | |
| src *bytes.Buffer | |
| } | |
| func (f *customReader) Read(buf []byte) (int, error) { | |
| f.totalCalls++ | |
| if f.totalCalls <= f.correctCalls { | |
| return f.src.Read(buf) | |
| } | |
| return f.n, f.err | |
| } | |
| func TestStdCopyReturnsErrorReadingHeader(t *testing.T) { | |
| expectedError := errors.New("error") | |
| reader := &customReader{ | |
| err: expectedError, | |
| } | |
| written, err := StdCopy(io.Discard, io.Discard, reader) | |
| if written != 0 { | |
| t.Fatalf("Expected 0 bytes read, got %d", written) | |
| } | |
| if err != expectedError { | |
| t.Fatalf("Didn't get expected error") | |
| } | |
| } | |
| func TestStdCopyReturnsErrorReadingFrame(t *testing.T) { | |
| expectedError := errors.New("error") | |
| stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) | |
| stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) | |
| buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| reader := &customReader{ | |
| correctCalls: 1, | |
| n: stdWriterPrefixLen + 1, | |
| err: expectedError, | |
| src: buffer, | |
| } | |
| written, err := StdCopy(io.Discard, io.Discard, reader) | |
| if written != 0 { | |
| t.Fatalf("Expected 0 bytes read, got %d", written) | |
| } | |
| if err != expectedError { | |
| t.Fatalf("Didn't get expected error") | |
| } | |
| } | |
| func TestStdCopyDetectsCorruptedFrame(t *testing.T) { | |
| stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) | |
| stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) | |
| buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| reader := &customReader{ | |
| correctCalls: 1, | |
| n: stdWriterPrefixLen + 1, | |
| err: io.EOF, | |
| src: buffer, | |
| } | |
| written, err := StdCopy(io.Discard, io.Discard, reader) | |
| if written != startingBufLen { | |
| t.Fatalf("Expected %d bytes read, got %d", startingBufLen, written) | |
| } | |
| if err != nil { | |
| t.Fatal("Didn't get nil error") | |
| } | |
| } | |
| func TestStdCopyWithInvalidInputHeader(t *testing.T) { | |
| dstOut := NewStdWriter(io.Discard, Stdout) | |
| dstErr := NewStdWriter(io.Discard, Stderr) | |
| src := strings.NewReader("Invalid input") | |
| _, err := StdCopy(dstOut, dstErr, src) | |
| if err == nil { | |
| t.Fatal("StdCopy with invalid input header should fail.") | |
| } | |
| } | |
| func TestStdCopyWithCorruptedPrefix(t *testing.T) { | |
| data := []byte{0x01, 0x02, 0x03} | |
| src := bytes.NewReader(data) | |
| written, err := StdCopy(nil, nil, src) | |
| if err != nil { | |
| t.Fatalf("StdCopy should not return an error with corrupted prefix.") | |
| } | |
| if written != 0 { | |
| t.Fatalf("StdCopy should have written 0, but has written %d", written) | |
| } | |
| } | |
| func TestStdCopyReturnsWriteErrors(t *testing.T) { | |
| stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) | |
| stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) | |
| buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| expectedError := errors.New("expected") | |
| dstOut := &errWriter{err: expectedError} | |
| written, err := StdCopy(dstOut, io.Discard, buffer) | |
| if written != 0 { | |
| t.Fatalf("StdCopy should have written 0, but has written %d", written) | |
| } | |
| if err != expectedError { | |
| t.Fatalf("Didn't get expected error, got %v", err) | |
| } | |
| } | |
| func TestStdCopyDetectsNotFullyWrittenFrames(t *testing.T) { | |
| stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) | |
| stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) | |
| buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| dstOut := &errWriter{n: startingBufLen - 10} | |
| written, err := StdCopy(dstOut, io.Discard, buffer) | |
| if written != 0 { | |
| t.Fatalf("StdCopy should have return 0 written bytes, but returned %d", written) | |
| } | |
| if err != io.ErrShortWrite { | |
| t.Fatalf("Didn't get expected io.ErrShortWrite error") | |
| } | |
| } | |
| // TestStdCopyReturnsErrorFromSystem tests that StdCopy correctly returns an | |
| // error, when that error is muxed into the Systemerr stream. | |
| func TestStdCopyReturnsErrorFromSystem(t *testing.T) { | |
| // write in the basic messages, just so there's some fluff in there | |
| stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) | |
| stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) | |
| buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // add in an error message on the Systemerr stream | |
| systemErrBytes := []byte(strings.Repeat("S", startingBufLen)) | |
| systemWriter := NewStdWriter(buffer, Systemerr) | |
| _, err = systemWriter.Write(systemErrBytes) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // now copy and demux. we should expect an error containing the string we | |
| // wrote out | |
| _, err = StdCopy(io.Discard, io.Discard, buffer) | |
| if err == nil { | |
| t.Fatal("expected error, got none") | |
| } | |
| if !strings.Contains(err.Error(), string(systemErrBytes)) { | |
| t.Fatal("expected error to contain message") | |
| } | |
| } | |
| func BenchmarkWrite(b *testing.B) { | |
| w := NewStdWriter(io.Discard, Stdout) | |
| data := []byte("Test line for testing stdwriter performance\n") | |
| data = bytes.Repeat(data, 100) | |
| b.SetBytes(int64(len(data))) | |
| b.ResetTimer() | |
| for i := 0; i < b.N; i++ { | |
| if _, err := w.Write(data); err != nil { | |
| b.Fatal(err) | |
| } | |
| } | |
| } |