forked from leanovate/gopter
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathshrink.go
153 lines (136 loc) · 3.25 KB
/
shrink.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package gopter
// Shrink is a stream of shrinked down values
// Once the result of a shrink is false, it is considered to be exhausted
type Shrink func() (interface{}, bool)
// Filter creates a shrink filtered by a condition
func (s Shrink) Filter(condition func(interface{}) bool) Shrink {
if condition == nil {
return s
}
return func() (interface{}, bool) {
value, ok := s()
for ok && !condition(value) {
value, ok = s()
}
return value, ok
}
}
// Map creates a shrink by a appling a converter to each element of a shrink
func (s Shrink) Map(f func(interface{}) interface{}) Shrink {
return func() (interface{}, bool) {
value, ok := s()
if ok {
return f(value), ok
}
return nil, false
}
}
// All collects all shrinks as a slice. Use with care as this might create
// large results depending on the complexity of the shrink
func (s Shrink) All() []interface{} {
result := make([]interface{}, 0)
value, ok := s()
for ok {
result = append(result, value)
value, ok = s()
}
return result
}
type concatedShrink struct {
index int
shrinks []Shrink
}
func (c *concatedShrink) Next() (interface{}, bool) {
for c.index < len(c.shrinks) {
value, ok := c.shrinks[c.index]()
if ok {
return value, ok
}
c.index++
}
return nil, false
}
// ConcatShrinks concats an array of shrinks to a single shrinks
func ConcatShrinks(shrinks ...Shrink) Shrink {
concated := &concatedShrink{
index: 0,
shrinks: shrinks,
}
return concated.Next
}
type interleaved struct {
first Shrink
second Shrink
firstExhausted bool
secondExhaused bool
state bool
}
func (i *interleaved) Next() (interface{}, bool) {
for !i.firstExhausted || !i.secondExhaused {
i.state = !i.state
if i.state && !i.firstExhausted {
value, ok := i.first()
if ok {
return value, true
}
i.firstExhausted = true
} else if !i.state && !i.secondExhaused {
value, ok := i.second()
if ok {
return value, true
}
i.secondExhaused = true
}
}
return nil, false
}
// Interleave this shrink with another
// Both shrinks are expected to produce the same result
func (s Shrink) Interleave(other Shrink) Shrink {
interleaved := &interleaved{
first: s,
second: other,
}
return interleaved.Next
}
// Shrinker creates a shrink for a given value
type Shrinker func(value interface{}) Shrink
type elementShrink struct {
original []interface{}
index int
elementShrink Shrink
}
func (e *elementShrink) Next() (interface{}, bool) {
element, ok := e.elementShrink()
if !ok {
return nil, false
}
shrinked := make([]interface{}, len(e.original))
copy(shrinked, e.original)
shrinked[e.index] = element
return shrinked, true
}
func CombineShrinker(shrinkers ...Shrinker) Shrinker {
return func(v interface{}) Shrink {
values := v.([]interface{})
shrinks := make([]Shrink, 0, len(values))
for i, shrinker := range shrinkers {
if i >= len(values) {
break
}
shrink := &elementShrink{
original: values,
index: i,
elementShrink: shrinker(values[i]),
}
shrinks = append(shrinks, shrink.Next)
}
return ConcatShrinks(shrinks...)
}
}
var NoShrink = Shrink(func() (interface{}, bool) {
return nil, false
})
var NoShrinker = Shrinker(func(value interface{}) Shrink {
return NoShrink
})