From 9baf599c9104aa567c4b570329904724a5b92873 Mon Sep 17 00:00:00 2001 From: karthic rao Date: Sun, 31 Jul 2016 02:06:43 +0530 Subject: [PATCH] tests: Unit tests and fixes for copyBuffer. (#2333) - Unit tests for copyBuffer. - Shadowing fix for copyBuffer. --- erasure-utils.go | 7 ++-- erasure-utils_test.go | 92 +++++++++++++++++++++++++++++++++++++++++++ test-utils_test.go | 30 ++++++++++++++ 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/erasure-utils.go b/erasure-utils.go index 6ca6a8bdd..008173eda 100644 --- a/erasure-utils.go +++ b/erasure-utils.go @@ -163,10 +163,9 @@ func copyBuffer(writer io.Writer, disk StorageAPI, volume string, path string, b for { n, err := disk.ReadFile(volume, path, startOffset, buf) if n > 0 { - var m int - m, err = writer.Write(buf[:n]) - if err != nil { - return err + m, wErr := writer.Write(buf[:n]) + if wErr != nil { + return wErr } if int64(m) != n { return io.ErrShortWrite diff --git a/erasure-utils_test.go b/erasure-utils_test.go index af2153488..fdd08ed82 100644 --- a/erasure-utils_test.go +++ b/erasure-utils_test.go @@ -17,6 +17,9 @@ package main import ( + "bytes" + "errors" + "io" "testing" ) @@ -63,3 +66,92 @@ func TestGetChunkSize(t *testing.T) { } } } + +// TestCopyBuffer - Tests validate the result and errors produced when `copyBuffer` is called with sample inputs. +func TestCopyBuffer(t *testing.T) { + // create posix test setup + disk, diskPath, err := newPosixTestSetup() + if err != nil { + t.Fatalf("Unable to create posix test setup, %s", err) + } + defer removeAll(diskPath) + + volume := "success-vol" + // Setup test environment. + if err = disk.MakeVol(volume); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + // creating io.Writer for the input to copyBuffer. + buffers := []*bytes.Buffer{ + new(bytes.Buffer), + new(bytes.Buffer), + new(bytes.Buffer), + } + + testFile := "testFile" + testContent := []byte("hello, world") + err = disk.AppendFile(volume, testFile, testContent) + + testCases := []struct { + writer io.Writer + disk StorageAPI + volume string + path string + buf []byte + + expectedResult []byte + // flag to indicate whether test case should pass. + shouldPass bool + expectedErr error + }{ + // Test case - 1. + // case with empty buffer. + {nil, nil, "", "", []byte{}, nil, false, errors.New("empty buffer in readBuffer")}, + // Test case - 2. + // Test case with empty volume. + {buffers[0], disk, "", "", make([]byte, 5), nil, false, errInvalidArgument}, + // Test case - 3. + // Test case with non existent volume. + {buffers[0], disk, "abc", "", make([]byte, 5), nil, false, errVolumeNotFound}, + // Test case - 4. + // Test case with empty filename/path. + {buffers[0], disk, volume, "", make([]byte, 5), nil, false, errIsNotRegular}, + // Test case - 5. + // Test case with non existent file name. + {buffers[0], disk, volume, "abcd", make([]byte, 5), nil, false, errFileNotFound}, + // Test case - 6. + // Test case where the writer returns EOF. + {NewEOFWriter(buffers[0], 3), disk, volume, testFile, make([]byte, 5), nil, false, io.EOF}, + // Test case - 7. + // Test case to produce io.ErrShortWrite, the TruncateWriter returns after writing 3 bytes. + {TruncateWriter(buffers[1], 3), disk, volume, testFile, make([]byte, 5), nil, false, io.ErrShortWrite}, + // Teset case - 8. + // Valid case, expected to read till EOF and write the contents into the writer. + {buffers[2], disk, volume, testFile, make([]byte, 5), testContent, true, nil}, + } + // iterate over the test cases and call copy Buffer with data. + for i, testCase := range testCases { + actualErr := copyBuffer(testCase.writer, testCase.disk, testCase.volume, testCase.path, testCase.buf) + + if actualErr != nil && testCase.shouldPass { + t.Errorf("Test %d: Expected to pass but failed instead with \"%s\"", i+1, actualErr) + } + + if actualErr == nil && !testCase.shouldPass { + t.Errorf("Test %d: Expected to fail with error \"%v\", but instead passed", i+1, testCase.expectedErr) + } + // Failed as expected, but does it fail for the expected reason. + if actualErr != nil && !testCase.shouldPass { + if testCase.expectedErr.Error() != actualErr.Error() { + t.Errorf("Test %d: Expected Error to be \"%v\", but instead found \"%v\" ", i+1, testCase.expectedErr, actualErr) + } + } + // test passed as expected, asserting the result. + if actualErr == nil && testCase.shouldPass { + if !bytes.Equal(testCase.expectedResult, buffers[2].Bytes()) { + t.Errorf("Test %d: copied buffer differs from the expected one.", i+1) + } + } + } +} diff --git a/test-utils_test.go b/test-utils_test.go index 83dbc65bf..6a4afb702 100644 --- a/test-utils_test.go +++ b/test-utils_test.go @@ -400,6 +400,36 @@ func getRandomBucketName() string { } +// TruncateWriter - Writes `n` bytes, then returns with number of bytes written. +// differs from iotest.TruncateWriter, the difference is commented in the Write method. +func TruncateWriter(w io.Writer, n int64) io.Writer { + return &truncateWriter{w, n} +} + +type truncateWriter struct { + w io.Writer + n int64 +} + +func (t *truncateWriter) Write(p []byte) (n int, err error) { + if t.n <= 0 { + return len(p), nil + } + // real write + n = len(p) + if int64(n) > t.n { + n = int(t.n) + } + n, err = t.w.Write(p[0:n]) + t.n -= int64(n) + // Removed from iotest.TruncateWriter. + // Need the Write method to return truncated number of bytes written, not the size of the buffer requested to be written. + // if err == nil { + // n = len(p) + // } + return +} + // NewEOFWriter returns a Writer that writes to w, // but returns EOF error after writing n bytes. func NewEOFWriter(w io.Writer, n int64) io.Writer {