/
slice_shrink.go
105 lines (93 loc) · 2.72 KB
/
slice_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
package gen
import (
"fmt"
"reflect"
"github.com/leanovate/gopter"
)
type sliceShrinkOne struct {
original reflect.Value
index int
elementShrink gopter.Shrink
}
func (s *sliceShrinkOne) Next() (interface{}, bool) {
value, ok := s.elementShrink()
if !ok {
return nil, false
}
result := reflect.MakeSlice(s.original.Type(), s.original.Len(), s.original.Len())
reflect.Copy(result, s.original)
if value == nil {
result.Index(s.index).Set(reflect.Zero(s.original.Type().Elem()))
} else {
result.Index(s.index).Set(reflect.ValueOf(value))
}
return result.Interface(), true
}
// SliceShrinkerOne creates a slice shrinker from a shrinker for the elements of the slice.
// The length of the slice will remains unchanged, instead each element is shrunk after the
// other.
func SliceShrinkerOne(elementShrinker gopter.Shrinker) gopter.Shrinker {
return func(v interface{}) gopter.Shrink {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Slice {
panic(fmt.Sprintf("%#v is not a slice", v))
}
shrinks := make([]gopter.Shrink, 0, rv.Len())
for i := 0; i < rv.Len(); i++ {
sliceShrinkOne := &sliceShrinkOne{
original: rv,
index: i,
elementShrink: elementShrinker(rv.Index(i).Interface()),
}
shrinks = append(shrinks, sliceShrinkOne.Next)
}
return gopter.ConcatShrinks(shrinks...)
}
}
type sliceShrink struct {
original reflect.Value
length int
offset int
chunkLength int
}
func (s *sliceShrink) Next() (interface{}, bool) {
if s.chunkLength == 0 {
return nil, false
}
value := reflect.AppendSlice(reflect.MakeSlice(s.original.Type(), 0, s.length-s.chunkLength), s.original.Slice(0, s.offset))
s.offset += s.chunkLength
if s.offset < s.length {
value = reflect.AppendSlice(value, s.original.Slice(s.offset, s.length))
} else {
s.offset = 0
s.chunkLength >>= 1
}
return value.Interface(), true
}
// SliceShrinker creates a slice shrinker from a shrinker for the elements of the slice.
// The length of the slice will be shrunk as well
func SliceShrinker(elementShrinker gopter.Shrinker) gopter.Shrinker {
return func(v interface{}) gopter.Shrink {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Slice {
panic(fmt.Sprintf("%#v is not a slice", v))
}
sliceShrink := &sliceShrink{
original: rv,
offset: 0,
length: rv.Len(),
chunkLength: rv.Len() >> 1,
}
shrinks := make([]gopter.Shrink, 0, rv.Len()+1)
shrinks = append(shrinks, sliceShrink.Next)
for i := 0; i < rv.Len(); i++ {
sliceShrinkOne := &sliceShrinkOne{
original: rv,
index: i,
elementShrink: elementShrinker(rv.Index(i).Interface()),
}
shrinks = append(shrinks, sliceShrinkOne.Next)
}
return gopter.ConcatShrinks(shrinks...)
}
}