github.com/leanovate/gopter@v0.2.9/prop/forall.go (about) 1 package prop 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/leanovate/gopter" 8 ) 9 10 var typeOfError = reflect.TypeOf((*error)(nil)).Elem() 11 12 /* 13 ForAll creates a property that requires the check condition to be true for all values, if the 14 condition falsiies the generated values will be shrunk. 15 16 "condition" has to be a function with the same number of parameters as the provided 17 generators "gens". The function may return a simple bool (true means that the 18 condition has passed), a string (empty string means that condition has passed), 19 a *PropResult, or one of former combined with an error. 20 */ 21 func ForAll(condition interface{}, gens ...gopter.Gen) gopter.Prop { 22 callCheck, err := checkConditionFunc(condition, len(gens)) 23 if err != nil { 24 return ErrorProp(err) 25 } 26 27 return gopter.SaveProp(func(genParams *gopter.GenParameters) *gopter.PropResult { 28 genResults := make([]*gopter.GenResult, len(gens)) 29 values := make([]reflect.Value, len(gens)) 30 valuesFormated := make([]string, len(gens)) 31 var ok bool 32 for i, gen := range gens { 33 result := gen(genParams) 34 genResults[i] = result 35 values[i], ok = result.RetrieveAsValue() 36 if !ok { 37 return &gopter.PropResult{ 38 Status: gopter.PropUndecided, 39 } 40 } 41 valuesFormated[i] = fmt.Sprintf("%+v", values[i].Interface()) 42 } 43 result := callCheck(values) 44 if result.Success() { 45 for i, genResult := range genResults { 46 result = result.AddArgs(gopter.NewPropArg(genResult, 0, values[i].Interface(), valuesFormated[i], values[i].Interface(), valuesFormated[i])) 47 } 48 } else { 49 for i, genResult := range genResults { 50 nextResult, nextValue := shrinkValue(genParams.MaxShrinkCount, genResult, values[i].Interface(), valuesFormated[i], result, 51 func(v interface{}) *gopter.PropResult { 52 shrunkOne := make([]reflect.Value, len(values)) 53 copy(shrunkOne, values) 54 if v == nil { 55 shrunkOne[i] = reflect.Zero(values[i].Type()) 56 } else { 57 shrunkOne[i] = reflect.ValueOf(v) 58 } 59 return callCheck(shrunkOne) 60 }) 61 result = nextResult 62 if nextValue == nil { 63 values[i] = reflect.Zero(values[i].Type()) 64 } else { 65 values[i] = reflect.ValueOf(nextValue) 66 } 67 } 68 } 69 return result 70 }) 71 } 72 73 // ForAll1 legacy interface to be removed in the future 74 func ForAll1(gen gopter.Gen, check func(v interface{}) (interface{}, error)) gopter.Prop { 75 checkFunc := func(v interface{}) *gopter.PropResult { 76 return convertResult(check(v)) 77 } 78 return gopter.SaveProp(func(genParams *gopter.GenParameters) *gopter.PropResult { 79 genResult := gen(genParams) 80 value, ok := genResult.Retrieve() 81 if !ok { 82 return &gopter.PropResult{ 83 Status: gopter.PropUndecided, 84 } 85 } 86 valueFormated := fmt.Sprintf("%+v", value) 87 result := checkFunc(value) 88 if result.Success() { 89 return result.AddArgs(gopter.NewPropArg(genResult, 0, value, valueFormated, value, valueFormated)) 90 } 91 92 result, _ = shrinkValue(genParams.MaxShrinkCount, genResult, value, valueFormated, result, checkFunc) 93 return result 94 }) 95 } 96 97 func shrinkValue(maxShrinkCount int, genResult *gopter.GenResult, origValue interface{}, orgiValueFormated string, 98 firstFail *gopter.PropResult, check func(interface{}) *gopter.PropResult) (*gopter.PropResult, interface{}) { 99 lastFail := firstFail 100 lastValue := origValue 101 lastValueFormated := orgiValueFormated 102 103 shrinks := 0 104 shrink := genResult.Shrinker(lastValue).Filter(genResult.Sieve) 105 nextResult, nextValue, nextValueFormated := firstFailure(shrink, check) 106 for nextResult != nil && shrinks < maxShrinkCount { 107 shrinks++ 108 lastValue = nextValue 109 lastValueFormated = nextValueFormated 110 lastFail = nextResult 111 112 shrink = genResult.Shrinker(lastValue).Filter(genResult.Sieve) 113 nextResult, nextValue, nextValueFormated = firstFailure(shrink, check) 114 } 115 116 return lastFail.WithArgs(firstFail.Args).AddArgs(gopter.NewPropArg(genResult, shrinks, lastValue, lastValueFormated, origValue, orgiValueFormated)), lastValue 117 } 118 119 func firstFailure(shrink gopter.Shrink, check func(interface{}) *gopter.PropResult) (*gopter.PropResult, interface{}, string) { 120 value, ok := shrink() 121 for ok { 122 valueFormated := fmt.Sprintf("%+v", value) 123 result := check(value) 124 if !result.Success() { 125 return result, value, valueFormated 126 } 127 value, ok = shrink() 128 } 129 return nil, nil, "" 130 }