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 }