279 lines
6.1 KiB
Go
279 lines
6.1 KiB
Go
package bind_test
|
|
|
|
import (
|
|
"code.squareroundforest.org/arpio/bind"
|
|
"code.squareroundforest.org/arpio/notation"
|
|
"slices"
|
|
"sort"
|
|
"testing"
|
|
)
|
|
|
|
func TestField(t *testing.T) {
|
|
t.Run("fields", func(t *testing.T) {
|
|
type s0 struct {
|
|
FieldOne int
|
|
}
|
|
|
|
type s1 struct {
|
|
Foo int
|
|
}
|
|
|
|
type s2 struct {
|
|
s0
|
|
foo int
|
|
FooBar int
|
|
Baz s1
|
|
Qux *s1
|
|
Chan chan int
|
|
Que bool
|
|
Hola []int
|
|
}
|
|
|
|
f := bind.Fields[s2]()
|
|
if len(f) != 6 {
|
|
t.Fatal(notation.Sprintwt(f))
|
|
}
|
|
|
|
m := make(map[string]bind.Field)
|
|
for _, fi := range f {
|
|
m[fi.Name()] = fi
|
|
}
|
|
|
|
fieldOne := m["field-one"]
|
|
if !slices.Equal(fieldOne.Path(), []string{"FieldOne"}) {
|
|
t.Fatal(fieldOne.Name())
|
|
}
|
|
|
|
fooBar := m["foo-bar"]
|
|
if !slices.Equal(fooBar.Path(), []string{"FooBar"}) {
|
|
t.Fatal(fooBar.Name())
|
|
}
|
|
|
|
bazFoo := m["baz-foo"]
|
|
if !slices.Equal(bazFoo.Path(), []string{"Baz", "Foo"}) {
|
|
t.Fatal(bazFoo.Name())
|
|
}
|
|
|
|
quxFoo := m["qux-foo"]
|
|
if !slices.Equal(quxFoo.Path(), []string{"Qux", "Foo"}) {
|
|
t.Fatal(quxFoo.Name())
|
|
}
|
|
|
|
que := m["que"]
|
|
if !slices.Equal(que.Path(), []string{"Que"}) || !que.Bool() {
|
|
t.Fatal(que.Name())
|
|
}
|
|
|
|
hola := m["hola"]
|
|
if !slices.Equal(hola.Path(), []string{"Hola"}) || !hola.List() {
|
|
t.Fatal(hola.Name())
|
|
}
|
|
|
|
t.Run("cannot have fields", func(t *testing.T) {
|
|
type i []int
|
|
f := bind.Fields[i]()
|
|
if len(f) != 0 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("has circular type", func(t *testing.T) {
|
|
type s struct{ Foo *s }
|
|
if len(bind.Fields[s]()) != 0 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("list", func(t *testing.T) {
|
|
type s struct{ Foo int }
|
|
f := bind.Fields[[]s]()
|
|
if len(f) != 1 || !f[0].List() {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("field values", func(t *testing.T) {
|
|
t.Run("has circular reference", func(t *testing.T) {
|
|
type s struct{ Foo any }
|
|
var v s
|
|
v.Foo = v
|
|
if len(bind.FieldValues(v)) != 0 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("slice", func(t *testing.T) {
|
|
type s struct{ Foo int }
|
|
f := bind.FieldValues([]s{{21}, {42}})
|
|
if len(f) != 2 || f[0].Value() != 21 || f[1].Value() != 42 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("scalar map", func(t *testing.T) {
|
|
f := bind.FieldValues(map[string]int{"foo": 21, "bar": 42})
|
|
sort.Slice(f, func(i, j int) bool {
|
|
if f[i].Name() > f[j].Name() {
|
|
return true
|
|
}
|
|
|
|
if f[i].Value().(int) < f[j].Value().(int) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
})
|
|
|
|
if len(f) != 2 ||
|
|
f[0].Name() != "foo" || f[0].Value() != 21 || !f[0].Free() ||
|
|
f[1].Name() != "bar" || f[1].Value() != 42 || !f[1].Free() {
|
|
t.Fatal(notation.Println(f))
|
|
}
|
|
})
|
|
|
|
t.Run("scalar map with list values", func(t *testing.T) {
|
|
f := bind.FieldValues(map[string][]int{"foo": []int{21, 36}, "bar": []int{42, 72}})
|
|
sort.Slice(f, func(i, j int) bool {
|
|
if f[i].Name() > f[j].Name() {
|
|
return true
|
|
}
|
|
|
|
if f[i].Value().(int) < f[j].Value().(int) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
})
|
|
|
|
if len(f) != 4 ||
|
|
f[0].Name() != "foo" || f[0].Value() != 21 || !f[0].Free() ||
|
|
f[1].Name() != "foo" || f[1].Value() != 36 || !f[1].Free() ||
|
|
f[2].Name() != "bar" || f[2].Value() != 42 || !f[2].Free() ||
|
|
f[3].Name() != "bar" || f[3].Value() != 72 || !f[3].Free() {
|
|
t.Fatal(notation.Println(f))
|
|
}
|
|
})
|
|
|
|
t.Run("not a struct", func(t *testing.T) {
|
|
v := []int{21, 42, 84}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 0 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("not exported field", func(t *testing.T) {
|
|
type s struct {
|
|
Foo int
|
|
bar string
|
|
}
|
|
v := s{Foo: 42, bar: "baz"}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 1 || f[0].Name() != "foo" {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("scalar fields", func(t *testing.T) {
|
|
type s struct {
|
|
Foo int
|
|
Bar bool
|
|
}
|
|
v := s{Foo: 42, Bar: true}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 2 ||
|
|
f[0].Name() != "foo" || !slices.Equal(f[0].Path(), []string{"Foo"}) || f[0].Value() != 42 || f[0].Bool() ||
|
|
f[1].Name() != "bar" || !slices.Equal(f[1].Path(), []string{"Bar"}) || f[1].Value() != true || !f[1.].Bool() {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("list field", func(t *testing.T) {
|
|
type s struct{ Foo []int }
|
|
v := s{Foo: []int{21, 42, 84}}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 3 ||
|
|
f[0].Name() != "foo" || f[0].Value() != 21 ||
|
|
f[1].Name() != "foo" || f[1].Value() != 42 ||
|
|
f[2].Name() != "foo" || f[2].Value() != 84 {
|
|
t.Fatal(notation.Sprintwt(f))
|
|
}
|
|
})
|
|
|
|
t.Run("list of lists", func(t *testing.T) {
|
|
type s struct{ Foo [][]int }
|
|
v := s{Foo: [][]int{{21}, {42}}}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 2 ||
|
|
f[0].Name() != "foo" || f[0].Value() != 21 ||
|
|
f[1].Name() != "foo" || f[1].Value() != 42 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("list of scalar maps", func(t *testing.T) {
|
|
type s struct{ Foo any }
|
|
v := s{Foo: []any{map[string]int{"foo": 42}}}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 1 || f[0].Name() != "foo-foo" || f[0].Value() != 42 {
|
|
t.Fatal(notation.Sprintwt(f))
|
|
}
|
|
})
|
|
|
|
t.Run("list of structs", func(t *testing.T) {
|
|
type s struct{ Foo any }
|
|
v := s{Foo: []any{s{Foo: 21}, s{Foo: 42}}}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 2 ||
|
|
f[0].Name() != "foo-foo" || f[0].Value() != 21 ||
|
|
f[1].Name() != "foo-foo" || f[1].Value() != 42 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("scalar map field", func(t *testing.T) {
|
|
type s struct{ Foo map[string]int }
|
|
v := s{Foo: map[string]int{"foo": 42}}
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 1 || f[0].Name() != "foo-foo" || f[0].Value() != 42 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("anonymous field", func(t *testing.T) {
|
|
type s0 struct{ Foo int }
|
|
type s1 struct {
|
|
s0
|
|
Bar int
|
|
}
|
|
var v s1
|
|
v.Foo = 21
|
|
v.Bar = 42
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 2 ||
|
|
f[0].Name() != "foo" || f[0].Value() != 21 ||
|
|
f[1].Name() != "bar" || f[1].Value() != 42 {
|
|
t.Fatal()
|
|
}
|
|
})
|
|
|
|
t.Run("child struct", func(t *testing.T) {
|
|
type S0 struct{ Foo int }
|
|
type s1 struct {
|
|
S0 S0
|
|
Bar int
|
|
}
|
|
var v s1
|
|
v.S0.Foo = 21
|
|
v.Bar = 42
|
|
f := bind.FieldValues(v)
|
|
if len(f) != 2 ||
|
|
f[0].Name() != "s-0-foo" || f[0].Value() != 21 ||
|
|
f[1].Name() != "bar" || f[1].Value() != 42 {
|
|
t.Fatal(notation.Sprintwt(f))
|
|
}
|
|
})
|
|
})
|
|
}
|