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 }