mirror of
https://github.com/golang/go
synced 2024-11-24 04:40:24 -07:00
34a43d7c1d
Check the value of target after As returns true. Change-Id: I76a2b25fe825ee1dbb5f39f8f0b211c55bd25a4f Reviewed-on: https://go-review.googlesource.com/c/go/+/181299 Reviewed-by: Bryan C. Mills <bcmills@google.com>
254 lines
4.8 KiB
Go
254 lines
4.8 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package errors_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestIs(t *testing.T) {
|
|
err1 := errors.New("1")
|
|
erra := wrapped{"wrap 2", err1}
|
|
errb := wrapped{"wrap 3", erra}
|
|
|
|
err3 := errors.New("3")
|
|
|
|
poser := &poser{"either 1 or 3", func(err error) bool {
|
|
return err == err1 || err == err3
|
|
}}
|
|
|
|
testCases := []struct {
|
|
err error
|
|
target error
|
|
match bool
|
|
}{
|
|
{nil, nil, true},
|
|
{err1, nil, false},
|
|
{err1, err1, true},
|
|
{erra, err1, true},
|
|
{errb, err1, true},
|
|
{err1, err3, false},
|
|
{erra, err3, false},
|
|
{errb, err3, false},
|
|
{poser, err1, true},
|
|
{poser, err3, true},
|
|
{poser, erra, false},
|
|
{poser, errb, false},
|
|
{errorUncomparable{}, errorUncomparable{}, true},
|
|
{errorUncomparable{}, &errorUncomparable{}, false},
|
|
{&errorUncomparable{}, errorUncomparable{}, true},
|
|
{&errorUncomparable{}, &errorUncomparable{}, false},
|
|
{errorUncomparable{}, err1, false},
|
|
{&errorUncomparable{}, err1, false},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run("", func(t *testing.T) {
|
|
if got := errors.Is(tc.err, tc.target); got != tc.match {
|
|
t.Errorf("Is(%v, %v) = %v, want %v", tc.err, tc.target, got, tc.match)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type poser struct {
|
|
msg string
|
|
f func(error) bool
|
|
}
|
|
|
|
var poserPathErr = &os.PathError{Op: "poser"}
|
|
|
|
func (p *poser) Error() string { return p.msg }
|
|
func (p *poser) Is(err error) bool { return p.f(err) }
|
|
func (p *poser) As(err interface{}) bool {
|
|
switch x := err.(type) {
|
|
case **poser:
|
|
*x = p
|
|
case *errorT:
|
|
*x = errorT{"poser"}
|
|
case **os.PathError:
|
|
*x = poserPathErr
|
|
default:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TestAs(t *testing.T) {
|
|
var errT errorT
|
|
var errP *os.PathError
|
|
var timeout interface{ Timeout() bool }
|
|
var p *poser
|
|
_, errF := os.Open("non-existing")
|
|
poserErr := &poser{"oh no", nil}
|
|
|
|
testCases := []struct {
|
|
err error
|
|
target interface{}
|
|
match bool
|
|
want interface{} // value of target on match
|
|
}{{
|
|
nil,
|
|
&errP,
|
|
false,
|
|
nil,
|
|
}, {
|
|
wrapped{"pitied the fool", errorT{"T"}},
|
|
&errT,
|
|
true,
|
|
errorT{"T"},
|
|
}, {
|
|
errF,
|
|
&errP,
|
|
true,
|
|
errF,
|
|
}, {
|
|
errorT{},
|
|
&errP,
|
|
false,
|
|
nil,
|
|
}, {
|
|
wrapped{"wrapped", nil},
|
|
&errT,
|
|
false,
|
|
nil,
|
|
}, {
|
|
&poser{"error", nil},
|
|
&errT,
|
|
true,
|
|
errorT{"poser"},
|
|
}, {
|
|
&poser{"path", nil},
|
|
&errP,
|
|
true,
|
|
poserPathErr,
|
|
}, {
|
|
poserErr,
|
|
&p,
|
|
true,
|
|
poserErr,
|
|
}, {
|
|
errors.New("err"),
|
|
&timeout,
|
|
false,
|
|
nil,
|
|
}, {
|
|
errF,
|
|
&timeout,
|
|
true,
|
|
errF,
|
|
}, {
|
|
wrapped{"path error", errF},
|
|
&timeout,
|
|
true,
|
|
errF,
|
|
}}
|
|
for i, tc := range testCases {
|
|
name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.err, tc.target)
|
|
// Clear the target pointer, in case it was set in a previous test.
|
|
rtarget := reflect.ValueOf(tc.target)
|
|
rtarget.Elem().Set(reflect.Zero(reflect.TypeOf(tc.target).Elem()))
|
|
t.Run(name, func(t *testing.T) {
|
|
match := errors.As(tc.err, tc.target)
|
|
if match != tc.match {
|
|
t.Fatalf("match: got %v; want %v", match, tc.match)
|
|
}
|
|
if !match {
|
|
return
|
|
}
|
|
if got := rtarget.Elem().Interface(); got != tc.want {
|
|
t.Fatalf("got %#v, want %#v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAsValidation(t *testing.T) {
|
|
var s string
|
|
testCases := []interface{}{
|
|
nil,
|
|
(*int)(nil),
|
|
"error",
|
|
&s,
|
|
}
|
|
err := errors.New("error")
|
|
for _, tc := range testCases {
|
|
t.Run(fmt.Sprintf("%T(%v)", tc, tc), func(t *testing.T) {
|
|
defer func() {
|
|
recover()
|
|
}()
|
|
if errors.As(err, tc) {
|
|
t.Errorf("As(err, %T(%v)) = true, want false", tc, tc)
|
|
return
|
|
}
|
|
t.Errorf("As(err, %T(%v)) did not panic", tc, tc)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnwrap(t *testing.T) {
|
|
err1 := errors.New("1")
|
|
erra := wrapped{"wrap 2", err1}
|
|
|
|
testCases := []struct {
|
|
err error
|
|
want error
|
|
}{
|
|
{nil, nil},
|
|
{wrapped{"wrapped", nil}, nil},
|
|
{err1, nil},
|
|
{erra, err1},
|
|
{wrapped{"wrap 3", erra}, erra},
|
|
}
|
|
for _, tc := range testCases {
|
|
if got := errors.Unwrap(tc.err); got != tc.want {
|
|
t.Errorf("Unwrap(%v) = %v, want %v", tc.err, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type errorT struct{ s string }
|
|
|
|
func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
|
|
|
|
type wrapped struct {
|
|
msg string
|
|
err error
|
|
}
|
|
|
|
func (e wrapped) Error() string { return e.msg }
|
|
|
|
func (e wrapped) Unwrap() error { return e.err }
|
|
|
|
type errorUncomparable struct {
|
|
f []string
|
|
}
|
|
|
|
func (errorUncomparable) Error() string {
|
|
return "uncomparable error"
|
|
}
|
|
|
|
func (errorUncomparable) Is(target error) bool {
|
|
_, ok := target.(errorUncomparable)
|
|
return ok
|
|
}
|
|
|
|
func ExampleAs() {
|
|
if _, err := os.Open("non-existing"); err != nil {
|
|
var pathError *os.PathError
|
|
if errors.As(err, &pathError) {
|
|
fmt.Println("Failed at path:", pathError.Path)
|
|
} else {
|
|
fmt.Println(err)
|
|
}
|
|
}
|
|
|
|
// Output:
|
|
// Failed at path: non-existing
|
|
}
|