diff --git a/Taskfile.yml b/Taskfile.yml index 7febe83e7b63269468299d999d7aac31118ef44f..3465c6935536db5c5c55b9151bc8537c3288c0fd 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -17,7 +17,7 @@ tasks: desc: Conduct fuzzing tests.# cmds: - echo "Conduct fuzzing tests." - - go test -v -fuzztime=30s -fuzz=Fuzz ./... + - go test -v -fuzztime=10s -fuzz=Fuzz ./... add-licenses: desc: Attach license headers to Go files. diff --git a/import.go b/import.go index 12bd306eb2f45d3d4eecc6a56078bb20f881e831..2a83855717866a14a0ce08fa90fe48d0087fb92f 100644 --- a/import.go +++ b/import.go @@ -94,71 +94,12 @@ func (s *Settings[C]) importStreams() { } } -// // replacePath replaces all pathInterface fields in the struct with the given path -// func replacePath(p string, c any) { -// -// if reflect.TypeOf(c).Kind() != reflect.Ptr { -// panic("c must be a pointer") -// } -// -// if reflect.TypeOf(c).Elem().Kind() != reflect.Struct { -// panic("c must be a pointer to a struct") -// } -// -// fields := reflect.VisibleFields(reflect.TypeOf(c).Elem()) -// for _, field := range fields { -// -// r := reflect.ValueOf(c).Elem().FieldByName(field.Name) -// if field.Type.Kind() == reflect.Struct { -// if r.CanAddr() { -// replacePath(p, r.Addr().Interface()) -// } -// continue -// } -// -// _, ok := r.Interface().(pathInterface) -// if ok { -// -// if r.CanSet() { -// if !path.IsAbs(r.String()) { -// r.SetString(path.Join(p, r.String())) -// } -// } -// continue -// -// } -// -// if r.Kind() == reflect.Slice { -// for i := 0; i < r.Len(); i++ { -// if r.Index(i).CanAddr() { -// replacePath(p, r.Index(i).Addr().Interface()) -// } -// } -// } else if r.Kind() == reflect.Map { -// for _, k := range r.MapKeys() { -// if r.MapIndex(k).CanAddr() { -// replacePath(p, r.MapIndex(k).Addr().Interface()) -// } -// } -// } else if r.Kind() == reflect.Ptr { -// if r.Elem().CanAddr() { -// replacePath(p, r.Elem().Addr().Interface()) -// } -// } else if r.Kind() == reflect.Interface { -// if r.Elem().CanAddr() { -// replacePath(p, r.Elem().Addr().Interface()) -// } -// } -// -// } -// -// } func replacePath(p string, c interface{}) { cValue := reflect.ValueOf(c) // If c is of type PathValue (which implements pathInterface), modify the path - if cValue.Type().ConvertibleTo(reflect.TypeOf(PathValue(""))) { - pathVal := cValue.Convert(reflect.TypeOf(PathValue(""))).Interface().(PathValue) + if cValue.Type() == reflect.TypeOf(PathValue("")) { + pathVal := cValue.Interface().(PathValue) if !path.IsAbs(pathVal.String()) { newPath := PathValue(path.Join(p, pathVal.String())) if cValue.CanSet() { @@ -189,8 +130,8 @@ func handleField(p string, r reflect.Value) { case reflect.Slice: for i := 0; i < r.Len(); i++ { elem := r.Index(i) - if elem.Type().ConvertibleTo(reflect.TypeOf(PathValue(""))) { - pathVal := elem.Convert(reflect.TypeOf(PathValue(""))).Interface().(PathValue) + if elem.Type() == reflect.TypeOf(PathValue("")) { + pathVal := elem.Interface().(PathValue) if !path.IsAbs(pathVal.String()) { newPath := PathValue(path.Join(p, pathVal.String())) if elem.CanSet() { @@ -204,8 +145,8 @@ func handleField(p string, r reflect.Value) { case reflect.Map: for _, k := range r.MapKeys() { elem := r.MapIndex(k) - if elem.Type().ConvertibleTo(reflect.TypeOf(PathValue(""))) { - pathVal := elem.Convert(reflect.TypeOf(PathValue(""))).Interface().(PathValue) + if elem.Type() == reflect.TypeOf(PathValue("")) { + pathVal := elem.Interface().(PathValue) if !path.IsAbs(pathVal.String()) { newPath := PathValue(path.Join(p, pathVal.String())) r.SetMapIndex(k, reflect.ValueOf(newPath)) @@ -217,9 +158,15 @@ func handleField(p string, r reflect.Value) { default: // Check for pathInterface if v, ok := r.Interface().(pathInterface); ok { + // Check if r is nil + if r.Kind() == reflect.Ptr && r.IsNil() { + return + } currentPath := v.String() if r.CanSet() && !path.IsAbs(currentPath) { - r.SetString(path.Join(p, currentPath)) + if r.Type() == reflect.TypeOf(PathValue("")) { + r.Set(reflect.ValueOf(PathValue(path.Join(p, currentPath)))) + } } } } diff --git a/integration_test.go b/integration_test.go index 89e6e908f341dbfe42ca43269891b8c1c720ccfd..c5f1b925eb6b8a65ff3cc98a7c2e64b56ab3eba8 100644 --- a/integration_test.go +++ b/integration_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -func FuzzTest(f *testing.F) { +func Fuzz1Test(f *testing.F) { f.Fuzz(func(t *testing.T, a string, b bool, f int) { diff --git a/issue-7_test.go b/issue-7_test.go index 781f678e3c287fb1d4d61d8f1f8d56947a87fc18..711714f2de55c63db0ec8a7de234609274dd4d6d 100644 --- a/issue-7_test.go +++ b/issue-7_test.go @@ -87,6 +87,7 @@ type Issue7TestStruct2 struct { B Issue7TestStruct1 C []PathValue D map[string]PathValue + E string } func TestIssue7ReplacePath(t *testing.T) { @@ -114,6 +115,7 @@ func TestIssue7ReplacePath(t *testing.T) { "key1": "relkey1", "key2": "relkey2", }, + E: "justastring", } replacePath(basePath, &ts2) @@ -136,4 +138,110 @@ func TestIssue7ReplacePath(t *testing.T) { if ts2.D["key2"] != PathValue(path.Join(basePath, "relkey2")) { t.Errorf("Expected '%s', got '%s'", path.Join(basePath, "relkey2"), ts2.D["key2"]) } + if ts2.E != "justastring" { + t.Errorf("Expected '%s', got '%s'", "justastring", ts2.E) + } +} + +type DeepNestedStruct struct { + A PathValue + B string + C *PathValue + D []PathValue + E map[string]PathValue + F interface{} + G *DeepNestedStruct + H []DeepNestedStruct + I map[string]DeepNestedStruct +} + +type MyStruct struct { + A PathValue + B string + C *PathValue + D []PathValue + E map[string]PathValue + F interface{} + G DeepNestedStruct + H []DeepNestedStruct + I map[string]DeepNestedStruct +} + +func TestReplacePath(t *testing.T) { + // Sample seed data to guide the fuzzer + tempPathValueC := PathValue("rel/pathC") + tempPathValueF := PathValue("rel/pathF") + tempPathValueGC := PathValue("rel/pathGC") + + s := &MyStruct{ + A: "rel/pathA", + B: "normalStringB", + C: &tempPathValueC, + D: []PathValue{"rel1D", "rel2D"}, + E: map[string]PathValue{ + "key1E": "relkey1E", + "key2E": "relkey2E", + }, + F: tempPathValueF, + G: DeepNestedStruct{ + A: "rel/pathGA", + B: "normalStringGB", + C: &tempPathValueGC, + D: []PathValue{"rel1GD", "rel2GD"}, + E: map[string]PathValue{ + "key1GE": "relkey1GE", + "key2GE": "relkey2GE", + }, + F: tempPathValueF, + H: []DeepNestedStruct{ + { + A: "rel/pathGHA", + }, + }, + I: map[string]DeepNestedStruct{ + "key1GI": { + A: "rel/pathGIA", + }, + }, + }, + H: []DeepNestedStruct{ + { + A: "rel/pathHA", + }, + }, + I: map[string]DeepNestedStruct{ + "key1I": { + A: "rel/pathIA", + }, + }, + } + + basePath := "/basepath" + // Copy the struct to compare later + original := *s + + replacePath(basePath, s) + + // Checking each field to ensure replacePath works as expected + if s.A != original.A && !path.IsAbs(s.A.String()) { + t.Errorf("Path not replaced correctly for field A") + } + if s.C != nil && *s.C != *original.C && !path.IsAbs(s.C.String()) { + t.Errorf("Path not replaced correctly for field C") + } + for idx, val := range s.D { + if val != original.D[idx] && !path.IsAbs(val.String()) { + t.Errorf("Path not replaced correctly for field D at index %d", idx) + } + } + for key, val := range s.E { + if val != original.E[key] && !path.IsAbs(val.String()) { + t.Errorf("Path not replaced correctly for field E with key %s", key) + } + } + if s.F != original.F && !path.IsAbs(s.F.(PathValue).String()) { + t.Errorf("Path not replaced correctly for field F") + } + // Check fields of the nested struct (G) similarly... + // Continue this for all fields and nested fields of MyStruct }