github.com/leanovate/gopter@v0.2.9/derived_gen.go (about)

     1  package gopter
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  )
     7  
     8  type derivedGen struct {
     9  	biMapper   *BiMapper
    10  	upGens     []Gen
    11  	resultType reflect.Type
    12  }
    13  
    14  func (d *derivedGen) Generate(genParams *GenParameters) *GenResult {
    15  	labels := []string{}
    16  	up := make([]interface{}, len(d.upGens))
    17  	shrinkers := make([]Shrinker, len(d.upGens))
    18  	sieves := make([]func(v interface{}) bool, len(d.upGens))
    19  
    20  	var ok bool
    21  	for i, gen := range d.upGens {
    22  		result := gen(genParams)
    23  		labels = append(labels, result.Labels...)
    24  		shrinkers[i] = result.Shrinker
    25  		sieves[i] = result.Sieve
    26  		up[i], ok = result.Retrieve()
    27  		if !ok {
    28  			return &GenResult{
    29  				Shrinker:   d.Shrinker(result.Shrinker),
    30  				Result:     nil,
    31  				Labels:     result.Labels,
    32  				ResultType: d.resultType,
    33  				Sieve:      d.Sieve(sieves...),
    34  			}
    35  		}
    36  	}
    37  	down := d.biMapper.ConvertDown(up)
    38  	if len(down) == 1 {
    39  		return &GenResult{
    40  			Shrinker:   d.Shrinker(CombineShrinker(shrinkers...)),
    41  			Result:     down[0],
    42  			Labels:     labels,
    43  			ResultType: reflect.TypeOf(down[0]),
    44  			Sieve:      d.Sieve(sieves...),
    45  		}
    46  	}
    47  	return &GenResult{
    48  		Shrinker:   d.Shrinker(CombineShrinker(shrinkers...)),
    49  		Result:     down,
    50  		Labels:     labels,
    51  		ResultType: reflect.TypeOf(down),
    52  		Sieve:      d.Sieve(sieves...),
    53  	}
    54  }
    55  
    56  func (d *derivedGen) Sieve(baseSieve ...func(interface{}) bool) func(interface{}) bool {
    57  	return func(down interface{}) bool {
    58  		if down == nil {
    59  			return false
    60  		}
    61  		downs, ok := down.([]interface{})
    62  		if !ok {
    63  			downs = []interface{}{down}
    64  		}
    65  		ups := d.biMapper.ConvertUp(downs)
    66  		for i, up := range ups {
    67  			if baseSieve[i] != nil && !baseSieve[i](up) {
    68  				return false
    69  			}
    70  		}
    71  		return true
    72  	}
    73  }
    74  
    75  func (d *derivedGen) Shrinker(baseShrinker Shrinker) func(down interface{}) Shrink {
    76  	return func(down interface{}) Shrink {
    77  		downs, ok := down.([]interface{})
    78  		if !ok {
    79  			downs = []interface{}{down}
    80  		}
    81  		ups := d.biMapper.ConvertUp(downs)
    82  		upShrink := baseShrinker(ups)
    83  
    84  		return upShrink.Map(func(shrunkUps []interface{}) interface{} {
    85  			downs := d.biMapper.ConvertDown(shrunkUps)
    86  			if len(downs) == 1 {
    87  				return downs[0]
    88  			}
    89  			return downs
    90  		})
    91  	}
    92  }
    93  
    94  // DeriveGen derives a generator with shrinkers from a sequence of other
    95  // generators mapped by a bijective function (BiMapper)
    96  func DeriveGen(downstream interface{}, upstream interface{}, gens ...Gen) Gen {
    97  	biMapper := NewBiMapper(downstream, upstream)
    98  
    99  	if len(gens) != len(biMapper.UpTypes) {
   100  		panic(fmt.Sprintf("Expected %d generators != %d", len(biMapper.UpTypes), len(gens)))
   101  	}
   102  
   103  	resultType := reflect.TypeOf([]interface{}{})
   104  	if len(biMapper.DownTypes) == 1 {
   105  		resultType = biMapper.DownTypes[0]
   106  	}
   107  
   108  	derived := &derivedGen{
   109  		biMapper:   biMapper,
   110  		upGens:     gens,
   111  		resultType: resultType,
   112  	}
   113  	return derived.Generate
   114  }