1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package xerrors_test 6 7import ( 8 "fmt" 9 "os" 10 "testing" 11 12 "golang.org/x/xerrors" 13) 14 15func TestIs(t *testing.T) { 16 err1 := xerrors.New("1") 17 erra := xerrors.Errorf("wrap 2: %w", err1) 18 errb := xerrors.Errorf("wrap 3: %w", erra) 19 erro := xerrors.Opaque(err1) 20 errco := xerrors.Errorf("opaque: %w", erro) 21 err3 := xerrors.New("3") 22 23 poser := &poser{"either 1 or 3", func(err error) bool { 24 return err == err1 || err == err3 25 }} 26 27 testCases := []struct { 28 err error 29 target error 30 match bool 31 }{ 32 {nil, nil, true}, 33 {nil, err1, false}, 34 {err1, nil, false}, 35 {err1, err1, true}, 36 {erra, err1, true}, 37 {errb, err1, true}, 38 {errco, erro, true}, 39 {errco, err1, false}, 40 {erro, erro, true}, 41 {err1, err3, false}, 42 {erra, err3, false}, 43 {errb, err3, false}, 44 {poser, err1, true}, 45 {poser, err3, true}, 46 {poser, erra, false}, 47 {poser, errb, false}, 48 {poser, erro, false}, 49 {poser, errco, false}, 50 {errorUncomparable{}, errorUncomparable{}, true}, 51 {errorUncomparable{}, &errorUncomparable{}, false}, 52 {&errorUncomparable{}, errorUncomparable{}, true}, 53 {&errorUncomparable{}, &errorUncomparable{}, false}, 54 {errorUncomparable{}, err1, false}, 55 {&errorUncomparable{}, err1, false}, 56 } 57 for _, tc := range testCases { 58 t.Run("", func(t *testing.T) { 59 if got := xerrors.Is(tc.err, tc.target); got != tc.match { 60 t.Errorf("Is(%v, %v) = %v, want %v", tc.err, tc.target, got, tc.match) 61 } 62 }) 63 } 64} 65 66type poser struct { 67 msg string 68 f func(error) bool 69} 70 71func (p *poser) Error() string { return p.msg } 72func (p *poser) Is(err error) bool { return p.f(err) } 73func (p *poser) As(err interface{}) bool { 74 switch x := err.(type) { 75 case **poser: 76 *x = p 77 case *errorT: 78 *x = errorT{} 79 case **os.PathError: 80 *x = &os.PathError{} 81 default: 82 return false 83 } 84 return true 85} 86 87func TestAs(t *testing.T) { 88 var errT errorT 89 var errP *os.PathError 90 var timeout interface{ Timeout() bool } 91 var p *poser 92 _, errF := os.Open("non-existing") 93 94 testCases := []struct { 95 err error 96 target interface{} 97 match bool 98 }{{ 99 nil, 100 &errP, 101 false, 102 }, { 103 xerrors.Errorf("pittied the fool: %w", errorT{}), 104 &errT, 105 true, 106 }, { 107 errF, 108 &errP, 109 true, 110 }, { 111 xerrors.Opaque(errT), 112 &errT, 113 false, 114 }, { 115 errorT{}, 116 &errP, 117 false, 118 }, { 119 errWrap{nil}, 120 &errT, 121 false, 122 }, { 123 &poser{"error", nil}, 124 &errT, 125 true, 126 }, { 127 &poser{"path", nil}, 128 &errP, 129 true, 130 }, { 131 &poser{"oh no", nil}, 132 &p, 133 true, 134 }, { 135 xerrors.New("err"), 136 &timeout, 137 false, 138 }, { 139 errF, 140 &timeout, 141 true, 142 }, { 143 xerrors.Errorf("path error: %w", errF), 144 &timeout, 145 true, 146 }} 147 for i, tc := range testCases { 148 name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.err, tc.target) 149 t.Run(name, func(t *testing.T) { 150 match := xerrors.As(tc.err, tc.target) 151 if match != tc.match { 152 t.Fatalf("xerrors.As(%T, %T): got %v; want %v", tc.err, tc.target, match, tc.match) 153 } 154 if !match { 155 return 156 } 157 if tc.target == nil { 158 t.Fatalf("non-nil result after match") 159 } 160 }) 161 } 162} 163 164func TestAsValidation(t *testing.T) { 165 var s string 166 testCases := []interface{}{ 167 nil, 168 (*int)(nil), 169 "error", 170 &s, 171 } 172 err := xerrors.New("error") 173 for _, tc := range testCases { 174 t.Run(fmt.Sprintf("%T(%v)", tc, tc), func(t *testing.T) { 175 defer func() { 176 recover() 177 }() 178 if xerrors.As(err, tc) { 179 t.Errorf("As(err, %T(%v)) = true, want false", tc, tc) 180 return 181 } 182 t.Errorf("As(err, %T(%v)) did not panic", tc, tc) 183 }) 184 } 185} 186 187func TestUnwrap(t *testing.T) { 188 err1 := xerrors.New("1") 189 erra := xerrors.Errorf("wrap 2: %w", err1) 190 erro := xerrors.Opaque(err1) 191 192 testCases := []struct { 193 err error 194 want error 195 }{ 196 {nil, nil}, 197 {errWrap{nil}, nil}, 198 {err1, nil}, 199 {erra, err1}, 200 {xerrors.Errorf("wrap 3: %w", erra), erra}, 201 202 {erro, nil}, 203 {xerrors.Errorf("opaque: %w", erro), erro}, 204 } 205 for _, tc := range testCases { 206 if got := xerrors.Unwrap(tc.err); got != tc.want { 207 t.Errorf("Unwrap(%v) = %v, want %v", tc.err, got, tc.want) 208 } 209 } 210} 211 212func TestOpaque(t *testing.T) { 213 got := fmt.Sprintf("%v", xerrors.Errorf("foo: %v", xerrors.Opaque(errorT{}))) 214 want := "foo: errorT" 215 if got != want { 216 t.Errorf("error without Format: got %v; want %v", got, want) 217 } 218 219 got = fmt.Sprintf("%v", xerrors.Errorf("foo: %v", xerrors.Opaque(errorD{}))) 220 want = "foo: errorD" 221 if got != want { 222 t.Errorf("error with Format: got %v; want %v", got, want) 223 } 224} 225 226type errorT struct{} 227 228func (errorT) Error() string { return "errorT" } 229 230type errorD struct{} 231 232func (errorD) Error() string { return "errorD" } 233 234func (errorD) FormatError(p xerrors.Printer) error { 235 p.Print("errorD") 236 p.Detail() 237 p.Print("detail") 238 return nil 239} 240 241type errWrap struct{ error } 242 243func (errWrap) Error() string { return "wrapped" } 244 245func (errWrap) Unwrap() error { return nil } 246 247type errorUncomparable struct { 248 f []string 249} 250 251func (errorUncomparable) Error() string { 252 return "uncomparable error" 253} 254 255func (errorUncomparable) Is(target error) bool { 256 _, ok := target.(errorUncomparable) 257 return ok 258} 259