Skip to content

Commit 4e544bd

Browse files
committed
feat(Between): handles value implementing Compare & Less methods
as well as Gt, Gte, Lt and Lte of course. Compare and/or Less methods signatures follow: func (a T) Less(b T) bool // returns true if a < b func (a T) Compare(b T) int // returns -1 if a < b, 1 if a > b, 0 if a == b Additional Between, Gt, Gte, Lt and Lte changes: - failure reports display numbers as elsewhere; - docs fixed. Signed-off-by: Maxime Soulé <btik-git@scoubidou.com>
1 parent 515da4a commit 4e544bd

7 files changed

Lines changed: 519 additions & 107 deletions

File tree

internal/types/order.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2021-2022, Maxime Soulé
2+
// All rights reserved.
3+
//
4+
// This source code is licensed under the BSD-style license found in the
5+
// LICENSE file in the root directory of this source tree.
6+
7+
package types
8+
9+
import (
10+
"reflect"
11+
12+
"github.com/maxatome/go-testdeep/internal/dark"
13+
)
14+
15+
// NewOrder returns a function able to compare 2 non-nil values of type "t".
16+
// It returns nil if the type "t" is not comparable.
17+
func NewOrder(t reflect.Type) func(a, b reflect.Value) int {
18+
// Compare(T) int
19+
if m, ok := cmpMethod("Compare", t, Int); ok {
20+
return func(va, vb reflect.Value) int {
21+
// use dark.MustGetInterface() to bypass possible private fields
22+
ret := m.Call([]reflect.Value{
23+
reflect.ValueOf(dark.MustGetInterface(va)),
24+
reflect.ValueOf(dark.MustGetInterface(vb)),
25+
})
26+
return int(ret[0].Int())
27+
}
28+
}
29+
30+
// Less(T) bool
31+
if m, ok := cmpMethod("Less", t, Bool); ok {
32+
return func(va, vb reflect.Value) int {
33+
// use dark.MustGetInterface() to bypass possible private fields
34+
va = reflect.ValueOf(dark.MustGetInterface(va))
35+
vb = reflect.ValueOf(dark.MustGetInterface(vb))
36+
ret := m.Call([]reflect.Value{va, vb})
37+
if ret[0].Bool() { // a < b
38+
return -1
39+
}
40+
ret = m.Call([]reflect.Value{vb, va})
41+
if ret[0].Bool() { // b < a
42+
return 1
43+
}
44+
return 0
45+
}
46+
}
47+
48+
return nil
49+
}
50+
51+
func cmpMethod(name string, in, out reflect.Type) (reflect.Value, bool) {
52+
if equal, ok := in.MethodByName(name); ok {
53+
ft := equal.Type
54+
if !ft.IsVariadic() &&
55+
ft.NumIn() == 2 &&
56+
ft.NumOut() == 1 &&
57+
ft.In(0) == in &&
58+
ft.In(1) == in &&
59+
ft.Out(0) == out {
60+
return equal.Func, true
61+
}
62+
}
63+
return reflect.Value{}, false
64+
}

internal/types/order_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) 2022, Maxime Soulé
2+
// All rights reserved.
3+
//
4+
// This source code is licensed under the BSD-style license found in the
5+
// LICENSE file in the root directory of this source tree.
6+
7+
package types_test
8+
9+
import (
10+
"reflect"
11+
"testing"
12+
13+
"github.com/maxatome/go-testdeep/internal/test"
14+
"github.com/maxatome/go-testdeep/internal/types"
15+
)
16+
17+
type compareType int
18+
19+
func (i compareType) Compare(j compareType) int {
20+
if i < j {
21+
return -1
22+
}
23+
if i > j {
24+
return 1
25+
}
26+
return 0
27+
}
28+
29+
type lessType int
30+
31+
func (i lessType) Less(j lessType) bool {
32+
return i < j
33+
}
34+
35+
type badType1 int
36+
37+
func (i badType1) Compare(j ...badType1) int { return 0 } // IsVariadic()
38+
func (i badType1) Less(j, k badType1) bool { return false } // NumIn() == 3
39+
40+
type badType2 int
41+
42+
func (i badType2) Compare() int { return 0 } // NumIn() == 1
43+
func (i badType2) Less(j badType2) {} // NumOut() == 0
44+
45+
type badType3 int
46+
47+
func (i badType3) Compare(j badType3) (int, int) { return 0, 0 } // NumOut() == 2
48+
func (i badType3) Less(j int) bool { return false } // In(1) ≠ in
49+
50+
type badType4 int
51+
52+
func (i badType4) Compare(j badType4) bool { return false } // Out(0) ≠ out
53+
func (i badType4) Less(j badType4) int { return 0 } // Out(0) ≠ out
54+
55+
func TestOrder(t *testing.T) {
56+
if types.NewOrder(reflect.TypeOf(0)) != nil {
57+
t.Error("types.NewOrder(int) returned non-nil func")
58+
}
59+
60+
fn := types.NewOrder(reflect.TypeOf(compareType(0)))
61+
if fn == nil {
62+
t.Error("types.NewOrder(compareType) returned nil func")
63+
} else {
64+
a, b := reflect.ValueOf(compareType(1)), reflect.ValueOf(compareType(2))
65+
test.EqualInt(t, fn(a, b), -1)
66+
test.EqualInt(t, fn(b, a), 1)
67+
test.EqualInt(t, fn(a, a), 0)
68+
}
69+
70+
fn = types.NewOrder(reflect.TypeOf(lessType(0)))
71+
if fn == nil {
72+
t.Error("types.NewOrder(lessType) returned nil func")
73+
} else {
74+
a, b := reflect.ValueOf(lessType(1)), reflect.ValueOf(lessType(2))
75+
test.EqualInt(t, fn(a, b), -1)
76+
test.EqualInt(t, fn(b, a), 1)
77+
test.EqualInt(t, fn(a, a), 0)
78+
}
79+
80+
if types.NewOrder(reflect.TypeOf(badType1(0))) != nil {
81+
t.Error("types.NewOrder(badType1) returned non-nil func")
82+
}
83+
if types.NewOrder(reflect.TypeOf(badType2(0))) != nil {
84+
t.Error("types.NewOrder(badType2) returned non-nil func")
85+
}
86+
if types.NewOrder(reflect.TypeOf(badType3(0))) != nil {
87+
t.Error("types.NewOrder(badType3) returned non-nil func")
88+
}
89+
if types.NewOrder(reflect.TypeOf(badType4(0))) != nil {
90+
t.Error("types.NewOrder(badType4) returned non-nil func")
91+
}
92+
}

0 commit comments

Comments
 (0)