github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/fserrors/error_test.go (about)

     1  package fserrors
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"net/url"
     8  	"os"
     9  	"syscall"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  var errUseOfClosedNetworkConnection = errors.New("use of closed network connection")
    18  
    19  // make a plausible network error with the underlying errno
    20  func makeNetErr(errno syscall.Errno) error {
    21  	return &net.OpError{
    22  		Op:     "write",
    23  		Net:    "tcp",
    24  		Source: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 123},
    25  		Addr:   &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8080},
    26  		Err: &os.SyscallError{
    27  			Syscall: "write",
    28  			Err:     errno,
    29  		},
    30  	}
    31  }
    32  
    33  type myError1 struct {
    34  	Err error
    35  }
    36  
    37  func (e myError1) Error() string { return e.Err.Error() }
    38  
    39  type myError2 struct {
    40  	Err error
    41  }
    42  
    43  func (e *myError2) Error() string {
    44  	if e == nil {
    45  		return "myError2(nil)"
    46  	}
    47  	if e.Err == nil {
    48  		return "myError2{Err: nil}"
    49  	}
    50  	return e.Err.Error()
    51  }
    52  
    53  type myError3 struct {
    54  	Err int
    55  }
    56  
    57  func (e *myError3) Error() string { return "hello" }
    58  
    59  type myError4 struct {
    60  	e error
    61  }
    62  
    63  func (e *myError4) Error() string { return e.e.Error() }
    64  
    65  type myError5 struct{}
    66  
    67  func (e *myError5) Error() string { return "" }
    68  
    69  func (e *myError5) Temporary() bool { return true }
    70  
    71  type errorCause struct {
    72  	e error
    73  }
    74  
    75  func (e *errorCause) Error() string { return fmt.Sprintf("%#v", e) }
    76  
    77  func (e *errorCause) Cause() error { return e.e }
    78  
    79  func TestCause(t *testing.T) {
    80  	e3 := &myError3{3}
    81  	e4 := &myError4{io.EOF}
    82  	e5 := &myError5{}
    83  	eNil1 := &myError2{nil}
    84  	eNil2 := &myError2{Err: (*myError2)(nil)}
    85  	errPotato := errors.New("potato")
    86  	nilCause1 := &errorCause{nil}
    87  	nilCause2 := &errorCause{(*myError2)(nil)}
    88  
    89  	for i, test := range []struct {
    90  		err           error
    91  		wantRetriable bool
    92  		wantErr       error
    93  	}{
    94  		{nil, false, nil},
    95  		{errPotato, false, errPotato},
    96  		{errors.Wrap(errPotato, "potato"), false, errPotato},
    97  		{errors.Wrap(errors.Wrap(errPotato, "potato2"), "potato"), false, errPotato},
    98  		{errUseOfClosedNetworkConnection, false, errUseOfClosedNetworkConnection},
    99  		{makeNetErr(syscall.EAGAIN), true, syscall.EAGAIN},
   100  		{makeNetErr(syscall.Errno(123123123)), false, syscall.Errno(123123123)},
   101  		{eNil1, false, eNil1},
   102  		{eNil2, false, eNil2.Err},
   103  		{myError1{io.EOF}, false, io.EOF},
   104  		{&myError2{io.EOF}, false, io.EOF},
   105  		{e3, false, e3},
   106  		{e4, false, e4},
   107  		{e5, true, e5},
   108  		{&errorCause{errPotato}, false, errPotato},
   109  		{nilCause1, false, nilCause1},
   110  		{nilCause2, false, nilCause2.e},
   111  	} {
   112  		gotRetriable, gotErr := Cause(test.err)
   113  		what := fmt.Sprintf("test #%d: %v", i, test.err)
   114  		assert.Equal(t, test.wantErr, gotErr, what)
   115  		assert.Equal(t, test.wantRetriable, gotRetriable, what)
   116  	}
   117  }
   118  
   119  func TestShouldRetry(t *testing.T) {
   120  	for i, test := range []struct {
   121  		err  error
   122  		want bool
   123  	}{
   124  		{nil, false},
   125  		{errors.New("potato"), false},
   126  		{errors.Wrap(errUseOfClosedNetworkConnection, "connection"), true},
   127  		{io.EOF, true},
   128  		{io.ErrUnexpectedEOF, true},
   129  		{makeNetErr(syscall.EAGAIN), true},
   130  		{makeNetErr(syscall.Errno(123123123)), false},
   131  		{&url.Error{Op: "post", URL: "/", Err: io.EOF}, true},
   132  		{&url.Error{Op: "post", URL: "/", Err: errUseOfClosedNetworkConnection}, true},
   133  		{&url.Error{Op: "post", URL: "/", Err: fmt.Errorf("net/http: HTTP/1.x transport connection broken: %v", fmt.Errorf("http: ContentLength=%d with Body length %d", 100663336, 99590598))}, true},
   134  		{
   135  			errors.Wrap(&url.Error{
   136  				Op:  "post",
   137  				URL: "http://localhost/",
   138  				Err: makeNetErr(syscall.EPIPE),
   139  			}, "potato error"),
   140  			true,
   141  		},
   142  		{
   143  			errors.Wrap(&url.Error{
   144  				Op:  "post",
   145  				URL: "http://localhost/",
   146  				Err: makeNetErr(syscall.Errno(123123123)),
   147  			}, "listing error"),
   148  			false,
   149  		},
   150  	} {
   151  		got := ShouldRetry(test.err)
   152  		assert.Equal(t, test.want, got, fmt.Sprintf("test #%d: %v", i, test.err))
   153  	}
   154  }
   155  
   156  func TestRetryAfter(t *testing.T) {
   157  	e := NewErrorRetryAfter(time.Second)
   158  	after := e.RetryAfter()
   159  	dt := after.Sub(time.Now())
   160  	assert.True(t, dt >= 900*time.Millisecond && dt <= 1100*time.Millisecond)
   161  	assert.True(t, IsRetryAfterError(e))
   162  	assert.False(t, IsRetryAfterError(io.EOF))
   163  	assert.Equal(t, time.Time{}, RetryAfterErrorTime(io.EOF))
   164  	assert.False(t, IsRetryAfterError(nil))
   165  	assert.Contains(t, e.Error(), "try again after")
   166  
   167  	t0 := time.Now()
   168  	err := errors.Wrap(ErrorRetryAfter(t0), "potato")
   169  	assert.Equal(t, t0, RetryAfterErrorTime(err))
   170  	assert.True(t, IsRetryAfterError(err))
   171  	assert.Contains(t, e.Error(), "try again after")
   172  }