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