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  }