Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code coverage and tests on e2e tests #2225

Merged
merged 19 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 49 additions & 44 deletions azbfs/zt_retry_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (
"errors"
"fmt"
"github.com/Azure/azure-storage-azcopy/v10/azbfs"
"github.com/stretchr/testify/assert"
"io"
"net"
"net/http"
"testing"
"time"

chk "gopkg.in/check.v1"
)

// Testings for RetryReader
Expand Down Expand Up @@ -86,7 +86,8 @@ func (r *perByteReader) Close() error {

// Test normal retry succeed, note initial response not provided.
// Tests both with and without notification of failures
func (r *aztestsSuite) TestRetryReaderReadWithRetry(c *chk.C) {
func TestRetryReaderReadWithRetry(t *testing.T) {
a := assert.New(t)
// Test twice, the second time using the optional "logging"/notification callback for failed tries
// We must test both with and without the callback, since be testing without
// we are testing that it is, indeed, optional to provide the callback
Expand Down Expand Up @@ -124,7 +125,7 @@ func (r *aztestsSuite) TestRetryReaderReadWithRetry(c *chk.C) {

httpGetterInfo := azbfs.HTTPGetterInfo{Offset: 0, Count: int64(byteCount)}
initResponse, err := getter(context.Background(), httpGetterInfo)
c.Assert(err, chk.IsNil)
a.Nil(err)

rrOptions := azbfs.RetryReaderOptions{MaxRetryRequests: 1}
if logThisRun {
Expand All @@ -135,28 +136,29 @@ func (r *aztestsSuite) TestRetryReaderReadWithRetry(c *chk.C) {
// should fail and succeed through retry
can := make([]byte, 1)
n, err := retryReader.Read(can)
c.Assert(n, chk.Equals, 1)
c.Assert(err, chk.IsNil)
a.Equal(1, n)
a.Nil(err)

// check "logging", if it was enabled
if logThisRun {
// We only expect one failed try in this test
// And the notification method is not called for successes
c.Assert(failureMethodNumCalls, chk.Equals, 1) // this is the number of calls we counted
c.Assert(failureWillRetryCount, chk.Equals, 1) // the sole failure was retried
c.Assert(failureLastReportedFailureCount, chk.Equals, 1) // this is the number of failures reported by the notification method
c.Assert(failureLastReportedError, chk.NotNil)
a.Equal(1, failureMethodNumCalls) // this is the number of calls we counted
a.Equal(1, failureWillRetryCount) // the sole failure was retried
a.Equal(1, failureLastReportedFailureCount) // this is the number of failures reported by the notification method
a.NotNil(failureLastReportedError)
}
// should return EOF
n, err = retryReader.Read(can)
c.Assert(n, chk.Equals, 0)
c.Assert(err, chk.Equals, io.EOF)
a.Zero(n)
a.Equal(io.EOF, err)
}
}

// Test normal retry succeed, note initial response not provided.
// Tests both with and without notification of failures
func (r *aztestsSuite) TestRetryReaderReadWithRetryIoUnexpectedEOF(c *chk.C) {
func TestRetryReaderReadWithRetryIoUnexpectedEOF(t *testing.T) {
a := assert.New(t)
// Test twice, the second time using the optional "logging"/notification callback for failed tries
// We must test both with and without the callback, since be testing without
// we are testing that it is, indeed, optional to provide the callback
Expand Down Expand Up @@ -194,7 +196,7 @@ func (r *aztestsSuite) TestRetryReaderReadWithRetryIoUnexpectedEOF(c *chk.C) {

httpGetterInfo := azbfs.HTTPGetterInfo{Offset: 0, Count: int64(byteCount)}
initResponse, err := getter(context.Background(), httpGetterInfo)
c.Assert(err, chk.IsNil)
a.Nil(err)

rrOptions := azbfs.RetryReaderOptions{MaxRetryRequests: 1}
if logThisRun {
Expand All @@ -205,27 +207,28 @@ func (r *aztestsSuite) TestRetryReaderReadWithRetryIoUnexpectedEOF(c *chk.C) {
// should fail and succeed through retry
can := make([]byte, 1)
n, err := retryReader.Read(can)
c.Assert(n, chk.Equals, 1)
c.Assert(err, chk.IsNil)
a.Equal(1, n)
a.Nil(err)

// check "logging", if it was enabled
if logThisRun {
// We only expect one failed try in this test
// And the notification method is not called for successes
c.Assert(failureMethodNumCalls, chk.Equals, 1) // this is the number of calls we counted
c.Assert(failureWillRetryCount, chk.Equals, 1) // the sole failure was retried
c.Assert(failureLastReportedFailureCount, chk.Equals, 1) // this is the number of failures reported by the notification method
c.Assert(failureLastReportedError, chk.NotNil)
a.Equal(1, failureMethodNumCalls) // this is the number of calls we counted
a.Equal(1, failureWillRetryCount) // the sole failure was retried
a.Equal(1, failureLastReportedFailureCount) // this is the number of failures reported by the notification method
a.NotNil(failureLastReportedError)
}
// should return EOF
n, err = retryReader.Read(can)
c.Assert(n, chk.Equals, 0)
c.Assert(err, chk.Equals, io.EOF)
a.Zero(n)
a.Equal(io.EOF, err)
}
}

// Test normal retry fail as retry Count not enough.
func (r *aztestsSuite) TestRetryReaderReadNegativeNormalFail(c *chk.C) {
func TestRetryReaderReadNegativeNormalFail(t *testing.T) {
a := assert.New(t)
// Extra setup for testing notification of failures (i.e. of unsuccessful tries)
failureMethodNumCalls := 0
failureWillRetryCount := 0
Expand Down Expand Up @@ -267,20 +270,21 @@ func (r *aztestsSuite) TestRetryReaderReadNegativeNormalFail(c *chk.C) {
// should fail
can := make([]byte, 1)
n, err := retryReader.Read(can)
c.Assert(n, chk.Equals, 0)
c.Assert(err, chk.Equals, body.injectedError)
a.Zero(n)
a.Equal(body.injectedError, err)

// Check that we received the right notification callbacks
// We only expect two failed tries in this test, but only one
// of the would have had willRetry = true
c.Assert(failureMethodNumCalls, chk.Equals, 2) // this is the number of calls we counted
c.Assert(failureWillRetryCount, chk.Equals, 1) // only the first failure was retried
c.Assert(failureLastReportedFailureCount, chk.Equals, 2) // this is the number of failures reported by the notification method
c.Assert(failureLastReportedError, chk.NotNil)
a.Equal(2, failureMethodNumCalls) // this is the number of calls we counted
a.Equal(1, failureWillRetryCount) // only the first failure was retried
a.Equal(2, failureLastReportedFailureCount) // this is the number of failures reported by the notification method
a.NotNil(failureLastReportedError)
}

// Test boundary case when Count equals to 0 and fail.
func (r *aztestsSuite) TestRetryReaderReadCount0(c *chk.C) {
func TestRetryReaderReadCount0(t *testing.T) {
a := assert.New(t)
byteCount := 1
body := newPerByteReader(byteCount)
body.doInjectError = true
Expand All @@ -304,16 +308,17 @@ func (r *aztestsSuite) TestRetryReaderReadCount0(c *chk.C) {
// should consume the only byte
can := make([]byte, 1)
n, err := retryReader.Read(can)
c.Assert(n, chk.Equals, 1)
c.Assert(err, chk.IsNil)
a.Equal(1, n)
a.Nil(err)

// should not read when Count=0, and should return EOF
n, err = retryReader.Read(can)
c.Assert(n, chk.Equals, 0)
c.Assert(err, chk.Equals, io.EOF)
a.Zero(n)
a.Equal(io.EOF, err)
}

func (r *aztestsSuite) TestRetryReaderReadNegativeNonRetriableError(c *chk.C) {
func TestRetryReaderReadNegativeNonRetriableError(t *testing.T) {
a := assert.New(t)
byteCount := 1
body := newPerByteReader(byteCount)
body.doInjectError = true
Expand All @@ -336,16 +341,16 @@ func (r *aztestsSuite) TestRetryReaderReadNegativeNonRetriableError(c *chk.C) {

dest := make([]byte, 1)
_, err := retryReader.Read(dest)
c.Assert(err, chk.Equals, body.injectedError)
a.Equal(body.injectedError, err)
}

// Test the case where we programmatically force a retry to happen, via closing the body early from another goroutine
// Unlike the retries orchestrated elsewhere in this test file, which simulate network failures for the
// purposes of unit testing, here we are testing the cancellation mechanism that is exposed to
// consumers of the API, to allow programmatic forcing of retries (e.g. if the consumer deems
// the read to be taking too long, they may force a retry in the hope of better performance next time).
func (r *aztestsSuite) TestRetryReaderReadWithForcedRetry(c *chk.C) {

func TestRetryReaderReadWithForcedRetry(t *testing.T) {
a := assert.New(t)
for _, enableRetryOnEarlyClose := range []bool{false, true} {

// use the notification callback, so we know that the retry really did happen
Expand All @@ -371,7 +376,7 @@ func (r *aztestsSuite) TestRetryReaderReadWithForcedRetry(c *chk.C) {

httpGetterInfo := azbfs.HTTPGetterInfo{Offset: 0, Count: int64(byteCount)}
initResponse, err := getter(context.Background(), httpGetterInfo)
c.Assert(err, chk.IsNil)
a.Nil(err)

rrOptions := azbfs.RetryReaderOptions{MaxRetryRequests: 2, TreatEarlyCloseAsError: !enableRetryOnEarlyClose}
rrOptions.NotifyFailedRead = failureMethod
Expand All @@ -387,12 +392,12 @@ func (r *aztestsSuite) TestRetryReaderReadWithForcedRetry(c *chk.C) {
output := make([]byte, byteCount)
n, err := io.ReadFull(retryReader, output)
if enableRetryOnEarlyClose {
c.Assert(n, chk.Equals, byteCount)
c.Assert(err, chk.IsNil)
c.Assert(output, chk.DeepEquals, randBytes)
c.Assert(failureMethodNumCalls, chk.Equals, 1) // assert that the cancellation did indeed happen
a.Equal(byteCount, n)
a.Nil(err)
a.Equal(randBytes, output)
a.Equal(1, failureMethodNumCalls) // assert that the cancellation did indeed happen
} else {
c.Assert(err, chk.NotNil)
a.NotNil(err)
}
}
}
Expand Down
68 changes: 39 additions & 29 deletions azbfs/zt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,17 @@ package azbfs_test
import (
"context"
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"testing"
"time"

"github.com/Azure/azure-storage-azcopy/v10/azbfs"
chk "gopkg.in/check.v1"
)

func Test(t *testing.T) { chk.TestingT(t) }

type aztestsSuite struct{}

var _ = chk.Suite(&aztestsSuite{})

const (
fileSystemPrefix = "go"
directoryPrefix = "gotestdirectory"
Expand Down Expand Up @@ -59,7 +53,7 @@ func generateName(prefix string) string {
runtime.Callers(0, pc)
f := runtime.FuncForPC(pc[0])
name := f.Name()
for i := 0; !strings.Contains(name, "Suite"); i++ { // The tests are all scoped to the suite, so this ensures getting the actual test name
for i := 0; !strings.Contains(name, "Test"); i++ { // The tests are all scoped to the suite, so this ensures getting the actual test name
f = runtime.FuncForPC(pc[i])
name = f.Name()
}
Expand All @@ -83,67 +77,83 @@ func generateFileName() string {
return generateName(filePrefix)
}

func getFileSystemURL(c *chk.C, fsu azbfs.ServiceURL) (fs azbfs.FileSystemURL, name string) {
func getFileSystemURL(a *assert.Assertions, fsu azbfs.ServiceURL) (fs azbfs.FileSystemURL, name string) {
name = generateFileSystemName()
fs = fsu.NewFileSystemURL(name)

return fs, name
}

func getDirectoryURLFromFileSystem(c *chk.C, fs azbfs.FileSystemURL) (directory azbfs.DirectoryURL, name string) {
func getDirectoryURLFromFileSystem(a *assert.Assertions, fs azbfs.FileSystemURL) (directory azbfs.DirectoryURL, name string) {
name = generateDirectoryName()
directory = fs.NewDirectoryURL(name)
return directory, name
}

func getDirectoryURLFromDirectory(c *chk.C, parentDirectory azbfs.DirectoryURL) (directory azbfs.DirectoryURL, name string) {
func getDirectoryURLFromDirectory(a *assert.Assertions, parentDirectory azbfs.DirectoryURL) (directory azbfs.DirectoryURL, name string) {
name = generateDirectoryName()
directory = parentDirectory.NewDirectoryURL(name)
return directory, name
}

// This is a convenience method, No public API to create file URL from fileSystem now. This method uses fileSystem's root directory.
func getFileURLFromFileSystem(c *chk.C, fs azbfs.FileSystemURL) (file azbfs.FileURL, name string) {
func getFileURLFromFileSystem(a *assert.Assertions, fs azbfs.FileSystemURL) (file azbfs.FileURL, name string) {
name = generateFileName()
file = fs.NewRootDirectoryURL().NewFileURL(name)

return file, name
}

func getFileURLFromDirectory(c *chk.C, directory azbfs.DirectoryURL) (file azbfs.FileURL, name string) {
func getFileURLFromDirectory(a *assert.Assertions, directory azbfs.DirectoryURL) (file azbfs.FileURL, name string) {
name = generateFileName()
file = directory.NewFileURL(name)

return file, name
}

func createNewFileSystem(c *chk.C, fsu azbfs.ServiceURL) (fs azbfs.FileSystemURL, name string) {
fs, name = getFileSystemURL(c, fsu)
func createNewFileSystem(a *assert.Assertions, fsu azbfs.ServiceURL) (fs azbfs.FileSystemURL, name string) {
fs, name = getFileSystemURL(a, fsu)

cResp, err := fs.Create(ctx)
c.Assert(err, chk.IsNil)
c.Assert(cResp.StatusCode(), chk.Equals, 201)
_, err := fs.Create(ctx)
a.Nil(err)
return fs, name
}

func createNewDirectoryFromFileSystem(c *chk.C, fileSystem azbfs.FileSystemURL) (dir azbfs.DirectoryURL, name string) {
dir, name = getDirectoryURLFromFileSystem(c, fileSystem)
func createNewDirectoryFromFileSystem(a *assert.Assertions, fileSystem azbfs.FileSystemURL) (dir azbfs.DirectoryURL, name string) {
dir, name = getDirectoryURLFromFileSystem(a, fileSystem)

cResp, err := dir.Create(ctx, true)
c.Assert(err, chk.IsNil)
c.Assert(cResp.StatusCode(), chk.Equals, 201)
_, err := dir.Create(ctx, true)
a.Nil(err)
return dir, name
}

// This is a convenience method, No public API to create file URL from fileSystem now. This method uses fileSystem's root directory.
func createNewFileFromFileSystem(c *chk.C, fileSystem azbfs.FileSystemURL) (file azbfs.FileURL, name string) {
func createNewFileFromFileSystem(a *assert.Assertions, fileSystem azbfs.FileSystemURL) (file azbfs.FileURL, name string) {
dir := fileSystem.NewRootDirectoryURL()

file, name = getFileURLFromDirectory(c, dir)
file, name = getFileURLFromDirectory(a, dir)

cResp, err := file.Create(ctx, azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
c.Assert(err, chk.IsNil)
c.Assert(cResp.StatusCode(), chk.Equals, 201)
_, err := file.Create(ctx, azbfs.BlobFSHTTPHeaders{}, azbfs.BlobFSAccessControl{})
a.Nil(err)

return file, name
}

func deleteFileSystem(a *assert.Assertions, fs azbfs.FileSystemURL) {
resp, err := fs.Delete(context.Background())
a.Nil(err)
a.Equal(http.StatusAccepted, resp.Response().StatusCode)
}

// deleteDirectory deletes the directory represented by directory Url
func deleteDirectory(a *assert.Assertions, dul azbfs.DirectoryURL) {
resp, err := dul.Delete(context.Background(), nil, true)
a.Nil(err)
a.Equal(http.StatusOK, resp.Response().StatusCode)
}

func deleteFile(a *assert.Assertions, file azbfs.FileURL) {
resp, err := file.Delete(context.Background())
a.Nil(err)
a.Equal(http.StatusOK, resp.Response().StatusCode)
}
Loading