From c3aacbd3f21bdf4b60454378dac23d966716aa1c Mon Sep 17 00:00:00 2001 From: xaspy <20kolpakov01@gmail.com> Date: Wed, 2 Feb 2022 23:11:45 +0500 Subject: [PATCH] #float_boundaries: * added ability to set boundaries for float32 and float64 types * rename old methods which supports only integers to more explicit * update tests * delete test which checks float to unsupported struct to boundaries --- example_with_tags_lenbounds_test.go | 5 ++ faker.go | 76 ++++++++++++++++++++++++----- faker_test.go | 51 +++++++++++-------- 3 files changed, 99 insertions(+), 33 deletions(-) diff --git a/example_with_tags_lenbounds_test.go b/example_with_tags_lenbounds_test.go index 8ada652..2b71d53 100644 --- a/example_with_tags_lenbounds_test.go +++ b/example_with_tags_lenbounds_test.go @@ -22,6 +22,9 @@ func Example_withTagsLengthAndBoundary() { UInt32 uint32 `faker:"boundary_start=0, boundary_end=40"` UInt64 uint64 `faker:"boundary_start=14, boundary_end=50"` + Float32 float32 `faker:"boundary_start=12.65, boundary_end=184.05"` + Float64 float64 `faker:"boundary_start=1.256, boundary_end=3.4"` + ASString []string `faker:"len=50"` SString string `faker:"len=25"` MSString map[string]string `faker:"len=30"` @@ -45,6 +48,8 @@ func Example_withTagsLengthAndBoundary() { UInt16:1797 UInt32:8 UInt64:34 + Float32:60.999058 + Float64:2.590148738554016 ASString:[ geHYIpEoQhQdijFooVEAOyvtTwJOofbQPJdbHvEEdjueZaKIgI WVJBBtmrrVccyIydAiLSkMwWbFzFMEotEXsyUXqcmBTVORlkJK diff --git a/faker.go b/faker.go index d237a61..07e3856 100644 --- a/faker.go +++ b/faker.go @@ -25,8 +25,10 @@ var ( testRandZero = false //Sets the default number of string when it is created randomly. randomStringLen = 25 - //Sets the boundary for random value generation. Boundaries can not exceed integer(4 byte...) - nBoundary = numberBoundary{start: 0, end: 100} + //Sets the boundary for random integer value generation. Boundaries can not exceed integer(4 byte...) + iBoundary = intBoundary{start: 0, end: 100} + //Sets the boundary for random float value generation. Boundaries should comply with float values constraints (IEEE 754) + fBoundary = floatBoundary{start: 0, end: 100} //Sets the random max size for slices and maps. randomMaxSize = 100 //Sets the random min size for slices and maps. @@ -43,11 +45,16 @@ var ( maxGenerateStringRetries = 1000000 ) -type numberBoundary struct { +type intBoundary struct { start int end int } +type floatBoundary struct { + start float64 + end float64 +} + type langRuneBoundary struct { start rune end rune @@ -364,7 +371,7 @@ func SetRandomNumberBoundaries(start, end int) error { if start > end { return errors.New(ErrStartValueBiggerThanEnd) } - nBoundary = numberBoundary{start: start, end: end} + iBoundary = intBoundary{start: start, end: end} return nil } @@ -594,9 +601,9 @@ func getValue(a interface{}) (reflect.Value, error) { case reflect.Int64: return reflect.ValueOf(int64(randomInteger())), nil case reflect.Float32: - return reflect.ValueOf(rand.Float32()), nil + return reflect.ValueOf(float32(randomFloat())), nil case reflect.Float64: - return reflect.ValueOf(rand.Float64()), nil + return reflect.ValueOf(randomFloat()), nil case reflect.Bool: val := rand.Intn(2) > 0 return reflect.ValueOf(val), nil @@ -797,7 +804,7 @@ func userDefinedMap(v reflect.Value, tag string) error { func getValueWithTag(t reflect.Type, tag string) (interface{}, error) { switch t.Kind() { case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Uint, reflect.Uint8, - reflect.Uint16, reflect.Uint32, reflect.Uint64: + reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: res, err := extractNumberFromTag(tag, t) if err != nil { return nil, err @@ -1091,15 +1098,35 @@ func extractNumberFromTag(tag string, t reflect.Type) (interface{}, error) { if len(valuesStr) != 2 { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) } - startBoundary, err := extractNumberFromText(valuesStr[0]) + + // TODO(Xaspy): When Golang provides generics, we will be able to make this method simpler and more beautiful. + if t.Kind() == reflect.Float64 || t.Kind() == reflect.Float32 { + startBoundary, err := extractFloatFromText(valuesStr[0]) + if err != nil { + return nil, err + } + endBoundary, err := extractFloatFromText(valuesStr[1]) + if err != nil { + return nil, err + } + boundary := floatBoundary{start: startBoundary, end: endBoundary} + switch t.Kind() { + case reflect.Float32: + return float32(randomFloatWithBoundary(boundary)), nil + case reflect.Float64: + return randomFloatWithBoundary(boundary), nil + } + } + + startBoundary, err := extractIntFromText(valuesStr[0]) if err != nil { return nil, err } - endBoundary, err := extractNumberFromText(valuesStr[1]) + endBoundary, err := extractIntFromText(valuesStr[1]) if err != nil { return nil, err } - boundary := numberBoundary{start: startBoundary, end: endBoundary} + boundary := intBoundary{start: startBoundary, end: endBoundary} switch t.Kind() { case reflect.Uint: return uint(randomIntegerWithBoundary(boundary)), nil @@ -1126,7 +1153,7 @@ func extractNumberFromTag(tag string, t reflect.Type) (interface{}, error) { } } -func extractNumberFromText(text string) (int, error) { +func extractIntFromText(text string) (int, error) { text = strings.TrimSpace(text) texts := strings.SplitN(text, Equals, -1) if len(texts) != 2 { @@ -1135,6 +1162,15 @@ func extractNumberFromText(text string) (int, error) { return strconv.Atoi(texts[1]) } +func extractFloatFromText(text string) (float64, error) { + text = strings.TrimSpace(text) + texts := strings.SplitN(text, Equals, -1) + if len(texts) != 2 { + return 0, fmt.Errorf(ErrWrongFormattedTag, text) + } + return strconv.ParseFloat(texts[1], 64) +} + func fetchOneOfArgsFromTag(tag string) ([]string, error) { items := strings.Split(tag, colon) argsList := items[1:] @@ -1185,7 +1221,7 @@ func randomString(n int, lang *langRuneBoundary) (string, error) { } // randomIntegerWithBoundary returns a random integer between input start and end boundary. [start, end) -func randomIntegerWithBoundary(boundary numberBoundary) int { +func randomIntegerWithBoundary(boundary intBoundary) int { span := boundary.end - boundary.start if span <= 0 { return boundary.start @@ -1193,9 +1229,23 @@ func randomIntegerWithBoundary(boundary numberBoundary) int { return rand.Intn(span) + boundary.start } +// randomFloatWithBoundary returns a random float between input start and end boundary. [start, end) +func randomFloatWithBoundary(boundary floatBoundary) float64 { + span := boundary.end - boundary.start + if span <= 0 { + return boundary.start + } + return boundary.start + rand.Float64()*span +} + // randomInteger returns a random integer between start and end boundary. [start, end) func randomInteger() int { - return randomIntegerWithBoundary(nBoundary) + return randomIntegerWithBoundary(iBoundary) +} + +// randomFloat returns a random float between start and end boundary. [start, end) +func randomFloat() float64 { + return randomFloatWithBoundary(fBoundary) } // randomSliceAndMapSize returns a random integer between [0,randomSliceAndMapSize). If the testRandZero is set, returns 0 diff --git a/faker_test.go b/faker_test.go index eef3064..49efe48 100644 --- a/faker_test.go +++ b/faker_test.go @@ -130,6 +130,9 @@ type SomeStructWithLen struct { UInt32 uint32 `faker:"boundary_start=5, boundary_end=10"` UInt64 uint64 `faker:"boundary_start=5, boundary_end=10"` + Float32 float32 `faker:"boundary_start=5, boundary_end=10"` + Float64 float64 `faker:"boundary_start=5, boundary_end=10"` + ASString []string `faker:"len=2"` SString string `faker:"len=2"` MSString map[string]string `faker:"len=2"` @@ -482,7 +485,7 @@ func TestSetRandomNumberBoundaries(t *testing.T) { if err := SetRandomNumberBoundaries(10, 0); err == nil { t.Error("Start must be smaller than end value") } - boundary := numberBoundary{start: 10, end: 90} + boundary := intBoundary{start: 10, end: 90} if err := SetRandomNumberBoundaries(boundary.start, boundary.end); err != nil { t.Error("SetRandomNumberBoundaries method is corrupted.") } @@ -548,34 +551,40 @@ func TestBoundaryAndLen(t *testing.T) { if err := FakeData(&someStruct); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.Int8)); err != nil { + if err := validateIntRange(int(someStruct.Int8)); err != nil { + t.Error(err) + } + if err := validateIntRange(int(someStruct.Int16)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.Int16)); err != nil { + if err := validateIntRange(int(someStruct.Int32)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.Int32)); err != nil { + if err := validateIntRange(someStruct.Inta); err != nil { t.Error(err) } - if err := validateRange(someStruct.Inta); err != nil { + if err := validateIntRange(int(someStruct.Int64)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.Int64)); err != nil { + if err := validateIntRange(int(someStruct.UInt8)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.UInt8)); err != nil { + if err := validateIntRange(int(someStruct.UInt16)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.UInt16)); err != nil { + if err := validateIntRange(int(someStruct.UInt32)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.UInt32)); err != nil { + if err := validateIntRange(int(someStruct.UInta)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.UInta)); err != nil { + if err := validateIntRange(int(someStruct.UInt64)); err != nil { t.Error(err) } - if err := validateRange(int(someStruct.UInt64)); err != nil { + if err := validateFloatRange(float64(someStruct.Float32)); err != nil { + t.Error(err) + } + if err := validateFloatRange(someStruct.Float64); err != nil { t.Error(err) } if err := validateLen(someStruct.SString); err != nil { @@ -595,10 +604,10 @@ func TestBoundaryAndLen(t *testing.T) { } } for k, v := range someStruct.MIint { - if err := validateRange(k); err != nil { + if err := validateIntRange(k); err != nil { t.Error(err) } - if err := validateRange(v); err != nil { + if err := validateIntRange(v); err != nil { t.Error(err) } } @@ -807,12 +816,6 @@ func isStringLangCorrect(value string, lang langRuneBoundary) error { } func TestExtractNumberFromTagFail(t *testing.T) { - notSupportedTypeStruct := &struct { - Test float32 `faker:"boundary_start=5, boundary_end=10"` - }{} - if err := FakeData(¬SupportedTypeStruct); err == nil { - t.Error(err) - } notSupportedStruct := &struct { Test int `faker:"boundary_start=5"` }{} @@ -861,7 +864,7 @@ func validateLen(value string) error { return nil } -func validateRange(value int) error { +func validateIntRange(value int) error { if value < someStructBoundaryStart || value > someStructBoundaryEnd { return fmt.Errorf("%d must be between %d and %d", value, someStructBoundaryStart, someStructBoundaryEnd) @@ -869,6 +872,14 @@ func validateRange(value int) error { return nil } +func validateFloatRange(value float64) error { + if value < someStructBoundaryStart || value > someStructBoundaryEnd { + return fmt.Errorf("%f must be between %d and %d", value, someStructBoundaryStart, + someStructBoundaryEnd) + } + return nil +} + func TestSetDataWithTagIfFirstArgumentNotPtr(t *testing.T) { temp := struct{}{} if setDataWithTag(reflect.ValueOf(temp), "").Error() != "Not a pointer value" {