Compare commits

..

14 Commits

Author SHA1 Message Date
Muyao CHEN
a0bad4d6e9 walk: handle functions... 2024-09-20 21:04:25 +02:00
Muyao CHEN
ab7818e9ea walk: handle channels 2024-09-20 21:00:47 +02:00
Muyao CHEN
574532f484 walk: handle map test separately 2024-09-20 20:51:44 +02:00
Muyao CHEN
10a93aeb27 walk: rerefacto 2024-09-20 20:44:40 +02:00
Muyao CHEN
61a0d0ff03 walk: deal with maps 2024-09-20 20:41:50 +02:00
Muyao CHEN
f64d75ca45 walk: deal with array 2024-09-20 20:37:54 +02:00
Muyao CHEN
799d730d14 walk: refacto slice and struct 2024-09-20 20:36:24 +02:00
Muyao CHEN
1c3ad9b9d8 walk: refacto 2024-09-20 20:32:37 +02:00
Muyao CHEN
049c346e63 walk: deal with slices 2024-09-20 20:27:53 +02:00
Muyao CHEN
80959f822b waik: work with pointer 2024-09-20 20:19:49 +02:00
Muyao CHEN
30a408c9e4 walk: recursively search for string 2024-09-20 20:15:59 +02:00
Muyao CHEN
8026475424 walk: check if the field is of string type 2024-09-20 20:05:32 +02:00
Muyao CHEN
122770ae5c walk: loop on the string fields 2024-09-20 20:00:05 +02:00
Muyao CHEN
766d7449e8 walk: refactor test to table test 2024-09-20 19:56:28 +02:00
2 changed files with 176 additions and 15 deletions

View File

@ -5,7 +5,48 @@ import (
)
func Walk(x interface{}, fn func(string)) {
val := reflect.ValueOf(x)
field := val.Field(0)
fn(field.String())
val := getValue(x)
walkValue := func(value reflect.Value) {
Walk(value.Interface(), fn)
}
switch val.Kind() {
case reflect.String:
fn(val.String())
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
// XXX: Interface() to get interface
walkValue(val.Field(i))
}
case reflect.Slice, reflect.Array:
for i := 0; i < val.Len(); i++ {
walkValue(val.Index(i))
}
case reflect.Map:
for _, key := range val.MapKeys() {
walkValue(val.MapIndex(key))
}
case reflect.Chan:
for {
if v, ok := val.Recv(); ok {
walkValue(v)
} else {
break
}
}
case reflect.Func:
valFnResult := val.Call(nil)
for _, res := range valFnResult {
walkValue(res)
}
}
}
func getValue(x interface{}) reflect.Value {
val := reflect.ValueOf(x)
if val.Kind() == reflect.Pointer {
return val.Elem()
}
return val
}

View File

@ -1,27 +1,147 @@
package walk
import (
"reflect"
"testing"
)
type Profile struct {
Age int
City string
}
type Person struct {
Name string
Profile Profile
}
func TestWalk(t *testing.T) {
t.Run("walk function test", func(t *testing.T) {
expected := "Chris"
cases := []struct {
Name string
Input interface{}
ExpectedCalls []string
}{
{
"struct with one string field",
struct {
Name string
}{"Chris"},
[]string{"Chris"},
},
{
"struct with two string fields",
struct {
Name string
City string
}{"Chris", "London"},
[]string{"Chris", "London"},
},
{
"struct with non string field",
struct {
Name string
Age int
}{"Chris", 29},
[]string{"Chris"},
},
{
"nested fields",
Person{"Chris", Profile{29, "London"}},
[]string{"Chris", "London"},
},
{
"pointer to things",
&Person{"Chris", Profile{29, "London"}},
[]string{"Chris", "London"},
},
{
"slices",
[]Profile{
{29, "London"},
{33, "Paris"},
},
[]string{"London", "Paris"},
},
{
"arrays",
[2]Profile{
{29, "London"},
{33, "Paris"},
},
[]string{"London", "Paris"},
},
}
for _, test := range cases {
t.Run(test.Name, func(t *testing.T) {
var got []string
Walk(test.Input, func(input string) {
got = append(got, input)
})
if !reflect.DeepEqual(got, test.ExpectedCalls) {
t.Errorf("got %v want %v", got, test.ExpectedCalls)
}
})
}
t.Run("with maps", func(t *testing.T) {
aMap := map[string]string{
"Cow": "meuh",
"Sheep": "meh",
}
var got []string
x := struct {
Name string
}{expected}
Walk(x, func(input string) {
Walk(aMap, func(input string) {
got = append(got, input)
})
if len(got) != 1 {
t.Errorf("wrong number of function calls, got %d want %d", len(got), 1)
assertContains(t, got, "meuh")
assertContains(t, got, "meh")
})
t.Run("with channels", func(t *testing.T) {
aChannel := make(chan Profile)
go func() {
aChannel <- Profile{21, "Berlin"}
aChannel <- Profile{28, "Beijing"}
close(aChannel)
}()
var got []string
want := []string{"Berlin", "Beijing"}
Walk(aChannel, func(input string) {
got = append(got, input)
})
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
if got[0] != expected {
t.Errorf("got %q want %q", got[0], expected)
})
t.Run("with functions", func(t *testing.T) {
aFunction := func() (Profile, Profile) {
return Profile{21, "Berlin"}, Profile{28, "Beijing"}
}
var got []string
want := []string{"Berlin", "Beijing"}
Walk(aFunction, func(input string) {
got = append(got, input)
})
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
})
}
func assertContains(t testing.TB, haystack []string, needle string) {
t.Helper()
contains := false
for _, x := range haystack {
if x == needle {
contains = true
}
}
if !contains {
t.Errorf("expected %v to contain %q but it didn't", haystack, needle)
}
}