/
struct.go
92 lines (82 loc) · 2.89 KB
/
struct.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package gen
import (
"reflect"
"github.com/leanovate/gopter"
)
// Struct generates a given struct type.
// rt has to be the reflect type of the struct, gens contains a map of field generators.
// Note that the result types of the generators in gen have to match the type of the correspoinding
// field in the struct. Also note that only public fields of a struct can be generated
func Struct(rt reflect.Type, gens map[string]gopter.Gen) gopter.Gen {
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rt.Kind() != reflect.Struct {
return Fail(rt)
}
fieldGens := []gopter.Gen{}
fieldTypes := []reflect.Type{}
for i := 0; i < rt.NumField(); i++ {
fieldName := rt.Field(i).Name
gen := gens[fieldName]
if gen != nil {
fieldGens = append(fieldGens, gen)
fieldTypes = append(fieldTypes, rt.Field(i).Type)
}
}
buildStructType := reflect.FuncOf(fieldTypes, []reflect.Type{rt}, false)
unbuildStructType := reflect.FuncOf([]reflect.Type{rt}, fieldTypes, false)
buildStructFunc := reflect.MakeFunc(buildStructType, func(args []reflect.Value) []reflect.Value {
result := reflect.New(rt)
for i := 0; i < rt.NumField(); i++ {
if _, ok := gens[rt.Field(i).Name]; !ok {
continue
}
result.Elem().Field(i).Set(args[0])
args = args[1:]
}
return []reflect.Value{result.Elem()}
})
unbuildStructFunc := reflect.MakeFunc(unbuildStructType, func(args []reflect.Value) []reflect.Value {
s := args[0]
results := []reflect.Value{}
for i := 0; i < s.NumField(); i++ {
if _, ok := gens[rt.Field(i).Name]; !ok {
continue
}
results = append(results, s.Field(i))
}
return results
})
return gopter.DeriveGen(
buildStructFunc.Interface(),
unbuildStructFunc.Interface(),
fieldGens...,
)
}
// StructPtr generates pointers to a given struct type.
// Note that StructPtr does not generate nil, if you want to include nil in your
// testing you should combine gen.PtrOf with gen.Struct.
// rt has to be the reflect type of the struct, gens contains a map of field generators.
// Note that the result types of the generators in gen have to match the type of the correspoinding
// field in the struct. Also note that only public fields of a struct can be generated
func StructPtr(rt reflect.Type, gens map[string]gopter.Gen) gopter.Gen {
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
buildPtrType := reflect.FuncOf([]reflect.Type{rt}, []reflect.Type{reflect.PtrTo(rt)}, false)
unbuildPtrType := reflect.FuncOf([]reflect.Type{reflect.PtrTo(rt)}, []reflect.Type{rt}, false)
buildPtrFunc := reflect.MakeFunc(buildPtrType, func(args []reflect.Value) []reflect.Value {
sp := reflect.New(rt)
sp.Elem().Set(args[0])
return []reflect.Value{sp}
})
unbuildPtrFunc := reflect.MakeFunc(unbuildPtrType, func(args []reflect.Value) []reflect.Value {
return []reflect.Value{args[0].Elem()}
})
return gopter.DeriveGen(
buildPtrFunc.Interface(),
unbuildPtrFunc.Interface(),
Struct(rt, gens),
)
}