@@ -3,11 +3,12 @@ package assert
33import (
44 "errors"
55 "fmt"
6- "github.com/adamluzsi/testcase/internal"
76 "reflect"
87 "strings"
98 "testing"
109
10+ "github.com/adamluzsi/testcase/internal"
11+
1112 "github.com/adamluzsi/testcase/internal/fmterror"
1213)
1314
@@ -149,6 +150,14 @@ func (a Asserter) NotPanic(blk func(), msg ...interface{}) {
149150 })
150151}
151152
153+ type equalable [T any ] interface {
154+ IsEqual (oth T ) bool
155+ }
156+
157+ type equalableWithError [T any ] interface {
158+ IsEqual (oth T ) (bool , error )
159+ }
160+
152161func (a Asserter ) Equal (expected , actually interface {}, msg ... interface {}) {
153162 a .TB .Helper ()
154163 if a .eq (expected , actually ) {
@@ -193,9 +202,50 @@ func (a Asserter) NotEqual(v, oth interface{}, msg ...interface{}) {
193202}
194203
195204func (a Asserter ) eq (exp , act interface {}) bool {
205+ if isEqual , ok := a .tryIsEqual (exp , act ); ok {
206+ return isEqual
207+ }
208+
196209 return reflect .DeepEqual (exp , act )
197210}
198211
212+ func (a Asserter ) tryIsEqual (exp , act interface {}) (isEqual bool , ok bool ) {
213+ defer func () { recover () }()
214+ expRV := reflect .ValueOf (exp )
215+ actRV := reflect .ValueOf (act )
216+
217+ if expRV .Type () != actRV .Type () {
218+ return false , false
219+ }
220+
221+ method := expRV .MethodByName ("IsEqual" )
222+ methodType := method .Type ()
223+
224+ if methodType .NumIn () != 1 {
225+ return false , false
226+ }
227+ if numOut := methodType .NumOut (); ! (numOut == 1 || numOut == 2 ) {
228+ return false , false
229+ }
230+ if methodType .In (0 ) != actRV .Type () {
231+ return false , false
232+ }
233+
234+ res := method .Call ([]reflect.Value {actRV })
235+
236+ switch {
237+ case methodType .NumOut () == 1 : // IsEqual(T) (bool)
238+ return res [0 ].Bool (), true
239+
240+ case methodType .NumOut () == 2 : // IsEqual(T) (bool, error)
241+ Must (a .TB ).Nil (res [1 ].Interface ())
242+ return res [0 ].Bool (), true
243+
244+ default :
245+ return false , false
246+ }
247+ }
248+
199249func (a Asserter ) Contain (src , has interface {}, msg ... interface {}) {
200250 a .TB .Helper ()
201251 rSrc := reflect .ValueOf (src )
@@ -575,55 +625,49 @@ func (a Asserter) AnyOf(blk func(a *AnyOf), msg ...interface{}) {
575625 blk (anyOf )
576626}
577627
578- // Empty gets whether the specified value is considered empty.
579- func (a Asserter ) Empty (v interface {}, msg ... interface {}) {
580- a .TB .Helper ()
581-
582- fail := func () {
583- a .Fn (fmterror.Message {
584- Method : "Empty" ,
585- Cause : "Value was expected to be empty." ,
586- Values : []fmterror.Value {
587- {Label : "value" , Value : v },
588- },
589- UserMessage : msg ,
590- })
591- }
592-
628+ func (a Asserter ) isEmpty (v any ) bool {
593629 if v == nil {
594- return
630+ return true
595631 }
596632 rv := reflect .ValueOf (v )
597633 switch rv .Kind () {
598634 case reflect .Chan , reflect .Map , reflect .Slice :
599- if rv .Len () != 0 {
600- fail ()
601- }
635+ return rv .Len () == 0
636+
602637 case reflect .Array :
603638 zero := reflect .New (rv .Type ()).Elem ().Interface ()
604- if ! a .eq (zero , v ) {
605- fail ()
606- }
639+ return a .eq (zero , v )
607640
608641 case reflect .Ptr :
609642 if rv .IsNil () {
610- return
611- }
612- if ! a .try (func (a Asserter ) { a .Empty (rv .Elem ().Interface ()) }) {
613- fail ()
643+ return true
614644 }
645+ return a .isEmpty (rv .Elem ().Interface ())
615646
616647 default :
617- if ! a .eq (reflect .Zero (rv .Type ()).Interface (), v ) {
618- fail ()
619- }
648+ return a .eq (reflect .Zero (rv .Type ()).Interface (), v )
620649 }
621650}
622651
652+ // Empty gets whether the specified value is considered empty.
653+ func (a Asserter ) Empty (v interface {}, msg ... interface {}) {
654+ a .TB .Helper ()
655+ if a .isEmpty (v ) {
656+ return
657+ }
658+ a .Fn (fmterror.Message {
659+ Method : "Empty" ,
660+ Cause : "Value was expected to be empty." ,
661+ Values : []fmterror.Value {
662+ {Label : "value" , Value : v },
663+ },
664+ UserMessage : msg ,
665+ })
666+ }
667+
623668// NotEmpty gets whether the specified value is considered empty.
624669func (a Asserter ) NotEmpty (v interface {}, msg ... interface {}) {
625670 a .TB .Helper ()
626-
627671 if ! a .try (func (a Asserter ) { a .Empty (v ) }) {
628672 return
629673 }
@@ -639,7 +683,7 @@ func (a Asserter) NotEmpty(v interface{}, msg ...interface{}) {
639683
640684// ErrorIs allows you to assert an error value by an expectation.
641685// if the implementation of the test subject later changes, and for example, it starts to use wrapping,
642- // this should not be an issue as the err 's error chain is also matched against the expectation.
686+ // this should not be an issue as the IsEqualErr 's error chain is also matched against the expectation.
643687func (a Asserter ) ErrorIs (expected , actual error , msg ... interface {}) {
644688 a .TB .Helper ()
645689
0 commit comments