github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/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 errorCause struct {
    66  	e error
    67  }
    68  
    69  func (e *errorCause) Error() string { return fmt.Sprintf("%#v", e) }
    70  
    71  func (e *errorCause) Cause() error { return e.e }
    72  
    73  func TestCause(t *testing.T) {
    74  	e3 := &myError3{3}
    75  	e4 := &myError4{io.EOF}
    76  	eNil1 := &myError2{nil}
    77  	eNil2 := &myError2{Err: (*myError2)(nil)}
    78  	errPotato := errors.New("potato")
    79  	nilCause1 := &errorCause{nil}
    80  	nilCause2 := &errorCause{(*myError2)(nil)}
    81  
    82  	for i, test := range []struct {
    83  		err           error
    84  		wantRetriable bool
    85  		wantErr       error
    86  	}{
    87  		{nil, false, nil},
    88  		{errPotato, false, errPotato},
    89  		{errors.Wrap(errPotato, "potato"), false, errPotato},
    90  		{errors.Wrap(errors.Wrap(errPotato, "potato2"), "potato"), false, errPotato},
    91  		{errUseOfClosedNetworkConnection, false, errUseOfClosedNetworkConnection},
    92  		{makeNetErr(syscall.EAGAIN), true, syscall.EAGAIN},
    93  		{makeNetErr(syscall.Errno(123123123)), false, syscall.Errno(123123123)},
    94  		{eNil1, false, eNil1},
    95  		{eNil2, false, eNil2.Err},
    96  		{myError1{io.EOF}, false, io.EOF},
    97  		{&myError2{io.EOF}, false, io.EOF},
    98  		{e3, false, e3},
    99  		{e4, false, e4},
   100  		{&errorCause{errPotato}, false, errPotato},
   101  		{nilCause1, false, nilCause1},
   102  		{nilCause2, false, nilCause2.e},
   103  	} {
   104  		gotRetriable, gotErr := Cause(test.err)
   105  		what := fmt.Sprintf("test #%d: %v", i, test.err)
   106  		assert.Equal(t, test.wantErr, gotErr, what)
   107  		assert.Equal(t, test.wantRetriable, gotRetriable, what)
   108  	}
   109  }
   110  
   111  func TestShouldRetry(t *testing.T) {
   112  	for i, test := range []struct {
   113  		err  error
   114  		want bool
   115  	}{
   116  		{nil, false},
   117  		{errors.New("potato"), false},
   118  		{errors.Wrap(errUseOfClosedNetworkConnection, "connection"), true},
   119  		{io.EOF, true},
   120  		{io.ErrUnexpectedEOF, true},
   121  		{makeNetErr(syscall.EAGAIN), true},
   122  		{makeNetErr(syscall.Errno(123123123)), false},
   123  		{&url.Error{Op: "post", URL: "/", Err: io.EOF}, true},
   124  		{&url.Error{Op: "post", URL: "/", Err: errUseOfClosedNetworkConnection}, true},
   125  		{&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},
   126  		{
   127  			errors.Wrap(&url.Error{
   128  				Op:  "post",
   129  				URL: "http://localhost/",
   130  				Err: makeNetErr(syscall.EPIPE),
   131  			}, "potato error"),
   132  			true,
   133  		},
   134  		{
   135  			errors.Wrap(&url.Error{
   136  				Op:  "post",
   137  				URL: "http://localhost/",
   138  				Err: makeNetErr(syscall.Errno(123123123)),
   139  			}, "listing error"),
   140  			false,
   141  		},
   142  	} {
   143  		got := ShouldRetry(test.err)
   144  		assert.Equal(t, test.want, got, fmt.Sprintf("test #%d: %v", i, test.err))
   145  	}
   146  }
   147  
   148  func TestRetryAfter(t *testing.T) {
   149  	e := NewErrorRetryAfter(time.Second)
   150  	after := e.RetryAfter()
   151  	dt := after.Sub(time.Now())
   152  	assert.True(t, dt >= 900*time.Millisecond && dt <= 1100*time.Millisecond)
   153  	assert.True(t, IsRetryAfterError(e))
   154  	assert.False(t, IsRetryAfterError(io.EOF))
   155  	assert.Equal(t, time.Time{}, RetryAfterErrorTime(io.EOF))
   156  	assert.False(t, IsRetryAfterError(nil))
   157  	assert.Contains(t, e.Error(), "try again after")
   158  
   159  	t0 := time.Now()
   160  	err := errors.Wrap(ErrorRetryAfter(t0), "potato")
   161  	assert.Equal(t, t0, RetryAfterErrorTime(err))
   162  	assert.True(t, IsRetryAfterError(err))
   163  	assert.Contains(t, e.Error(), "try again after")
   164  }