feat(ErrorIs): becomes a smuggler operator with errors.As feature · maxatome/go-testdeep@3638797

@@ -8,68 +8,153 @@ package td_test

8899

import (

1010

"fmt"

11+

"io"

1112

"testing"

12131314

"github.com/maxatome/go-testdeep/internal/dark"

1415

"github.com/maxatome/go-testdeep/internal/test"

1516

"github.com/maxatome/go-testdeep/td"

1617

)

171819+

type errorIsSimpleErr string

20+21+

func (e errorIsSimpleErr) Error() string {

22+

return string(e)

23+

}

24+25+

type errorIsWrappedErr struct {

26+

s string

27+

err error

28+

}

29+30+

func (e errorIsWrappedErr) Error() string {

31+

if e.err != nil {

32+

return e.s + ": " + e.err.Error()

33+

}

34+

return e.s + ": nil"

35+

}

36+37+

func (e errorIsWrappedErr) Unwrap() error {

38+

return e.err

39+

}

40+41+

var _ = []error{errorIsSimpleErr(""), errorIsWrappedErr{}}

42+1843

func TestErrorIs(t *testing.T) {

19-

insideErr1 := fmt.Errorf("failure1")

20-

insideErr2 := fmt.Errorf("failure2: %w", insideErr1)

21-

insideErr3 := fmt.Errorf("failure3: %w", insideErr2)

22-

err := fmt.Errorf("failure4: %w", insideErr3)

44+

insideErr1 := errorIsSimpleErr("failure1")

45+

insideErr2 := errorIsWrappedErr{"failure2", insideErr1}

46+

insideErr3 := errorIsWrappedErr{"failure3", insideErr2}

47+

err := errorIsWrappedErr{"failure4", insideErr3}

23482449

checkOK(t, err, td.ErrorIs(err))

2550

checkOK(t, err, td.ErrorIs(insideErr3))

2651

checkOK(t, err, td.ErrorIs(insideErr2))

2752

checkOK(t, err, td.ErrorIs(insideErr1))

2853

checkOK(t, nil, td.ErrorIs(nil))

295455+

checkOK(t, err, td.ErrorIs(td.All(

56+

td.Isa(errorIsSimpleErr("")),

57+

td.String("failure1"),

58+

)))

59+60+

// many errorIsWrappedErr in the err's tree, so only the first

61+

// encountered matches

62+

checkOK(t, err, td.ErrorIs(td.All(

63+

td.Isa(errorIsWrappedErr{}),

64+

td.HasPrefix("failure4"),

65+

)))

66+67+

// HasPrefix().TypeBehind() always returns nil

68+

// so errors.As() is called with &any, so the toplevel error matches

69+

checkOK(t, err, td.ErrorIs(td.HasPrefix("failure4")))

70+3071

var errNil error

3172

checkOK(t, &errNil, td.Ptr(td.ErrorIs(nil)))

327374+

var inside errorIsSimpleErr

75+

checkOK(t, err, td.ErrorIs(td.Catch(&inside, td.String("failure1"))))

76+

test.EqualStr(t, string(inside), "failure1")

77+3378

checkError(t, nil, td.ErrorIs(insideErr1),

3479

expectedError{

35-

Message: mustBe("nil value"),

3680

Path: mustBe("DATA"),

81+

Message: mustBe("nil value"),

3782

Got: mustBe("nil"),

3883

Expected: mustBe("anything implementing error interface"),

3984

})

40854186

checkError(t, 45, td.ErrorIs(insideErr1),

4287

expectedError{

43-

Message: mustBe("int does not implement error interface"),

4488

Path: mustBe("DATA"),

89+

Message: mustBe("int does not implement error interface"),

4590

Got: mustBe("45"),

4691

Expected: mustBe("anything implementing error interface"),

4792

})

48934994

checkError(t, 45, td.ErrorIs(fmt.Errorf("another")),

5095

expectedError{

51-

Message: mustBe("int does not implement error interface"),

5296

Path: mustBe("DATA"),

97+

Message: mustBe("int does not implement error interface"),

5398

Got: mustBe("45"),

5499

Expected: mustBe("anything implementing error interface"),

55100

})

5610157102

checkError(t, err, td.ErrorIs(fmt.Errorf("another")),

58103

expectedError{

59-

Message: mustBe("is not the error"),

60104

Path: mustBe("DATA"),

61-

Got: mustBe(`(*fmt.wrapError) "failure4: failure3: failure2: failure1"`),

105+

Message: mustBe("is not found in err's tree"),

106+

Got: mustBe(`(td_test.errorIsWrappedErr) "failure4: failure3: failure2: failure1"`),

62107

Expected: mustBe(`(*errors.errorString) "another"`),

63108

})

64109110+

checkError(t, err, td.ErrorIs(td.String("nonono")),

111+

expectedError{

112+

Path: mustBe("DATA.ErrorIs(interface {})"),

113+

Message: mustBe("does not match"),

114+

Got: mustBe(`"failure4: failure3: failure2: failure1"`),

115+

Expected: mustBe(`"nonono"`),

116+

})

117+118+

checkError(t, err, td.ErrorIs(td.Isa(fmt.Errorf("another"))),

119+

expectedError{

120+

Path: mustBe("DATA"),

121+

Message: mustBe("type is not found in err's tree"),

122+

Got: mustBe(`(td_test.errorIsWrappedErr) failure4: failure3: failure2: failure1`),

123+

Expected: mustBe(`*errors.errorString`),

124+

})

125+126+

checkError(t, err, td.ErrorIs(td.Smuggle(io.ReadAll, td.String("xx"))),

127+

expectedError{

128+

Path: mustBe("DATA"),

129+

Message: mustBe("type is not found in err's tree"),

130+

Got: mustBe(`(td_test.errorIsWrappedErr) failure4: failure3: failure2: failure1`),

131+

Expected: mustBe(`io.Reader`),

132+

})

133+65134

checkError(t, err, td.ErrorIs(nil),

66135

expectedError{

67-

Message: mustBe("is not the error"),

68136

Path: mustBe("DATA"),

69-

Got: mustBe(`(*fmt.wrapError) "failure4: failure3: failure2: failure1"`),

137+

Message: mustBe("is not nil"),

138+

Got: mustBe(`(td_test.errorIsWrappedErr) "failure4: failure3: failure2: failure1"`),

139+

Expected: mustBe(`nil`),

140+

})

141+142+

// As errors.Is, it does not match

143+

checkError(t, errorIsWrappedErr{"failure", nil}, td.ErrorIs(nil),

144+

expectedError{

145+

Path: mustBe("DATA"),

146+

Message: mustBe("is not nil"),

147+

Got: mustBe(`(td_test.errorIsWrappedErr) "failure: nil"`),

70148

Expected: mustBe(`nil`),

71149

})

72150151+

checkError(t, err, td.ErrorIs(td.Gt(0)),

152+

expectedError{

153+

Path: mustBe("DATA"),

154+

Message: mustBe("bad usage of ErrorIs operator"),

155+

Summary: mustBe(`ErrorIs(Gt): type int behind Gt operator is not an interface or does not implement error`),

156+

})

157+73158

type private struct{ err error }

74159

got := private{err: err}

75160

for _, expErr := range []error{err, insideErr3} {

@@ -95,6 +180,9 @@ func TestErrorIs(t *testing.T) {

95180

// String

96181

test.EqualStr(t, td.ErrorIs(insideErr1).String(), "ErrorIs(failure1)")

97182

test.EqualStr(t, td.ErrorIs(nil).String(), "ErrorIs(nil)")

183+

test.EqualStr(t, td.ErrorIs(td.HasPrefix("pipo")).String(),

184+

`ErrorIs(HasPrefix("pipo"))`)

185+

test.EqualStr(t, td.ErrorIs(12).String(), "ErrorIs(<ERROR>)")

98186

}

99187100188

func TestErrorIsTypeBehind(t *testing.T) {