From 72649f1c9ae1b49532065352501b3f885b40e06a Mon Sep 17 00:00:00 2001 From: Alex Pana <8968914+acpana@users.noreply.github.com> Date: Wed, 11 Dec 2024 01:17:51 +0000 Subject: [PATCH] refactor:tests: add overrides Signed-off-by: Alex Pana <8968914+acpana@users.noreply.github.com> --- pkg/test/fuzz/fuzz_test.go | 101 +++++++++++++++++++++++-------------- pkg/test/fuzz/krmgen.go | 62 +++++++++++------------ 2 files changed, 93 insertions(+), 70 deletions(-) diff --git a/pkg/test/fuzz/fuzz_test.go b/pkg/test/fuzz/fuzz_test.go index 9cd68b4843..76959e0707 100644 --- a/pkg/test/fuzz/fuzz_test.go +++ b/pkg/test/fuzz/fuzz_test.go @@ -16,6 +16,9 @@ package fuzz import ( "encoding/json" + "math/rand" + "reflect" + "strconv" "testing" ) @@ -38,14 +41,17 @@ type TestKRMType struct { ComplexMapFieldPtr *map[string]NestedKRMType NestedStruct NestedKRMType NestedStructPtr *NestedKRMType - EnumField MyEnumType - EnumFieldPtr *MyEnumType - StringEnumField MyStringEnumType - StringEnumFieldPtr *MyStringEnumType - StructSlice []NestedKRMType - StructSlicePtr *[]NestedKRMType - IntSlice []int - IntSlicePtr *[]int + // EnumField MyEnumType + // EnumFieldPtr *MyEnumType + // StringEnumField MyStringEnumType + // StringEnumFieldPtr *MyStringEnumType + StructSlice []NestedKRMType + StructSlicePtr *[]NestedKRMType + IntSlice []int + IntSlicePtr *[]int + + PtrStringFieldThatNeedsInt *string + StringFieldThatNeedsInt string } // Nested structs @@ -66,35 +72,36 @@ type DeepNestedKRMType struct { FieldCPtr *[]byte } -// Integer enum type -type MyEnumType int +// // Integer enum type +// type MyEnumType int -const ( - EnumValueA MyEnumType = iota - EnumValueB - EnumValueC -) +// const ( +// EnumValueA MyEnumType = iota +// EnumValueB +// EnumValueC +// ) -// String enum type -type MyStringEnumType string +// // String enum type +// type MyStringEnumType string -const ( - StringEnumOptionA MyStringEnumType = "OptionA" - StringEnumOptionB MyStringEnumType = "OptionB" - StringEnumOptionC MyStringEnumType = "OptionC" -) +// const ( +// StringEnumOptionA MyStringEnumType = "OptionA" +// StringEnumOptionB MyStringEnumType = "OptionB" +// StringEnumOptionC MyStringEnumType = "OptionC" +// ) func TestRandomFillerFields(t *testing.T) { - // Define allowable bounds for integer enums and specific values for non-integer enums - intEnumAllowableValues := map[string]int64{ - "MyEnumType": 2, // Allows values in the range [0, 2] - } - stringEnumAllowableValues := map[string][]interface{}{ - "MyStringEnumType": {"OptionA", "OptionB", "OptionC"}, + stream := rand.New(rand.NewSource(int64(9201995))) // for determinism + + funcF := func(t *testing.T, fieldName string, field reflect.Value) { + field.SetString(strconv.FormatInt(stream.Int63(), 10)) } - seed := int64(9201995) // for determinism - filler := NewRandomFiller(seed, intEnumAllowableValues, stringEnumAllowableValues) + overrides := map[string]OverrideFiller{ + ".PtrStringFieldThatNeedsInt": funcF, + ".StringFieldThatNeedsInt": funcF, + } + filler := NewRandomFiller(&FillerConfig{Stream: stream, FieldOverrides: overrides}) tests := []struct { name string @@ -118,14 +125,14 @@ func TestRandomFillerFields(t *testing.T) { {"ComplexMapFieldPtr", func(krmObj *TestKRMType) bool { return krmObj.ComplexMapFieldPtr != nil && len(*krmObj.ComplexMapFieldPtr) > 0 }}, - {"EnumField", func(krmObj *TestKRMType) bool { return krmObj.EnumField >= 0 && krmObj.EnumField <= 2 }}, - {"EnumFieldPtr", func(krmObj *TestKRMType) bool { - return krmObj.EnumFieldPtr != nil && *krmObj.EnumFieldPtr >= 0 && *krmObj.EnumFieldPtr <= 2 - }}, - {"StringEnumField", func(krmObj *TestKRMType) bool { return krmObj.StringEnumField != "" }}, - {"StringEnumFieldPtr", func(krmObj *TestKRMType) bool { - return krmObj.StringEnumFieldPtr != nil && *krmObj.StringEnumFieldPtr != "" - }}, + // {"EnumField", func(krmObj *TestKRMType) bool { return krmObj.EnumField >= 0 && krmObj.EnumField <= 2 }}, + // {"EnumFieldPtr", func(krmObj *TestKRMType) bool { + // return krmObj.EnumFieldPtr != nil && *krmObj.EnumFieldPtr >= 0 && *krmObj.EnumFieldPtr <= 2 + // }}, + // {"StringEnumField", func(krmObj *TestKRMType) bool { return krmObj.StringEnumField != "" }}, + // {"StringEnumFieldPtr", func(krmObj *TestKRMType) bool { + // return krmObj.StringEnumFieldPtr != nil && *krmObj.StringEnumFieldPtr != "" + // }}, {"StructSlice", func(krmObj *TestKRMType) bool { return len(krmObj.StructSlice) > 0 }}, {"StructSlicePtr", func(krmObj *TestKRMType) bool { return krmObj.StructSlicePtr != nil && len(*krmObj.StructSlicePtr) > 0 }}, {"IntSlice", func(krmObj *TestKRMType) bool { return len(krmObj.IntSlice) > 0 }}, @@ -154,6 +161,24 @@ func TestRandomFillerFields(t *testing.T) { {"NestedStruct.DeepNestedField.FieldCPtr", func(krmObj *TestKRMType) bool { return krmObj.NestedStruct.DeepNestedField != nil && krmObj.NestedStruct.DeepNestedField.FieldCPtr != nil && len(*krmObj.NestedStruct.DeepNestedField.FieldCPtr) > 0 }}, + {"PtrStringFieldThatNeedsInt", func(krmObj *TestKRMType) bool { + if krmObj.PtrStringFieldThatNeedsInt == nil { + return false + } + if _, err := strconv.ParseInt(*krmObj.PtrStringFieldThatNeedsInt, 10, 64); err != nil { + t.Error("converting string field to int") + } + return true + }}, + {"StringFieldThatNeedsInt", func(krmObj *TestKRMType) bool { + if krmObj.PtrStringFieldThatNeedsInt == nil { + return false + } + if _, err := strconv.ParseInt(*krmObj.PtrStringFieldThatNeedsInt, 10, 64); err != nil { + t.Error("converting string field to int") + } + return true + }}, } krmObj := &TestKRMType{} diff --git a/pkg/test/fuzz/krmgen.go b/pkg/test/fuzz/krmgen.go index 5f6e569f6f..e12c49413f 100644 --- a/pkg/test/fuzz/krmgen.go +++ b/pkg/test/fuzz/krmgen.go @@ -20,51 +20,54 @@ import ( "testing" ) +type OverrideFiller func(t *testing.T, fieldName string, field reflect.Value) type RandomFiller struct { randStream *rand.Rand - // for iota based enums, defines the upper bound for a named enum type - intEnumAllowableValues map[string]int64 - // for non iota based enums, holds the set of allowable values for a named enum type - stringEnumAllowableValues map[string][]interface{} + fieldOverrides map[string]OverrideFiller } -func NewRandomFiller(seed int64, enumBoundsMap map[string]int64, enumValuesMap map[string][]interface{}) *RandomFiller { +type FillerConfig struct { + Stream *rand.Rand + FieldOverrides map[string]OverrideFiller +} + +func NewRandomFiller(fc *FillerConfig) *RandomFiller { return &RandomFiller{ - randStream: rand.New(rand.NewSource(seed)), - intEnumAllowableValues: enumBoundsMap, - stringEnumAllowableValues: enumValuesMap, + randStream: fc.Stream, + fieldOverrides: fc.FieldOverrides, } } // Fill populates the fields of a struct with random values. Enums are handled separately in the // two maps passed to the RandomFiller. func (rf *RandomFiller) Fill(t *testing.T, obj interface{}) { - rf.fillWithRandom(t, reflect.ValueOf(obj).Elem()) + rf.fillWithRandom(t, "", reflect.ValueOf(obj).Elem()) + rf.fillWithRandom(t, "", reflect.ValueOf(obj).Elem()) } -func (rf *RandomFiller) fillWithRandom(t *testing.T, field reflect.Value) { +func (rf *RandomFiller) fillWithRandom(t *testing.T, fieldName string, field reflect.Value) { if field.Kind() == reflect.Ptr { if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } - rf.fillWithRandom(t, field.Elem()) + rf.fillWithRandom(t, fieldName, field.Elem()) return } + if rf.fieldOverrides != nil { + if override, ok := rf.fieldOverrides[fieldName]; ok { + override(t, fieldName, field) + return + } + } + switch field.Kind() { case reflect.Bool: field.SetBool(rf.randStream.Intn(2) == 1) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - // if this field is an iota enum field with a set of allowable values - if upperBound, ok := rf.intEnumAllowableValues[field.Type().Name()]; ok { - // Select a random integer value within the range [0, upperBound] for integer enums - field.SetInt(rf.randStream.Int63n(upperBound + 1)) - } else { - // Otherwise, fill with a generic random integer - field.SetInt(rf.randStream.Int63()) - } + field.SetInt(rf.randStream.Int63()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: field.SetUint(rf.randStream.Uint64()) @@ -73,22 +76,14 @@ func (rf *RandomFiller) fillWithRandom(t *testing.T, field reflect.Value) { field.SetFloat(rf.randStream.Float64()) case reflect.String: - // if this field is a enum field with a set of allowable values - if values, ok := rf.stringEnumAllowableValues[field.Type().Name()]; ok { - // Select a random string from predefined values in enumValuesMap - selectedValue := values[rf.randStream.Intn(len(values))] - field.SetString(selectedValue.(string)) - } else { - // Otherwise, fill with a generic random string - field.SetString(randomString(rf.randStream)) - } + field.SetString(randomString(rf.randStream)) case reflect.Slice: count := rf.randStream.Intn(10) + 1 slice := reflect.MakeSlice(field.Type(), count, count) for j := 0; j < count; j++ { element := reflect.New(field.Type().Elem()).Elem() - rf.fillWithRandom(t, element) + rf.fillWithRandom(t, "", element) // don't need to pass in a field name for slice elements slice.Index(j).Set(element) } field.Set(slice) @@ -99,15 +94,18 @@ func (rf *RandomFiller) fillWithRandom(t *testing.T, field reflect.Value) { for j := 0; j < count; j++ { key := reflect.New(field.Type().Key()).Elem() value := reflect.New(field.Type().Elem()).Elem() - rf.fillWithRandom(t, key) - rf.fillWithRandom(t, value) + rf.fillWithRandom(t, "", key) // no need to pass in a field name for keys + rf.fillWithRandom(t, "", value) // no need to pass in a field name for values mapType.SetMapIndex(key, value) } field.Set(mapType) case reflect.Struct: for i := 0; i < field.NumField(); i++ { - rf.fillWithRandom(t, field.Field(i)) + structFieldName := field.Type().Field(i).Name + nestedStructFieldname := fieldName + "." + structFieldName + + rf.fillWithRandom(t, nestedStructFieldname, field.Field(i)) } default: