-
Notifications
You must be signed in to change notification settings - Fork 40
/
derived_gen.go
114 lines (103 loc) · 2.7 KB
/
derived_gen.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package gopter
import (
"fmt"
"reflect"
)
type derivedGen struct {
biMapper *BiMapper
upGens []Gen
resultType reflect.Type
}
func (d *derivedGen) Generate(genParams *GenParameters) *GenResult {
labels := []string{}
up := make([]interface{}, len(d.upGens))
shrinkers := make([]Shrinker, len(d.upGens))
sieves := make([]func(v interface{}) bool, len(d.upGens))
var ok bool
for i, gen := range d.upGens {
result := gen(genParams)
labels = append(labels, result.Labels...)
shrinkers[i] = result.Shrinker
sieves[i] = result.Sieve
up[i], ok = result.Retrieve()
if !ok {
return &GenResult{
Shrinker: d.Shrinker(result.Shrinker),
Result: nil,
Labels: result.Labels,
ResultType: d.resultType,
Sieve: d.Sieve(sieves...),
}
}
}
down := d.biMapper.ConvertDown(up)
if len(down) == 1 {
return &GenResult{
Shrinker: d.Shrinker(CombineShrinker(shrinkers...)),
Result: down[0],
Labels: labels,
ResultType: reflect.TypeOf(down[0]),
Sieve: d.Sieve(sieves...),
}
}
return &GenResult{
Shrinker: d.Shrinker(CombineShrinker(shrinkers...)),
Result: down,
Labels: labels,
ResultType: reflect.TypeOf(down),
Sieve: d.Sieve(sieves...),
}
}
func (d *derivedGen) Sieve(baseSieve ...func(interface{}) bool) func(interface{}) bool {
return func(down interface{}) bool {
if down == nil {
return false
}
downs, ok := down.([]interface{})
if !ok {
downs = []interface{}{down}
}
ups := d.biMapper.ConvertUp(downs)
for i, up := range ups {
if baseSieve[i] != nil && !baseSieve[i](up) {
return false
}
}
return true
}
}
func (d *derivedGen) Shrinker(baseShrinker Shrinker) func(down interface{}) Shrink {
return func(down interface{}) Shrink {
downs, ok := down.([]interface{})
if !ok {
downs = []interface{}{down}
}
ups := d.biMapper.ConvertUp(downs)
upShrink := baseShrinker(ups)
return upShrink.Map(func(shrunkUps []interface{}) interface{} {
downs := d.biMapper.ConvertDown(shrunkUps)
if len(downs) == 1 {
return downs[0]
}
return downs
})
}
}
// DeriveGen derives a generator with shrinkers from a sequence of other
// generators mapped by a bijective function (BiMapper)
func DeriveGen(downstream interface{}, upstream interface{}, gens ...Gen) Gen {
biMapper := NewBiMapper(downstream, upstream)
if len(gens) != len(biMapper.UpTypes) {
panic(fmt.Sprintf("Expected %d generators != %d", len(biMapper.UpTypes), len(gens)))
}
resultType := reflect.TypeOf([]interface{}{})
if len(biMapper.DownTypes) == 1 {
resultType = biMapper.DownTypes[0]
}
derived := &derivedGen{
biMapper: biMapper,
upGens: gens,
resultType: resultType,
}
return derived.Generate
}