Source file
src/errors/wrap_test.go
1
2
3
4
5 package errors_test
6
7 import (
8 "errors"
9 "fmt"
10 "io/fs"
11 "os"
12 "reflect"
13 "testing"
14 )
15
16 func TestIs(t *testing.T) {
17 err1 := errors.New("1")
18 erra := wrapped{"wrap 2", err1}
19 errb := wrapped{"wrap 3", erra}
20
21 err3 := errors.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 {err1, nil, false},
34 {err1, err1, true},
35 {erra, err1, true},
36 {errb, err1, true},
37 {err1, err3, false},
38 {erra, err3, false},
39 {errb, err3, false},
40 {poser, err1, true},
41 {poser, err3, true},
42 {poser, erra, false},
43 {poser, errb, false},
44 {errorUncomparable{}, errorUncomparable{}, true},
45 {errorUncomparable{}, &errorUncomparable{}, false},
46 {&errorUncomparable{}, errorUncomparable{}, true},
47 {&errorUncomparable{}, &errorUncomparable{}, false},
48 {errorUncomparable{}, err1, false},
49 {&errorUncomparable{}, err1, false},
50 }
51 for _, tc := range testCases {
52 t.Run("", func(t *testing.T) {
53 if got := errors.Is(tc.err, tc.target); got != tc.match {
54 t.Errorf("Is(%v, %v) = %v, want %v", tc.err, tc.target, got, tc.match)
55 }
56 })
57 }
58 }
59
60 type poser struct {
61 msg string
62 f func(error) bool
63 }
64
65 var poserPathErr = &fs.PathError{Op: "poser"}
66
67 func (p *poser) Error() string { return p.msg }
68 func (p *poser) Is(err error) bool { return p.f(err) }
69 func (p *poser) As(err any) bool {
70 switch x := err.(type) {
71 case **poser:
72 *x = p
73 case *errorT:
74 *x = errorT{"poser"}
75 case **fs.PathError:
76 *x = poserPathErr
77 default:
78 return false
79 }
80 return true
81 }
82
83 func TestAs(t *testing.T) {
84 var errT errorT
85 var errP *fs.PathError
86 var timeout interface{ Timeout() bool }
87 var p *poser
88 _, errF := os.Open("non-existing")
89 poserErr := &poser{"oh no", nil}
90
91 testCases := []struct {
92 err error
93 target any
94 match bool
95 want any
96 }{{
97 nil,
98 &errP,
99 false,
100 nil,
101 }, {
102 wrapped{"pitied the fool", errorT{"T"}},
103 &errT,
104 true,
105 errorT{"T"},
106 }, {
107 errF,
108 &errP,
109 true,
110 errF,
111 }, {
112 errorT{},
113 &errP,
114 false,
115 nil,
116 }, {
117 wrapped{"wrapped", nil},
118 &errT,
119 false,
120 nil,
121 }, {
122 &poser{"error", nil},
123 &errT,
124 true,
125 errorT{"poser"},
126 }, {
127 &poser{"path", nil},
128 &errP,
129 true,
130 poserPathErr,
131 }, {
132 poserErr,
133 &p,
134 true,
135 poserErr,
136 }, {
137 errors.New("err"),
138 &timeout,
139 false,
140 nil,
141 }, {
142 errF,
143 &timeout,
144 true,
145 errF,
146 }, {
147 wrapped{"path error", errF},
148 &timeout,
149 true,
150 errF,
151 }}
152 for i, tc := range testCases {
153 name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.err, tc.target)
154
155 rtarget := reflect.ValueOf(tc.target)
156 rtarget.Elem().Set(reflect.Zero(reflect.TypeOf(tc.target).Elem()))
157 t.Run(name, func(t *testing.T) {
158 match := errors.As(tc.err, tc.target)
159 if match != tc.match {
160 t.Fatalf("match: got %v; want %v", match, tc.match)
161 }
162 if !match {
163 return
164 }
165 if got := rtarget.Elem().Interface(); got != tc.want {
166 t.Fatalf("got %#v, want %#v", got, tc.want)
167 }
168 })
169 }
170 }
171
172 func TestAsValidation(t *testing.T) {
173 var s string
174 testCases := []any{
175 nil,
176 (*int)(nil),
177 "error",
178 &s,
179 }
180 err := errors.New("error")
181 for _, tc := range testCases {
182 t.Run(fmt.Sprintf("%T(%v)", tc, tc), func(t *testing.T) {
183 defer func() {
184 recover()
185 }()
186 if errors.As(err, tc) {
187 t.Errorf("As(err, %T(%v)) = true, want false", tc, tc)
188 return
189 }
190 t.Errorf("As(err, %T(%v)) did not panic", tc, tc)
191 })
192 }
193 }
194
195 func TestUnwrap(t *testing.T) {
196 err1 := errors.New("1")
197 erra := wrapped{"wrap 2", err1}
198
199 testCases := []struct {
200 err error
201 want error
202 }{
203 {nil, nil},
204 {wrapped{"wrapped", nil}, nil},
205 {err1, nil},
206 {erra, err1},
207 {wrapped{"wrap 3", erra}, erra},
208 }
209 for _, tc := range testCases {
210 if got := errors.Unwrap(tc.err); got != tc.want {
211 t.Errorf("Unwrap(%v) = %v, want %v", tc.err, got, tc.want)
212 }
213 }
214 }
215
216 type errorT struct{ s string }
217
218 func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
219
220 type wrapped struct {
221 msg string
222 err error
223 }
224
225 func (e wrapped) Error() string { return e.msg }
226
227 func (e wrapped) Unwrap() error { return e.err }
228
229 type errorUncomparable struct {
230 f []string
231 }
232
233 func (errorUncomparable) Error() string {
234 return "uncomparable error"
235 }
236
237 func (errorUncomparable) Is(target error) bool {
238 _, ok := target.(errorUncomparable)
239 return ok
240 }
241
242 func ExampleIs() {
243 if _, err := os.Open("non-existing"); err != nil {
244 if errors.Is(err, fs.ErrNotExist) {
245 fmt.Println("file does not exist")
246 } else {
247 fmt.Println(err)
248 }
249 }
250
251
252
253 }
254
255 func ExampleAs() {
256 if _, err := os.Open("non-existing"); err != nil {
257 var pathError *fs.PathError
258 if errors.As(err, &pathError) {
259 fmt.Println("Failed at path:", pathError.Path)
260 } else {
261 fmt.Println(err)
262 }
263 }
264
265
266
267 }
268
269 func ExampleUnwrap() {
270 err1 := errors.New("error1")
271 err2 := fmt.Errorf("error2: [%w]", err1)
272 fmt.Println(err2)
273 fmt.Println(errors.Unwrap(err2))
274
275
276
277 }
278
View as plain text