Skip to content

Commit

Permalink
error.Is testing checker
Browse files Browse the repository at this point in the history
The following makes it easier to compare errors. Currently, if
we attempt to use satisfies checker, we'll more thank likely endup
with a deprecation warning that Is prefix predicate variants from
the errors package is being used.

The solution is to make a new checker that we can move to, which has
the semantics that we want moving forward.
  • Loading branch information
SimonRichardson committed Aug 15, 2023
1 parent 9af10d6 commit 448af9c
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
52 changes: 52 additions & 0 deletions checkers/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2023 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package checkers

import (
"errors"
"fmt"
"reflect"

gc "gopkg.in/check.v1"
)

type errorIsChecker struct {
*gc.CheckerInfo
}

// ErrorIs checks whether a value is an error that matches the other
// argument.
var ErrorIs gc.Checker = &errorIsChecker{
CheckerInfo: &gc.CheckerInfo{
Name: "ErrorIs",
Params: []string{"obtained", "error"},
},
}

var (
errType = reflect.TypeOf((*error)(nil)).Elem()
)

func (checker *errorIsChecker) Check(params []interface{}, names []string) (result bool, err string) {
if params[1] == nil || params[0] == nil {
return params[1] == params[0], ""
}

f := reflect.ValueOf(params[1])
ft := f.Type()
if !ft.Implements(errType) {
return false, fmt.Sprintf("wrong error target type, got: %s", ft)
}

v := reflect.ValueOf(params[0])
vt := v.Type()
if !v.IsValid() {
return false, fmt.Sprintf("wrong argument type %s for %s", vt, ft)
}
if !vt.Implements(errType) {
return false, fmt.Sprintf("wrong argument type %s for %s", vt, ft)
}

return errors.Is(v.Interface().(error), f.Interface().(error)), ""
}
69 changes: 69 additions & 0 deletions checkers/error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package checkers_test

import (
"fmt"

"github.com/juju/errors"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
)

type ErrorSuite struct{}

var _ = gc.Suite(&ErrorSuite{})

var errorIsTests = []struct {
arg interface{}
target interface{}
result bool
msg string
}{{
arg: fmt.Errorf("bar"),
target: nil,
result: false,
}, {
arg: nil,
target: fmt.Errorf("bar"),
result: false,
}, {
arg: nil,
target: nil,
result: true,
}, {
arg: fmt.Errorf("bar"),
target: fmt.Errorf("foo"),
result: false,
}, {
arg: errors.ConstError("bar"),
target: errors.ConstError("foo"),
result: false,
}, {
arg: errors.ConstError("foo"),
target: errors.ConstError("foo"),
result: true,
}, {
arg: errors.Trace(errors.ConstError("foo")),
target: errors.ConstError("foo"),
result: true,
}, {
arg: errors.ConstError("foo"),
target: "blah",
msg: "wrong error target type, got: string",
}, {
arg: "blah",
target: errors.ConstError("foo"),
msg: "wrong argument type string for errors.ConstError",
}, {
arg: (*error)(nil),
target: errors.ConstError("foo"),
msg: "wrong argument type *error for errors.ConstError",
}}

func (s *ErrorSuite) TestErrorIs(c *gc.C) {
for i, test := range errorIsTests {
c.Logf("test %d. %T %T", i, test.arg, test.target)
result, msg := jc.ErrorIs.Check([]interface{}{test.arg, test.target}, nil)
c.Check(result, gc.Equals, test.result)
c.Check(msg, gc.Equals, test.msg)
}
}

0 comments on commit 448af9c

Please sign in to comment.