github.com/leanovate/gopter@v0.2.9/gen/map_shrink.go (about) 1 package gen 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/leanovate/gopter" 8 ) 9 10 type mapShrinkOne struct { 11 original reflect.Value 12 key reflect.Value 13 keyShrink gopter.Shrink 14 elementShrink gopter.Shrink 15 state bool 16 keyExhausted bool 17 lastKey interface{} 18 elementExhausted bool 19 lastElement interface{} 20 } 21 22 func (s *mapShrinkOne) nextKeyValue() (interface{}, interface{}, bool) { 23 for !s.keyExhausted && !s.elementExhausted { 24 s.state = !s.state 25 if s.state && !s.keyExhausted { 26 value, ok := s.keyShrink() 27 if ok { 28 s.lastKey = value 29 return s.lastKey, s.lastElement, true 30 } 31 s.keyExhausted = true 32 } else if !s.state && !s.elementExhausted { 33 value, ok := s.elementShrink() 34 if ok { 35 s.lastElement = value 36 return s.lastKey, s.lastElement, true 37 } 38 s.elementExhausted = true 39 } 40 } 41 return nil, nil, false 42 } 43 44 func (s *mapShrinkOne) Next() (interface{}, bool) { 45 nextKey, nextValue, ok := s.nextKeyValue() 46 if !ok { 47 return nil, false 48 } 49 result := reflect.MakeMapWithSize(s.original.Type(), s.original.Len()) 50 for _, key := range s.original.MapKeys() { 51 if !reflect.DeepEqual(key.Interface(), s.key.Interface()) { 52 result.SetMapIndex(key, s.original.MapIndex(key)) 53 } 54 } 55 result.SetMapIndex(reflect.ValueOf(nextKey), reflect.ValueOf(nextValue)) 56 57 return result.Interface(), true 58 } 59 60 // MapShrinkerOne creates a map shrinker from a shrinker for the key values of a map. 61 // The length of the map will remain (mostly) unchanged, instead each key value pair is 62 // shrunk after the other. 63 func MapShrinkerOne(keyShrinker, elementShrinker gopter.Shrinker) gopter.Shrinker { 64 return func(v interface{}) gopter.Shrink { 65 rv := reflect.ValueOf(v) 66 if rv.Kind() != reflect.Map { 67 panic(fmt.Sprintf("%#v is not a map", v)) 68 } 69 70 keys := rv.MapKeys() 71 shrinks := make([]gopter.Shrink, 0, len(keys)) 72 for _, key := range keys { 73 mapShrinkOne := &mapShrinkOne{ 74 original: rv, 75 key: key, 76 keyShrink: keyShrinker(key.Interface()), 77 lastKey: key.Interface(), 78 elementShrink: elementShrinker(rv.MapIndex(key).Interface()), 79 lastElement: rv.MapIndex(key).Interface(), 80 } 81 shrinks = append(shrinks, mapShrinkOne.Next) 82 } 83 return gopter.ConcatShrinks(shrinks...) 84 } 85 } 86 87 type mapShrink struct { 88 original reflect.Value 89 originalKeys []reflect.Value 90 length int 91 offset int 92 chunkLength int 93 } 94 95 func (s *mapShrink) Next() (interface{}, bool) { 96 if s.chunkLength == 0 { 97 return nil, false 98 } 99 keys := make([]reflect.Value, 0, s.length-s.chunkLength) 100 keys = append(keys, s.originalKeys[0:s.offset]...) 101 s.offset += s.chunkLength 102 if s.offset < s.length { 103 keys = append(keys, s.originalKeys[s.offset:s.length]...) 104 } else { 105 s.offset = 0 106 s.chunkLength >>= 1 107 } 108 109 result := reflect.MakeMapWithSize(s.original.Type(), len(keys)) 110 for _, key := range keys { 111 result.SetMapIndex(key, s.original.MapIndex(key)) 112 } 113 114 return result.Interface(), true 115 } 116 117 // MapShrinker creates a map shrinker from shrinker for the key values. 118 // The length of the map will be shrunk as well 119 func MapShrinker(keyShrinker, elementShrinker gopter.Shrinker) gopter.Shrinker { 120 return func(v interface{}) gopter.Shrink { 121 rv := reflect.ValueOf(v) 122 if rv.Kind() != reflect.Map { 123 panic(fmt.Sprintf("%#v is not a Map", v)) 124 } 125 keys := rv.MapKeys() 126 mapShrink := &mapShrink{ 127 original: rv, 128 originalKeys: keys, 129 offset: 0, 130 length: rv.Len(), 131 chunkLength: rv.Len() >> 1, 132 } 133 134 shrinks := make([]gopter.Shrink, 0, rv.Len()+1) 135 shrinks = append(shrinks, mapShrink.Next) 136 for _, key := range keys { 137 mapShrinkOne := &mapShrinkOne{ 138 original: rv, 139 key: key, 140 keyShrink: keyShrinker(key.Interface()), 141 lastKey: key.Interface(), 142 elementShrink: elementShrinker(rv.MapIndex(key).Interface()), 143 lastElement: rv.MapIndex(key).Interface(), 144 } 145 shrinks = append(shrinks, mapShrinkOne.Next) 146 } 147 return gopter.ConcatShrinks(shrinks...) 148 } 149 }