Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Float boundaries #156

Merged
merged 2 commits into from Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions example_with_tags_lenbounds_test.go
Expand Up @@ -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"`
Expand All @@ -45,6 +48,8 @@ func Example_withTagsLengthAndBoundary() {
UInt16:1797
UInt32:8
UInt64:34
Float32:60.999058
Float64:2.590148738554016
ASString:[
geHYIpEoQhQdijFooVEAOyvtTwJOofbQPJdbHvEEdjueZaKIgI
WVJBBtmrrVccyIydAiLSkMwWbFzFMEotEXsyUXqcmBTVORlkJK
Expand Down
76 changes: 63 additions & 13 deletions faker.go
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -366,7 +373,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
}

Expand Down Expand Up @@ -596,9 +603,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
Expand Down Expand Up @@ -799,7 +806,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
Expand Down Expand Up @@ -1095,15 +1102,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
Expand All @@ -1130,7 +1157,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 {
Expand All @@ -1139,6 +1166,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:]
Expand Down Expand Up @@ -1189,17 +1225,31 @@ 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
}
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
Expand Down
51 changes: 31 additions & 20 deletions faker_test.go
Expand Up @@ -131,6 +131,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"`
Expand Down Expand Up @@ -485,7 +488,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.")
}
Expand Down Expand Up @@ -551,34 +554,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 {
Expand All @@ -598,10 +607,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)
}
}
Expand Down Expand Up @@ -823,12 +832,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(&notSupportedTypeStruct); err == nil {
t.Error(err)
}
notSupportedStruct := &struct {
Test int `faker:"boundary_start=5"`
}{}
Expand Down Expand Up @@ -877,14 +880,22 @@ 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)
}
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" {
Expand Down