2058 lines
47 KiB
Go
2058 lines
47 KiB
Go
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package schema
|
||
|
|
||
|
import (
|
||
|
"encoding/hex"
|
||
|
"errors"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type IntAlias int
|
||
|
|
||
|
type rudeBool bool
|
||
|
|
||
|
func (id *rudeBool) UnmarshalText(text []byte) error {
|
||
|
value := string(text)
|
||
|
switch {
|
||
|
case strings.EqualFold("Yup", value):
|
||
|
*id = true
|
||
|
case strings.EqualFold("Nope", value):
|
||
|
*id = false
|
||
|
default:
|
||
|
return errors.New("value must be yup or nope")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// All cases we want to cover, in a nutshell.
|
||
|
type S1 struct {
|
||
|
F01 int `schema:"f1"`
|
||
|
F02 *int `schema:"f2"`
|
||
|
F03 []int `schema:"f3"`
|
||
|
F04 []*int `schema:"f4"`
|
||
|
F05 *[]int `schema:"f5"`
|
||
|
F06 *[]*int `schema:"f6"`
|
||
|
F07 S2 `schema:"f7"`
|
||
|
F08 *S1 `schema:"f8"`
|
||
|
F09 int `schema:"-"`
|
||
|
F10 []S1 `schema:"f10"`
|
||
|
F11 []*S1 `schema:"f11"`
|
||
|
F12 *[]S1 `schema:"f12"`
|
||
|
F13 *[]*S1 `schema:"f13"`
|
||
|
F14 int `schema:"f14"`
|
||
|
F15 IntAlias `schema:"f15"`
|
||
|
F16 []IntAlias `schema:"f16"`
|
||
|
F17 S19 `schema:"f17"`
|
||
|
F18 rudeBool `schema:"f18"`
|
||
|
F19 *rudeBool `schema:"f19"`
|
||
|
F20 []rudeBool `schema:"f20"`
|
||
|
F21 []*rudeBool `schema:"f21"`
|
||
|
}
|
||
|
|
||
|
type S2 struct {
|
||
|
F01 *[]*int `schema:"f1"`
|
||
|
}
|
||
|
|
||
|
type S19 [2]byte
|
||
|
|
||
|
func (id *S19) UnmarshalText(text []byte) error {
|
||
|
buf, err := hex.DecodeString(string(text))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if len(buf) > len(*id) {
|
||
|
return errors.New("out of range")
|
||
|
}
|
||
|
copy((*id)[:], buf)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func TestAll(t *testing.T) {
|
||
|
v := map[string][]string{
|
||
|
"f1": {"1"},
|
||
|
"f2": {"2"},
|
||
|
"f3": {"31", "32"},
|
||
|
"f4": {"41", "42"},
|
||
|
"f5": {"51", "52"},
|
||
|
"f6": {"61", "62"},
|
||
|
"f7.f1": {"71", "72"},
|
||
|
"f8.f8.f7.f1": {"81", "82"},
|
||
|
"f9": {"9"},
|
||
|
"f10.0.f10.0.f6": {"101", "102"},
|
||
|
"f10.0.f10.1.f6": {"103", "104"},
|
||
|
"f11.0.f11.0.f6": {"111", "112"},
|
||
|
"f11.0.f11.1.f6": {"113", "114"},
|
||
|
"f12.0.f12.0.f6": {"121", "122"},
|
||
|
"f12.0.f12.1.f6": {"123", "124"},
|
||
|
"f13.0.f13.0.f6": {"131", "132"},
|
||
|
"f13.0.f13.1.f6": {"133", "134"},
|
||
|
"f14": {},
|
||
|
"f15": {"151"},
|
||
|
"f16": {"161", "162"},
|
||
|
"f17": {"1a2b"},
|
||
|
"f18": {"yup"},
|
||
|
"f19": {"nope"},
|
||
|
"f20": {"nope", "yup"},
|
||
|
"f21": {"yup", "nope"},
|
||
|
}
|
||
|
f2 := 2
|
||
|
f41, f42 := 41, 42
|
||
|
f61, f62 := 61, 62
|
||
|
f71, f72 := 71, 72
|
||
|
f81, f82 := 81, 82
|
||
|
f101, f102, f103, f104 := 101, 102, 103, 104
|
||
|
f111, f112, f113, f114 := 111, 112, 113, 114
|
||
|
f121, f122, f123, f124 := 121, 122, 123, 124
|
||
|
f131, f132, f133, f134 := 131, 132, 133, 134
|
||
|
var f151 IntAlias = 151
|
||
|
var f161, f162 IntAlias = 161, 162
|
||
|
var f152, f153 rudeBool = true, false
|
||
|
e := S1{
|
||
|
F01: 1,
|
||
|
F02: &f2,
|
||
|
F03: []int{31, 32},
|
||
|
F04: []*int{&f41, &f42},
|
||
|
F05: &[]int{51, 52},
|
||
|
F06: &[]*int{&f61, &f62},
|
||
|
F07: S2{
|
||
|
F01: &[]*int{&f71, &f72},
|
||
|
},
|
||
|
F08: &S1{
|
||
|
F08: &S1{
|
||
|
F07: S2{
|
||
|
F01: &[]*int{&f81, &f82},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F09: 0,
|
||
|
F10: []S1{
|
||
|
S1{
|
||
|
F10: []S1{
|
||
|
S1{F06: &[]*int{&f101, &f102}},
|
||
|
S1{F06: &[]*int{&f103, &f104}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F11: []*S1{
|
||
|
&S1{
|
||
|
F11: []*S1{
|
||
|
&S1{F06: &[]*int{&f111, &f112}},
|
||
|
&S1{F06: &[]*int{&f113, &f114}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F12: &[]S1{
|
||
|
S1{
|
||
|
F12: &[]S1{
|
||
|
S1{F06: &[]*int{&f121, &f122}},
|
||
|
S1{F06: &[]*int{&f123, &f124}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F13: &[]*S1{
|
||
|
&S1{
|
||
|
F13: &[]*S1{
|
||
|
&S1{F06: &[]*int{&f131, &f132}},
|
||
|
&S1{F06: &[]*int{&f133, &f134}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F14: 0,
|
||
|
F15: f151,
|
||
|
F16: []IntAlias{f161, f162},
|
||
|
F17: S19{0x1a, 0x2b},
|
||
|
F18: f152,
|
||
|
F19: &f153,
|
||
|
F20: []rudeBool{f153, f152},
|
||
|
F21: []*rudeBool{&f152, &f153},
|
||
|
}
|
||
|
|
||
|
s := &S1{}
|
||
|
_ = NewDecoder().Decode(s, v)
|
||
|
|
||
|
vals := func(values []*int) []int {
|
||
|
r := make([]int, len(values))
|
||
|
for k, v := range values {
|
||
|
r[k] = *v
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
if s.F01 != e.F01 {
|
||
|
t.Errorf("f1: expected %v, got %v", e.F01, s.F01)
|
||
|
}
|
||
|
if s.F02 == nil {
|
||
|
t.Errorf("f2: expected %v, got nil", *e.F02)
|
||
|
} else if *s.F02 != *e.F02 {
|
||
|
t.Errorf("f2: expected %v, got %v", *e.F02, *s.F02)
|
||
|
}
|
||
|
if s.F03 == nil {
|
||
|
t.Errorf("f3: expected %v, got nil", e.F03)
|
||
|
} else if len(s.F03) != 2 || s.F03[0] != e.F03[0] || s.F03[1] != e.F03[1] {
|
||
|
t.Errorf("f3: expected %v, got %v", e.F03, s.F03)
|
||
|
}
|
||
|
if s.F04 == nil {
|
||
|
t.Errorf("f4: expected %v, got nil", e.F04)
|
||
|
} else {
|
||
|
if len(s.F04) != 2 || *(s.F04)[0] != *(e.F04)[0] || *(s.F04)[1] != *(e.F04)[1] {
|
||
|
t.Errorf("f4: expected %v, got %v", vals(e.F04), vals(s.F04))
|
||
|
}
|
||
|
}
|
||
|
if s.F05 == nil {
|
||
|
t.Errorf("f5: expected %v, got nil", e.F05)
|
||
|
} else {
|
||
|
sF05, eF05 := *s.F05, *e.F05
|
||
|
if len(sF05) != 2 || sF05[0] != eF05[0] || sF05[1] != eF05[1] {
|
||
|
t.Errorf("f5: expected %v, got %v", eF05, sF05)
|
||
|
}
|
||
|
}
|
||
|
if s.F06 == nil {
|
||
|
t.Errorf("f6: expected %v, got nil", vals(*e.F06))
|
||
|
} else {
|
||
|
sF06, eF06 := *s.F06, *e.F06
|
||
|
if len(sF06) != 2 || *(sF06)[0] != *(eF06)[0] || *(sF06)[1] != *(eF06)[1] {
|
||
|
t.Errorf("f6: expected %v, got %v", vals(eF06), vals(sF06))
|
||
|
}
|
||
|
}
|
||
|
if s.F07.F01 == nil {
|
||
|
t.Errorf("f7.f1: expected %v, got nil", vals(*e.F07.F01))
|
||
|
} else {
|
||
|
sF07, eF07 := *s.F07.F01, *e.F07.F01
|
||
|
if len(sF07) != 2 || *(sF07)[0] != *(eF07)[0] || *(sF07)[1] != *(eF07)[1] {
|
||
|
t.Errorf("f7.f1: expected %v, got %v", vals(eF07), vals(sF07))
|
||
|
}
|
||
|
}
|
||
|
if s.F08 == nil {
|
||
|
t.Errorf("f8: got nil")
|
||
|
} else if s.F08.F08 == nil {
|
||
|
t.Errorf("f8.f8: got nil")
|
||
|
} else if s.F08.F08.F07.F01 == nil {
|
||
|
t.Errorf("f8.f8.f7.f1: expected %v, got nil", vals(*e.F08.F08.F07.F01))
|
||
|
} else {
|
||
|
sF08, eF08 := *s.F08.F08.F07.F01, *e.F08.F08.F07.F01
|
||
|
if len(sF08) != 2 || *(sF08)[0] != *(eF08)[0] || *(sF08)[1] != *(eF08)[1] {
|
||
|
t.Errorf("f8.f8.f7.f1: expected %v, got %v", vals(eF08), vals(sF08))
|
||
|
}
|
||
|
}
|
||
|
if s.F09 != e.F09 {
|
||
|
t.Errorf("f9: expected %v, got %v", e.F09, s.F09)
|
||
|
}
|
||
|
if s.F10 == nil {
|
||
|
t.Errorf("f10: got nil")
|
||
|
} else if len(s.F10) != 1 {
|
||
|
t.Errorf("f10: expected 1 element, got %v", s.F10)
|
||
|
} else {
|
||
|
if len(s.F10[0].F10) != 2 {
|
||
|
t.Errorf("f10.0.f10: expected 1 element, got %v", s.F10[0].F10)
|
||
|
} else {
|
||
|
sF10, eF10 := *s.F10[0].F10[0].F06, *e.F10[0].F10[0].F06
|
||
|
if sF10 == nil {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
|
||
|
} else {
|
||
|
if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
|
||
|
}
|
||
|
}
|
||
|
sF10, eF10 = *s.F10[0].F10[1].F06, *e.F10[0].F10[1].F06
|
||
|
if sF10 == nil {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
|
||
|
} else {
|
||
|
if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F11 == nil {
|
||
|
t.Errorf("f11: got nil")
|
||
|
} else if len(s.F11) != 1 {
|
||
|
t.Errorf("f11: expected 1 element, got %v", s.F11)
|
||
|
} else {
|
||
|
if len(s.F11[0].F11) != 2 {
|
||
|
t.Errorf("f11.0.f11: expected 1 element, got %v", s.F11[0].F11)
|
||
|
} else {
|
||
|
sF11, eF11 := *s.F11[0].F11[0].F06, *e.F11[0].F11[0].F06
|
||
|
if sF11 == nil {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
|
||
|
} else {
|
||
|
if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
|
||
|
}
|
||
|
}
|
||
|
sF11, eF11 = *s.F11[0].F11[1].F06, *e.F11[0].F11[1].F06
|
||
|
if sF11 == nil {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
|
||
|
} else {
|
||
|
if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F12 == nil {
|
||
|
t.Errorf("f12: got nil")
|
||
|
} else if len(*s.F12) != 1 {
|
||
|
t.Errorf("f12: expected 1 element, got %v", *s.F12)
|
||
|
} else {
|
||
|
sF12, eF12 := *(s.F12), *(e.F12)
|
||
|
if len(*sF12[0].F12) != 2 {
|
||
|
t.Errorf("f12.0.f12: expected 1 element, got %v", *sF12[0].F12)
|
||
|
} else {
|
||
|
sF122, eF122 := *(*sF12[0].F12)[0].F06, *(*eF12[0].F12)[0].F06
|
||
|
if sF122 == nil {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
|
||
|
} else {
|
||
|
if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
|
||
|
}
|
||
|
}
|
||
|
sF122, eF122 = *(*sF12[0].F12)[1].F06, *(*eF12[0].F12)[1].F06
|
||
|
if sF122 == nil {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
|
||
|
} else {
|
||
|
if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F13 == nil {
|
||
|
t.Errorf("f13: got nil")
|
||
|
} else if len(*s.F13) != 1 {
|
||
|
t.Errorf("f13: expected 1 element, got %v", *s.F13)
|
||
|
} else {
|
||
|
sF13, eF13 := *(s.F13), *(e.F13)
|
||
|
if len(*sF13[0].F13) != 2 {
|
||
|
t.Errorf("f13.0.f13: expected 1 element, got %v", *sF13[0].F13)
|
||
|
} else {
|
||
|
sF132, eF132 := *(*sF13[0].F13)[0].F06, *(*eF13[0].F13)[0].F06
|
||
|
if sF132 == nil {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
|
||
|
} else {
|
||
|
if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
|
||
|
}
|
||
|
}
|
||
|
sF132, eF132 = *(*sF13[0].F13)[1].F06, *(*eF13[0].F13)[1].F06
|
||
|
if sF132 == nil {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
|
||
|
} else {
|
||
|
if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F14 != e.F14 {
|
||
|
t.Errorf("f14: expected %v, got %v", e.F14, s.F14)
|
||
|
}
|
||
|
if s.F15 != e.F15 {
|
||
|
t.Errorf("f15: expected %v, got %v", e.F15, s.F15)
|
||
|
}
|
||
|
if s.F16 == nil {
|
||
|
t.Errorf("f16: nil")
|
||
|
} else if len(s.F16) != len(e.F16) {
|
||
|
t.Errorf("f16: expected len %d, got %d", len(e.F16), len(s.F16))
|
||
|
} else if !reflect.DeepEqual(s.F16, e.F16) {
|
||
|
t.Errorf("f16: expected %v, got %v", e.F16, s.F16)
|
||
|
}
|
||
|
if s.F17 != e.F17 {
|
||
|
t.Errorf("f17: expected %v, got %v", e.F17, s.F17)
|
||
|
}
|
||
|
if s.F18 != e.F18 {
|
||
|
t.Errorf("f18: expected %v, got %v", e.F18, s.F18)
|
||
|
}
|
||
|
if *s.F19 != *e.F19 {
|
||
|
t.Errorf("f19: expected %v, got %v", *e.F19, *s.F19)
|
||
|
}
|
||
|
if s.F20 == nil {
|
||
|
t.Errorf("f20: nil")
|
||
|
} else if len(s.F20) != len(e.F20) {
|
||
|
t.Errorf("f20: expected %v, got %v", e.F20, s.F20)
|
||
|
} else if !reflect.DeepEqual(s.F20, e.F20) {
|
||
|
t.Errorf("f20: expected %v, got %v", e.F20, s.F20)
|
||
|
}
|
||
|
if s.F21 == nil {
|
||
|
t.Errorf("f21: nil")
|
||
|
} else if len(s.F21) != len(e.F21) {
|
||
|
t.Errorf("f21: expected length %d, got %d", len(e.F21), len(s.F21))
|
||
|
} else if !reflect.DeepEqual(s.F21, e.F21) {
|
||
|
t.Errorf("f21: expected %v, got %v", e.F21, s.F21)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkAll(b *testing.B) {
|
||
|
v := map[string][]string{
|
||
|
"f1": {"1"},
|
||
|
"f2": {"2"},
|
||
|
"f3": {"31", "32"},
|
||
|
"f4": {"41", "42"},
|
||
|
"f5": {"51", "52"},
|
||
|
"f6": {"61", "62"},
|
||
|
"f7.f1": {"71", "72"},
|
||
|
"f8.f8.f7.f1": {"81", "82"},
|
||
|
"f9": {"9"},
|
||
|
"f10.0.f10.0.f6": {"101", "102"},
|
||
|
"f10.0.f10.1.f6": {"103", "104"},
|
||
|
"f11.0.f11.0.f6": {"111", "112"},
|
||
|
"f11.0.f11.1.f6": {"113", "114"},
|
||
|
"f12.0.f12.0.f6": {"121", "122"},
|
||
|
"f12.0.f12.1.f6": {"123", "124"},
|
||
|
"f13.0.f13.0.f6": {"131", "132"},
|
||
|
"f13.0.f13.1.f6": {"133", "134"},
|
||
|
}
|
||
|
|
||
|
b.ResetTimer()
|
||
|
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
s := &S1{}
|
||
|
_ = NewDecoder().Decode(s, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S3 struct {
|
||
|
F01 bool
|
||
|
F02 float32
|
||
|
F03 float64
|
||
|
F04 int
|
||
|
F05 int8
|
||
|
F06 int16
|
||
|
F07 int32
|
||
|
F08 int64
|
||
|
F09 string
|
||
|
F10 uint
|
||
|
F11 uint8
|
||
|
F12 uint16
|
||
|
F13 uint32
|
||
|
F14 uint64
|
||
|
}
|
||
|
|
||
|
func TestDefaultConverters(t *testing.T) {
|
||
|
v := map[string][]string{
|
||
|
"F01": {"true"},
|
||
|
"F02": {"4.2"},
|
||
|
"F03": {"4.3"},
|
||
|
"F04": {"-42"},
|
||
|
"F05": {"-43"},
|
||
|
"F06": {"-44"},
|
||
|
"F07": {"-45"},
|
||
|
"F08": {"-46"},
|
||
|
"F09": {"foo"},
|
||
|
"F10": {"42"},
|
||
|
"F11": {"43"},
|
||
|
"F12": {"44"},
|
||
|
"F13": {"45"},
|
||
|
"F14": {"46"},
|
||
|
}
|
||
|
e := S3{
|
||
|
F01: true,
|
||
|
F02: 4.2,
|
||
|
F03: 4.3,
|
||
|
F04: -42,
|
||
|
F05: -43,
|
||
|
F06: -44,
|
||
|
F07: -45,
|
||
|
F08: -46,
|
||
|
F09: "foo",
|
||
|
F10: 42,
|
||
|
F11: 43,
|
||
|
F12: 44,
|
||
|
F13: 45,
|
||
|
F14: 46,
|
||
|
}
|
||
|
s := &S3{}
|
||
|
_ = NewDecoder().Decode(s, v)
|
||
|
if s.F01 != e.F01 {
|
||
|
t.Errorf("F01: expected %v, got %v", e.F01, s.F01)
|
||
|
}
|
||
|
if s.F02 != e.F02 {
|
||
|
t.Errorf("F02: expected %v, got %v", e.F02, s.F02)
|
||
|
}
|
||
|
if s.F03 != e.F03 {
|
||
|
t.Errorf("F03: expected %v, got %v", e.F03, s.F03)
|
||
|
}
|
||
|
if s.F04 != e.F04 {
|
||
|
t.Errorf("F04: expected %v, got %v", e.F04, s.F04)
|
||
|
}
|
||
|
if s.F05 != e.F05 {
|
||
|
t.Errorf("F05: expected %v, got %v", e.F05, s.F05)
|
||
|
}
|
||
|
if s.F06 != e.F06 {
|
||
|
t.Errorf("F06: expected %v, got %v", e.F06, s.F06)
|
||
|
}
|
||
|
if s.F07 != e.F07 {
|
||
|
t.Errorf("F07: expected %v, got %v", e.F07, s.F07)
|
||
|
}
|
||
|
if s.F08 != e.F08 {
|
||
|
t.Errorf("F08: expected %v, got %v", e.F08, s.F08)
|
||
|
}
|
||
|
if s.F09 != e.F09 {
|
||
|
t.Errorf("F09: expected %v, got %v", e.F09, s.F09)
|
||
|
}
|
||
|
if s.F10 != e.F10 {
|
||
|
t.Errorf("F10: expected %v, got %v", e.F10, s.F10)
|
||
|
}
|
||
|
if s.F11 != e.F11 {
|
||
|
t.Errorf("F11: expected %v, got %v", e.F11, s.F11)
|
||
|
}
|
||
|
if s.F12 != e.F12 {
|
||
|
t.Errorf("F12: expected %v, got %v", e.F12, s.F12)
|
||
|
}
|
||
|
if s.F13 != e.F13 {
|
||
|
t.Errorf("F13: expected %v, got %v", e.F13, s.F13)
|
||
|
}
|
||
|
if s.F14 != e.F14 {
|
||
|
t.Errorf("F14: expected %v, got %v", e.F14, s.F14)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestOn(t *testing.T) {
|
||
|
v := map[string][]string{
|
||
|
"F01": {"on"},
|
||
|
}
|
||
|
s := S3{}
|
||
|
err := NewDecoder().Decode(&s, v)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !s.F01 {
|
||
|
t.Fatal("Value was not set to true")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
func TestInlineStruct(t *testing.T) {
|
||
|
s1 := &struct {
|
||
|
F01 bool
|
||
|
}{}
|
||
|
s2 := &struct {
|
||
|
F01 int
|
||
|
}{}
|
||
|
v1 := map[string][]string{
|
||
|
"F01": {"true"},
|
||
|
}
|
||
|
v2 := map[string][]string{
|
||
|
"F01": {"42"},
|
||
|
}
|
||
|
decoder := NewDecoder()
|
||
|
_ = decoder.Decode(s1, v1)
|
||
|
if s1.F01 != true {
|
||
|
t.Errorf("s1: expected %v, got %v", true, s1.F01)
|
||
|
}
|
||
|
_ = decoder.Decode(s2, v2)
|
||
|
if s2.F01 != 42 {
|
||
|
t.Errorf("s2: expected %v, got %v", 42, s2.F01)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type Foo struct {
|
||
|
F01 int
|
||
|
F02 Bar
|
||
|
Bif []Baz
|
||
|
}
|
||
|
|
||
|
type Bar struct {
|
||
|
F01 string
|
||
|
F02 string
|
||
|
F03 string
|
||
|
F14 string
|
||
|
S05 string
|
||
|
Str string
|
||
|
}
|
||
|
|
||
|
type Baz struct {
|
||
|
F99 []string
|
||
|
}
|
||
|
|
||
|
func TestSimpleExample(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"F01": {"1"},
|
||
|
"F02.F01": {"S1"},
|
||
|
"F02.F02": {"S2"},
|
||
|
"F02.F03": {"S3"},
|
||
|
"F02.F14": {"S4"},
|
||
|
"F02.S05": {"S5"},
|
||
|
"F02.Str": {"Str"},
|
||
|
"Bif.0.F99": {"A", "B", "C"},
|
||
|
}
|
||
|
|
||
|
e := &Foo{
|
||
|
F01: 1,
|
||
|
F02: Bar{
|
||
|
F01: "S1",
|
||
|
F02: "S2",
|
||
|
F03: "S3",
|
||
|
F14: "S4",
|
||
|
S05: "S5",
|
||
|
Str: "Str",
|
||
|
},
|
||
|
Bif: []Baz{{
|
||
|
F99: []string{"A", "B", "C"}},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &Foo{}
|
||
|
_ = NewDecoder().Decode(s, data)
|
||
|
|
||
|
if s.F01 != e.F01 {
|
||
|
t.Errorf("F01: expected %v, got %v", e.F01, s.F01)
|
||
|
}
|
||
|
if s.F02.F01 != e.F02.F01 {
|
||
|
t.Errorf("F02.F01: expected %v, got %v", e.F02.F01, s.F02.F01)
|
||
|
}
|
||
|
if s.F02.F02 != e.F02.F02 {
|
||
|
t.Errorf("F02.F02: expected %v, got %v", e.F02.F02, s.F02.F02)
|
||
|
}
|
||
|
if s.F02.F03 != e.F02.F03 {
|
||
|
t.Errorf("F02.F03: expected %v, got %v", e.F02.F03, s.F02.F03)
|
||
|
}
|
||
|
if s.F02.F14 != e.F02.F14 {
|
||
|
t.Errorf("F02.F14: expected %v, got %v", e.F02.F14, s.F02.F14)
|
||
|
}
|
||
|
if s.F02.S05 != e.F02.S05 {
|
||
|
t.Errorf("F02.S05: expected %v, got %v", e.F02.S05, s.F02.S05)
|
||
|
}
|
||
|
if s.F02.Str != e.F02.Str {
|
||
|
t.Errorf("F02.Str: expected %v, got %v", e.F02.Str, s.F02.Str)
|
||
|
}
|
||
|
if len(s.Bif) != len(e.Bif) {
|
||
|
t.Errorf("Bif len: expected %d, got %d", len(e.Bif), len(s.Bif))
|
||
|
} else {
|
||
|
if len(s.Bif[0].F99) != len(e.Bif[0].F99) {
|
||
|
t.Errorf("Bif[0].F99 len: expected %d, got %d", len(e.Bif[0].F99), len(s.Bif[0].F99))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S4 struct {
|
||
|
F01 int64
|
||
|
F02 float64
|
||
|
F03 bool
|
||
|
F04 rudeBool
|
||
|
}
|
||
|
|
||
|
func TestConversionError(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"F01": {"foo"},
|
||
|
"F02": {"bar"},
|
||
|
"F03": {"baz"},
|
||
|
"F04": {"not-a-yes-or-nope"},
|
||
|
}
|
||
|
s := &S4{}
|
||
|
e := NewDecoder().Decode(s, data)
|
||
|
|
||
|
m := e.(MultiError)
|
||
|
if len(m) != 4 {
|
||
|
t.Errorf("Expected 3 errors, got %v", m)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S5 struct {
|
||
|
F01 []string
|
||
|
}
|
||
|
|
||
|
func TestEmptyValue(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"F01": {"", "foo"},
|
||
|
}
|
||
|
s := &S5{}
|
||
|
err := NewDecoder().Decode(s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if len(s.F01) != 1 {
|
||
|
t.Errorf("Expected 1 values in F01")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestEmptyValueZeroEmpty(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"F01": {"", "foo"},
|
||
|
}
|
||
|
s := S5{}
|
||
|
d := NewDecoder()
|
||
|
d.ZeroEmpty(true)
|
||
|
err := d.Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(s.F01) != 2 {
|
||
|
t.Errorf("Expected 1 values in F01")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S6 struct {
|
||
|
id string
|
||
|
}
|
||
|
|
||
|
func TestUnexportedField(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"id": {"identifier"},
|
||
|
}
|
||
|
s := &S6{}
|
||
|
err := NewDecoder().Decode(s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if s.id != "" {
|
||
|
t.Errorf("Unexported field expected to be ignored")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S7 struct {
|
||
|
ID string
|
||
|
}
|
||
|
|
||
|
func TestMultipleValues(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"ID": {"0", "1"},
|
||
|
}
|
||
|
|
||
|
s := S7{}
|
||
|
err := NewDecoder().Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if s.ID != "1" {
|
||
|
t.Errorf("Last defined value must be used when multiple values for same field are provided")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S8 struct {
|
||
|
ID string `json:"id"`
|
||
|
}
|
||
|
|
||
|
func TestSetAliasTag(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"id": {"foo"},
|
||
|
}
|
||
|
|
||
|
s := S8{}
|
||
|
dec := NewDecoder()
|
||
|
dec.SetAliasTag("json")
|
||
|
err := dec.Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if s.ID != "foo" {
|
||
|
t.Fatalf("Bad value: got %q, want %q", s.ID, "foo")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestZeroEmpty(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"F01": {""},
|
||
|
"F03": {"true"},
|
||
|
}
|
||
|
s := S4{1, 1, false, false}
|
||
|
d := NewDecoder()
|
||
|
d.ZeroEmpty(true)
|
||
|
|
||
|
err := d.Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if s.F01 != 0 {
|
||
|
t.Errorf("F01: got %v, want %v", s.F01, 0)
|
||
|
}
|
||
|
if s.F02 != 1 {
|
||
|
t.Errorf("F02: got %v, want %v", s.F02, 1)
|
||
|
}
|
||
|
if s.F03 != true {
|
||
|
t.Errorf("F03: got %v, want %v", s.F03, true)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNoZeroEmpty(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"F01": {""},
|
||
|
"F03": {"true"},
|
||
|
}
|
||
|
s := S4{1, 1, false, false}
|
||
|
d := NewDecoder()
|
||
|
d.ZeroEmpty(false)
|
||
|
err := d.Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if s.F01 != 1 {
|
||
|
t.Errorf("F01: got %v, want %v", s.F01, 1)
|
||
|
}
|
||
|
if s.F02 != 1 {
|
||
|
t.Errorf("F02: got %v, want %v", s.F02, 1)
|
||
|
}
|
||
|
if s.F03 != true {
|
||
|
t.Errorf("F03: got %v, want %v", s.F03, true)
|
||
|
}
|
||
|
if s.F04 != false {
|
||
|
t.Errorf("F04: got %v, want %v", s.F04, false)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S9 struct {
|
||
|
Id string
|
||
|
}
|
||
|
|
||
|
type S10 struct {
|
||
|
S9
|
||
|
}
|
||
|
|
||
|
func TestEmbeddedField(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Id": {"identifier"},
|
||
|
}
|
||
|
s := &S10{}
|
||
|
err := NewDecoder().Decode(s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if s.Id != "identifier" {
|
||
|
t.Errorf("Missing support for embedded fields")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S11 struct {
|
||
|
S10
|
||
|
}
|
||
|
|
||
|
func TestMultipleLevelEmbeddedField(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Id": {"identifier"},
|
||
|
}
|
||
|
s := &S11{}
|
||
|
err := NewDecoder().Decode(s, data)
|
||
|
if s.Id != "identifier" {
|
||
|
t.Errorf("Missing support for multiple-level embedded fields (%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInvalidPath(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Foo.Bar": {"baz"},
|
||
|
}
|
||
|
s := S9{}
|
||
|
err := NewDecoder().Decode(&s, data)
|
||
|
expectedErr := `schema: invalid path "Foo.Bar"`
|
||
|
if err.Error() != expectedErr {
|
||
|
t.Fatalf("got %q, want %q", err, expectedErr)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInvalidPathIgnoreUnknownKeys(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Foo.Bar": {"baz"},
|
||
|
}
|
||
|
s := S9{}
|
||
|
dec := NewDecoder()
|
||
|
dec.IgnoreUnknownKeys(true)
|
||
|
err := dec.Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S1NT struct {
|
||
|
F1 int
|
||
|
F2 *int
|
||
|
F3 []int
|
||
|
F4 []*int
|
||
|
F5 *[]int
|
||
|
F6 *[]*int
|
||
|
F7 S2
|
||
|
F8 *S1
|
||
|
F9 int `schema:"-"`
|
||
|
F10 []S1
|
||
|
F11 []*S1
|
||
|
F12 *[]S1
|
||
|
F13 *[]*S1
|
||
|
}
|
||
|
|
||
|
func TestAllNT(t *testing.T) {
|
||
|
v := map[string][]string{
|
||
|
"f1": {"1"},
|
||
|
"f2": {"2"},
|
||
|
"f3": {"31", "32"},
|
||
|
"f4": {"41", "42"},
|
||
|
"f5": {"51", "52"},
|
||
|
"f6": {"61", "62"},
|
||
|
"f7.f1": {"71", "72"},
|
||
|
"f8.f8.f7.f1": {"81", "82"},
|
||
|
"f9": {"9"},
|
||
|
"f10.0.f10.0.f6": {"101", "102"},
|
||
|
"f10.0.f10.1.f6": {"103", "104"},
|
||
|
"f11.0.f11.0.f6": {"111", "112"},
|
||
|
"f11.0.f11.1.f6": {"113", "114"},
|
||
|
"f12.0.f12.0.f6": {"121", "122"},
|
||
|
"f12.0.f12.1.f6": {"123", "124"},
|
||
|
"f13.0.f13.0.f6": {"131", "132"},
|
||
|
"f13.0.f13.1.f6": {"133", "134"},
|
||
|
}
|
||
|
f2 := 2
|
||
|
f41, f42 := 41, 42
|
||
|
f61, f62 := 61, 62
|
||
|
f71, f72 := 71, 72
|
||
|
f81, f82 := 81, 82
|
||
|
f101, f102, f103, f104 := 101, 102, 103, 104
|
||
|
f111, f112, f113, f114 := 111, 112, 113, 114
|
||
|
f121, f122, f123, f124 := 121, 122, 123, 124
|
||
|
f131, f132, f133, f134 := 131, 132, 133, 134
|
||
|
e := S1NT{
|
||
|
F1: 1,
|
||
|
F2: &f2,
|
||
|
F3: []int{31, 32},
|
||
|
F4: []*int{&f41, &f42},
|
||
|
F5: &[]int{51, 52},
|
||
|
F6: &[]*int{&f61, &f62},
|
||
|
F7: S2{
|
||
|
F01: &[]*int{&f71, &f72},
|
||
|
},
|
||
|
F8: &S1{
|
||
|
F08: &S1{
|
||
|
F07: S2{
|
||
|
F01: &[]*int{&f81, &f82},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F9: 0,
|
||
|
F10: []S1{
|
||
|
S1{
|
||
|
F10: []S1{
|
||
|
S1{F06: &[]*int{&f101, &f102}},
|
||
|
S1{F06: &[]*int{&f103, &f104}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F11: []*S1{
|
||
|
&S1{
|
||
|
F11: []*S1{
|
||
|
&S1{F06: &[]*int{&f111, &f112}},
|
||
|
&S1{F06: &[]*int{&f113, &f114}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F12: &[]S1{
|
||
|
S1{
|
||
|
F12: &[]S1{
|
||
|
S1{F06: &[]*int{&f121, &f122}},
|
||
|
S1{F06: &[]*int{&f123, &f124}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
F13: &[]*S1{
|
||
|
&S1{
|
||
|
F13: &[]*S1{
|
||
|
&S1{F06: &[]*int{&f131, &f132}},
|
||
|
&S1{F06: &[]*int{&f133, &f134}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
s := &S1NT{}
|
||
|
_ = NewDecoder().Decode(s, v)
|
||
|
|
||
|
vals := func(values []*int) []int {
|
||
|
r := make([]int, len(values))
|
||
|
for k, v := range values {
|
||
|
r[k] = *v
|
||
|
}
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
if s.F1 != e.F1 {
|
||
|
t.Errorf("f1: expected %v, got %v", e.F1, s.F1)
|
||
|
}
|
||
|
if s.F2 == nil {
|
||
|
t.Errorf("f2: expected %v, got nil", *e.F2)
|
||
|
} else if *s.F2 != *e.F2 {
|
||
|
t.Errorf("f2: expected %v, got %v", *e.F2, *s.F2)
|
||
|
}
|
||
|
if s.F3 == nil {
|
||
|
t.Errorf("f3: expected %v, got nil", e.F3)
|
||
|
} else if len(s.F3) != 2 || s.F3[0] != e.F3[0] || s.F3[1] != e.F3[1] {
|
||
|
t.Errorf("f3: expected %v, got %v", e.F3, s.F3)
|
||
|
}
|
||
|
if s.F4 == nil {
|
||
|
t.Errorf("f4: expected %v, got nil", e.F4)
|
||
|
} else {
|
||
|
if len(s.F4) != 2 || *(s.F4)[0] != *(e.F4)[0] || *(s.F4)[1] != *(e.F4)[1] {
|
||
|
t.Errorf("f4: expected %v, got %v", vals(e.F4), vals(s.F4))
|
||
|
}
|
||
|
}
|
||
|
if s.F5 == nil {
|
||
|
t.Errorf("f5: expected %v, got nil", e.F5)
|
||
|
} else {
|
||
|
sF5, eF5 := *s.F5, *e.F5
|
||
|
if len(sF5) != 2 || sF5[0] != eF5[0] || sF5[1] != eF5[1] {
|
||
|
t.Errorf("f5: expected %v, got %v", eF5, sF5)
|
||
|
}
|
||
|
}
|
||
|
if s.F6 == nil {
|
||
|
t.Errorf("f6: expected %v, got nil", vals(*e.F6))
|
||
|
} else {
|
||
|
sF6, eF6 := *s.F6, *e.F6
|
||
|
if len(sF6) != 2 || *(sF6)[0] != *(eF6)[0] || *(sF6)[1] != *(eF6)[1] {
|
||
|
t.Errorf("f6: expected %v, got %v", vals(eF6), vals(sF6))
|
||
|
}
|
||
|
}
|
||
|
if s.F7.F01 == nil {
|
||
|
t.Errorf("f7.f1: expected %v, got nil", vals(*e.F7.F01))
|
||
|
} else {
|
||
|
sF7, eF7 := *s.F7.F01, *e.F7.F01
|
||
|
if len(sF7) != 2 || *(sF7)[0] != *(eF7)[0] || *(sF7)[1] != *(eF7)[1] {
|
||
|
t.Errorf("f7.f1: expected %v, got %v", vals(eF7), vals(sF7))
|
||
|
}
|
||
|
}
|
||
|
if s.F8 == nil {
|
||
|
t.Errorf("f8: got nil")
|
||
|
} else if s.F8.F08 == nil {
|
||
|
t.Errorf("f8.f8: got nil")
|
||
|
} else if s.F8.F08.F07.F01 == nil {
|
||
|
t.Errorf("f8.f8.f7.f1: expected %v, got nil", vals(*e.F8.F08.F07.F01))
|
||
|
} else {
|
||
|
sF8, eF8 := *s.F8.F08.F07.F01, *e.F8.F08.F07.F01
|
||
|
if len(sF8) != 2 || *(sF8)[0] != *(eF8)[0] || *(sF8)[1] != *(eF8)[1] {
|
||
|
t.Errorf("f8.f8.f7.f1: expected %v, got %v", vals(eF8), vals(sF8))
|
||
|
}
|
||
|
}
|
||
|
if s.F9 != e.F9 {
|
||
|
t.Errorf("f9: expected %v, got %v", e.F9, s.F9)
|
||
|
}
|
||
|
if s.F10 == nil {
|
||
|
t.Errorf("f10: got nil")
|
||
|
} else if len(s.F10) != 1 {
|
||
|
t.Errorf("f10: expected 1 element, got %v", s.F10)
|
||
|
} else {
|
||
|
if len(s.F10[0].F10) != 2 {
|
||
|
t.Errorf("f10.0.f10: expected 1 element, got %v", s.F10[0].F10)
|
||
|
} else {
|
||
|
sF10, eF10 := *s.F10[0].F10[0].F06, *e.F10[0].F10[0].F06
|
||
|
if sF10 == nil {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
|
||
|
} else {
|
||
|
if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
|
||
|
}
|
||
|
}
|
||
|
sF10, eF10 = *s.F10[0].F10[1].F06, *e.F10[0].F10[1].F06
|
||
|
if sF10 == nil {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got nil", vals(eF10))
|
||
|
} else {
|
||
|
if len(sF10) != 2 || *(sF10)[0] != *(eF10)[0] || *(sF10)[1] != *(eF10)[1] {
|
||
|
t.Errorf("f10.0.f10.0.f6: expected %v, got %v", vals(eF10), vals(sF10))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F11 == nil {
|
||
|
t.Errorf("f11: got nil")
|
||
|
} else if len(s.F11) != 1 {
|
||
|
t.Errorf("f11: expected 1 element, got %v", s.F11)
|
||
|
} else {
|
||
|
if len(s.F11[0].F11) != 2 {
|
||
|
t.Errorf("f11.0.f11: expected 1 element, got %v", s.F11[0].F11)
|
||
|
} else {
|
||
|
sF11, eF11 := *s.F11[0].F11[0].F06, *e.F11[0].F11[0].F06
|
||
|
if sF11 == nil {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
|
||
|
} else {
|
||
|
if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
|
||
|
}
|
||
|
}
|
||
|
sF11, eF11 = *s.F11[0].F11[1].F06, *e.F11[0].F11[1].F06
|
||
|
if sF11 == nil {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got nil", vals(eF11))
|
||
|
} else {
|
||
|
if len(sF11) != 2 || *(sF11)[0] != *(eF11)[0] || *(sF11)[1] != *(eF11)[1] {
|
||
|
t.Errorf("f11.0.f11.0.f6: expected %v, got %v", vals(eF11), vals(sF11))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F12 == nil {
|
||
|
t.Errorf("f12: got nil")
|
||
|
} else if len(*s.F12) != 1 {
|
||
|
t.Errorf("f12: expected 1 element, got %v", *s.F12)
|
||
|
} else {
|
||
|
sF12, eF12 := *(s.F12), *(e.F12)
|
||
|
if len(*sF12[0].F12) != 2 {
|
||
|
t.Errorf("f12.0.f12: expected 1 element, got %v", *sF12[0].F12)
|
||
|
} else {
|
||
|
sF122, eF122 := *(*sF12[0].F12)[0].F06, *(*eF12[0].F12)[0].F06
|
||
|
if sF122 == nil {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
|
||
|
} else {
|
||
|
if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
|
||
|
}
|
||
|
}
|
||
|
sF122, eF122 = *(*sF12[0].F12)[1].F06, *(*eF12[0].F12)[1].F06
|
||
|
if sF122 == nil {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got nil", vals(eF122))
|
||
|
} else {
|
||
|
if len(sF122) != 2 || *(sF122)[0] != *(eF122)[0] || *(sF122)[1] != *(eF122)[1] {
|
||
|
t.Errorf("f12.0.f12.0.f6: expected %v, got %v", vals(eF122), vals(sF122))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if s.F13 == nil {
|
||
|
t.Errorf("f13: got nil")
|
||
|
} else if len(*s.F13) != 1 {
|
||
|
t.Errorf("f13: expected 1 element, got %v", *s.F13)
|
||
|
} else {
|
||
|
sF13, eF13 := *(s.F13), *(e.F13)
|
||
|
if len(*sF13[0].F13) != 2 {
|
||
|
t.Errorf("f13.0.f13: expected 1 element, got %v", *sF13[0].F13)
|
||
|
} else {
|
||
|
sF132, eF132 := *(*sF13[0].F13)[0].F06, *(*eF13[0].F13)[0].F06
|
||
|
if sF132 == nil {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
|
||
|
} else {
|
||
|
if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
|
||
|
}
|
||
|
}
|
||
|
sF132, eF132 = *(*sF13[0].F13)[1].F06, *(*eF13[0].F13)[1].F06
|
||
|
if sF132 == nil {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got nil", vals(eF132))
|
||
|
} else {
|
||
|
if len(sF132) != 2 || *(sF132)[0] != *(eF132)[0] || *(sF132)[1] != *(eF132)[1] {
|
||
|
t.Errorf("f13.0.f13.0.f6: expected %v, got %v", vals(eF132), vals(sF132))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S12A struct {
|
||
|
ID []int
|
||
|
}
|
||
|
|
||
|
func TestCSVSlice(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"ID": {"0,1"},
|
||
|
}
|
||
|
|
||
|
s := S12A{}
|
||
|
err := NewDecoder().Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if len(s.ID) != 2 {
|
||
|
t.Errorf("Expected two values in the result list, got %+v", s.ID)
|
||
|
}
|
||
|
if s.ID[0] != 0 || s.ID[1] != 1 {
|
||
|
t.Errorf("Expected []{0, 1} got %+v", s)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S12B struct {
|
||
|
ID []string
|
||
|
}
|
||
|
|
||
|
// Decode should not split on , into a slice for string only
|
||
|
func TestCSVStringSlice(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"ID": {"0,1"},
|
||
|
}
|
||
|
|
||
|
s := S12B{}
|
||
|
err := NewDecoder().Decode(&s, data)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if len(s.ID) != 1 {
|
||
|
t.Errorf("Expected one value in the result list, got %+v", s.ID)
|
||
|
}
|
||
|
if s.ID[0] != "0,1" {
|
||
|
t.Errorf("Expected []{0, 1} got %+v", s)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Invalid data provided by client should not panic (github issue 33)
|
||
|
func TestInvalidDataProvidedByClient(t *testing.T) {
|
||
|
defer func() {
|
||
|
if r := recover(); r != nil {
|
||
|
t.Errorf("Panicked calling decoder.Decode: %v", r)
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
type S struct {
|
||
|
f string // nolint:unused
|
||
|
}
|
||
|
|
||
|
data := map[string][]string{
|
||
|
"f.f": {"v"},
|
||
|
}
|
||
|
|
||
|
err := NewDecoder().Decode(new(S), data)
|
||
|
if err == nil {
|
||
|
t.Errorf("invalid path in decoder.Decode should return an error.")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// underlying cause of error in issue 33
|
||
|
func TestInvalidPathInCacheParsePath(t *testing.T) {
|
||
|
type S struct {
|
||
|
f string // nolint:unused
|
||
|
}
|
||
|
|
||
|
typ := reflect.ValueOf(new(S)).Elem().Type()
|
||
|
c := newCache()
|
||
|
_, err := c.parsePath("f.f", typ)
|
||
|
if err == nil {
|
||
|
t.Errorf("invalid path in cache.parsePath should return an error.")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// issue 32
|
||
|
func TestDecodeToTypedField(t *testing.T) {
|
||
|
type Aa bool
|
||
|
s1 := &struct{ Aa }{}
|
||
|
v1 := map[string][]string{"Aa": {"true"}}
|
||
|
err := NewDecoder().Decode(s1, v1)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
if s1.Aa != Aa(true) {
|
||
|
t.Errorf("s1: expected %v, got %v", true, s1.Aa)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// issue 37
|
||
|
func TestRegisterConverter(t *testing.T) {
|
||
|
type Aa int
|
||
|
type Bb int
|
||
|
s1 := &struct {
|
||
|
Aa
|
||
|
Bb
|
||
|
}{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
decoder.RegisterConverter(s1.Aa, func(s string) reflect.Value { return reflect.ValueOf(1) })
|
||
|
decoder.RegisterConverter(s1.Bb, func(s string) reflect.Value { return reflect.ValueOf(2) })
|
||
|
|
||
|
v1 := map[string][]string{"Aa": {"4"}, "Bb": {"5"}}
|
||
|
err := decoder.Decode(s1, v1)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
|
||
|
if s1.Aa != Aa(1) {
|
||
|
t.Errorf("s1.Aa: expected %v, got %v", 1, s1.Aa)
|
||
|
}
|
||
|
if s1.Bb != Bb(2) {
|
||
|
t.Errorf("s1.Bb: expected %v, got %v", 2, s1.Bb)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Issue #40
|
||
|
func TestRegisterConverterSlice(t *testing.T) {
|
||
|
decoder := NewDecoder()
|
||
|
decoder.RegisterConverter([]string{}, func(input string) reflect.Value {
|
||
|
return reflect.ValueOf(strings.Split(input, ","))
|
||
|
})
|
||
|
|
||
|
result := struct {
|
||
|
Multiple []string `schema:"multiple"`
|
||
|
}{}
|
||
|
|
||
|
expected := []string{"one", "two", "three"}
|
||
|
err := decoder.Decode(&result, map[string][]string{
|
||
|
"multiple": []string{"one,two,three"},
|
||
|
})
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
for i := range expected {
|
||
|
if got, want := expected[i], result.Multiple[i]; got != want {
|
||
|
t.Errorf("%d: got %s, want %s", i, got, want)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRegisterConverterMap(t *testing.T) {
|
||
|
decoder := NewDecoder()
|
||
|
decoder.IgnoreUnknownKeys(false)
|
||
|
decoder.RegisterConverter(map[string]string{}, func(input string) reflect.Value {
|
||
|
m := make(map[string]string)
|
||
|
for _, pair := range strings.Split(input, ",") {
|
||
|
parts := strings.Split(pair, ":")
|
||
|
switch len(parts) {
|
||
|
case 2:
|
||
|
m[parts[0]] = parts[1]
|
||
|
}
|
||
|
}
|
||
|
return reflect.ValueOf(m)
|
||
|
})
|
||
|
|
||
|
result := struct {
|
||
|
Multiple map[string]string `schema:"multiple"`
|
||
|
}{}
|
||
|
|
||
|
err := decoder.Decode(&result, map[string][]string{
|
||
|
"multiple": []string{"a:one,b:two"},
|
||
|
})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expected := map[string]string{"a": "one", "b": "two"}
|
||
|
for k, v := range expected {
|
||
|
got, ok := result.Multiple[k]
|
||
|
if !ok {
|
||
|
t.Fatalf("got %v, want %v", result.Multiple, expected)
|
||
|
}
|
||
|
if got != v {
|
||
|
t.Errorf("got %s, want %s", got, v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S13 struct {
|
||
|
Value []S14
|
||
|
}
|
||
|
|
||
|
type S14 struct {
|
||
|
F1 string
|
||
|
F2 string
|
||
|
}
|
||
|
|
||
|
func (n *S14) UnmarshalText(text []byte) error {
|
||
|
textParts := strings.Split(string(text), " ")
|
||
|
if len(textParts) < 2 {
|
||
|
return errors.New("Not a valid name!")
|
||
|
}
|
||
|
|
||
|
n.F1, n.F2 = textParts[0], textParts[len(textParts)-1]
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type S15 struct {
|
||
|
Value []S16
|
||
|
}
|
||
|
|
||
|
type S16 struct {
|
||
|
F1 string
|
||
|
F2 string
|
||
|
}
|
||
|
|
||
|
func TestCustomTypeSlice(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value.0": []string{"Louisa May Alcott"},
|
||
|
"Value.1": []string{"Florence Nightingale"},
|
||
|
"Value.2": []string{"Clara Barton"},
|
||
|
}
|
||
|
|
||
|
s := S13{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if len(s.Value) != 3 {
|
||
|
t.Fatalf("Expected 3 values in the result list, got %+v", s.Value)
|
||
|
}
|
||
|
if s.Value[0].F1 != "Louisa" || s.Value[0].F2 != "Alcott" {
|
||
|
t.Errorf("Expected S14{'Louisa', 'Alcott'} got %+v", s.Value[0])
|
||
|
}
|
||
|
if s.Value[1].F1 != "Florence" || s.Value[1].F2 != "Nightingale" {
|
||
|
t.Errorf("Expected S14{'Florence', 'Nightingale'} got %+v", s.Value[1])
|
||
|
}
|
||
|
if s.Value[2].F1 != "Clara" || s.Value[2].F2 != "Barton" {
|
||
|
t.Errorf("Expected S14{'Clara', 'Barton'} got %+v", s.Value[2])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCustomTypeSliceWithError(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value.0": []string{"Louisa May Alcott"},
|
||
|
"Value.1": []string{"Florence Nightingale"},
|
||
|
"Value.2": []string{"Clara"},
|
||
|
}
|
||
|
|
||
|
s := S13{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err == nil {
|
||
|
t.Error("Not detecting error in conversion")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNoTextUnmarshalerTypeSlice(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value.0": []string{"Louisa May Alcott"},
|
||
|
"Value.1": []string{"Florence Nightingale"},
|
||
|
"Value.2": []string{"Clara Barton"},
|
||
|
}
|
||
|
|
||
|
s := S15{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err == nil {
|
||
|
t.Error("Not detecting when there's no converter")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
type S17 struct {
|
||
|
Value S14
|
||
|
}
|
||
|
|
||
|
type S18 struct {
|
||
|
Value S16
|
||
|
}
|
||
|
|
||
|
func TestCustomType(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value": []string{"Louisa May Alcott"},
|
||
|
}
|
||
|
|
||
|
s := S17{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if s.Value.F1 != "Louisa" || s.Value.F2 != "Alcott" {
|
||
|
t.Errorf("Expected S14{'Louisa', 'Alcott'} got %+v", s.Value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCustomTypeWithError(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value": []string{"Louisa"},
|
||
|
}
|
||
|
|
||
|
s := S17{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err == nil {
|
||
|
t.Error("Not detecting error in conversion")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNoTextUnmarshalerType(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value": []string{"Louisa May Alcott"},
|
||
|
}
|
||
|
|
||
|
s := S18{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err == nil {
|
||
|
t.Error("Not detecting when there's no converter")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestExpectedType(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"bools": []string{"1", "a"},
|
||
|
"date": []string{"invalid"},
|
||
|
"Foo.Bar": []string{"a", "b"},
|
||
|
}
|
||
|
|
||
|
type B struct {
|
||
|
Bar *int
|
||
|
}
|
||
|
type A struct {
|
||
|
Bools []bool `schema:"bools"`
|
||
|
Date time.Time `schema:"date"`
|
||
|
Foo B
|
||
|
}
|
||
|
|
||
|
a := A{}
|
||
|
|
||
|
err := NewDecoder().Decode(&a, data)
|
||
|
|
||
|
e := err.(MultiError)["bools"].(ConversionError)
|
||
|
if e.Type != reflect.TypeOf(false) && e.Index == 1 {
|
||
|
t.Errorf("Expected bool, index: 1 got %+v, index: %d", e.Type, e.Index)
|
||
|
}
|
||
|
e = err.(MultiError)["date"].(ConversionError)
|
||
|
if e.Type != reflect.TypeOf(time.Time{}) {
|
||
|
t.Errorf("Expected time.Time got %+v", e.Type)
|
||
|
}
|
||
|
e = err.(MultiError)["Foo.Bar"].(ConversionError)
|
||
|
if e.Type != reflect.TypeOf(0) {
|
||
|
t.Errorf("Expected int got %+v", e.Type)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type R1 struct {
|
||
|
A string `schema:"a,required"`
|
||
|
B struct {
|
||
|
C int `schema:"c,required"`
|
||
|
D float64 `schema:"d"`
|
||
|
E string `schema:"e,required"`
|
||
|
} `schema:"b"`
|
||
|
F []string `schema:"f,required"`
|
||
|
G []int `schema:"g,othertag"`
|
||
|
H bool `schema:"h,required"`
|
||
|
}
|
||
|
|
||
|
func TestRequiredField(t *testing.T) {
|
||
|
var a R1
|
||
|
v := map[string][]string{
|
||
|
"a": []string{"bbb"},
|
||
|
"b.c": []string{"88"},
|
||
|
"b.d": []string{"9"},
|
||
|
"f": []string{""},
|
||
|
"h": []string{"true"},
|
||
|
}
|
||
|
err := NewDecoder().Decode(&a, v)
|
||
|
if err == nil {
|
||
|
t.Errorf("error nil, b.e is empty expect")
|
||
|
return
|
||
|
}
|
||
|
// b.e empty
|
||
|
v["b.e"] = []string{""} // empty string
|
||
|
err = NewDecoder().Decode(&a, v)
|
||
|
if err == nil {
|
||
|
t.Errorf("error nil, b.e is empty expect")
|
||
|
return
|
||
|
}
|
||
|
if expected := `b.e is empty`; err.Error() != expected {
|
||
|
t.Errorf("got %q, want %q", err, expected)
|
||
|
}
|
||
|
|
||
|
// all fields ok
|
||
|
v["b.e"] = []string{"nonempty"}
|
||
|
err = NewDecoder().Decode(&a, v)
|
||
|
if err != nil {
|
||
|
t.Errorf("error: %v", err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// set f empty
|
||
|
v["f"] = []string{}
|
||
|
err = NewDecoder().Decode(&a, v)
|
||
|
if err == nil {
|
||
|
t.Errorf("error nil, f is empty expect")
|
||
|
return
|
||
|
}
|
||
|
if expected := `f is empty`; err.Error() != expected {
|
||
|
t.Errorf("got %q, want %q", err, expected)
|
||
|
}
|
||
|
v["f"] = []string{"nonempty"}
|
||
|
|
||
|
// b.c type int with empty string
|
||
|
v["b.c"] = []string{""}
|
||
|
err = NewDecoder().Decode(&a, v)
|
||
|
if err == nil {
|
||
|
t.Errorf("error nil, b.c is empty expect")
|
||
|
return
|
||
|
}
|
||
|
v["b.c"] = []string{"3"}
|
||
|
|
||
|
// h type bool with empty string
|
||
|
v["h"] = []string{""}
|
||
|
err = NewDecoder().Decode(&a, v)
|
||
|
if err == nil {
|
||
|
t.Errorf("error nil, h is empty expect")
|
||
|
return
|
||
|
}
|
||
|
if expected := `h is empty`; err.Error() != expected {
|
||
|
t.Errorf("got %q, want %q", err, expected)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type R2 struct {
|
||
|
A struct {
|
||
|
B int `schema:"b"`
|
||
|
} `schema:"a,required"`
|
||
|
}
|
||
|
|
||
|
func TestRequiredStructFiled(t *testing.T) {
|
||
|
v := map[string][]string{
|
||
|
"a.b": []string{"3"},
|
||
|
}
|
||
|
var a R2
|
||
|
err := NewDecoder().Decode(&a, v)
|
||
|
if err != nil {
|
||
|
t.Errorf("error: %v", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRequiredFieldIsMissingCorrectError(t *testing.T) {
|
||
|
type RM1S struct {
|
||
|
A string `schema:"rm1aa,required"`
|
||
|
B string `schema:"rm1bb,required"`
|
||
|
}
|
||
|
type RM1 struct {
|
||
|
RM1S
|
||
|
}
|
||
|
|
||
|
var a RM1
|
||
|
v := map[string][]string{
|
||
|
"rm1aa": {"aaa"},
|
||
|
}
|
||
|
expectedError := "RM1S.rm1bb is empty"
|
||
|
err := NewDecoder().Decode(&a, v)
|
||
|
if err.Error() != expectedError {
|
||
|
t.Errorf("expected %v, got %v", expectedError, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type AS1 struct {
|
||
|
A int32 `schema:"a,required"`
|
||
|
E int32 `schema:"e,required"`
|
||
|
}
|
||
|
type AS2 struct {
|
||
|
AS1
|
||
|
B string `schema:"b,required"`
|
||
|
}
|
||
|
type AS3 struct {
|
||
|
C int32 `schema:"c"`
|
||
|
}
|
||
|
|
||
|
type AS4 struct {
|
||
|
AS3
|
||
|
D string `schema:"d"`
|
||
|
}
|
||
|
|
||
|
func TestAnonymousStructField(t *testing.T) {
|
||
|
patterns := []map[string][]string{
|
||
|
{
|
||
|
"a": {"1"},
|
||
|
"e": {"2"},
|
||
|
"b": {"abc"},
|
||
|
},
|
||
|
{
|
||
|
"AS1.a": {"1"},
|
||
|
"AS1.e": {"2"},
|
||
|
"b": {"abc"},
|
||
|
},
|
||
|
}
|
||
|
for _, v := range patterns {
|
||
|
a := AS2{}
|
||
|
err := NewDecoder().Decode(&a, v)
|
||
|
if err != nil {
|
||
|
t.Errorf("Decode failed %s, %#v", err, v)
|
||
|
continue
|
||
|
}
|
||
|
if a.A != 1 {
|
||
|
t.Errorf("A: expected %v, got %v", 1, a.A)
|
||
|
}
|
||
|
if a.E != 2 {
|
||
|
t.Errorf("E: expected %v, got %v", 2, a.E)
|
||
|
}
|
||
|
if a.B != "abc" {
|
||
|
t.Errorf("B: expected %v, got %v", "abc", a.B)
|
||
|
}
|
||
|
if a.AS1.A != 1 {
|
||
|
t.Errorf("AS1.A: expected %v, got %v", 1, a.AS1.A)
|
||
|
}
|
||
|
if a.AS1.E != 2 {
|
||
|
t.Errorf("AS1.E: expected %v, got %v", 2, a.AS1.E)
|
||
|
}
|
||
|
}
|
||
|
a := AS2{}
|
||
|
err := NewDecoder().Decode(&a, map[string][]string{
|
||
|
"e": {"2"},
|
||
|
"b": {"abc"},
|
||
|
})
|
||
|
if err == nil {
|
||
|
t.Errorf("error nil, a is empty expect")
|
||
|
}
|
||
|
patterns = []map[string][]string{
|
||
|
{
|
||
|
"c": {"1"},
|
||
|
"d": {"abc"},
|
||
|
},
|
||
|
{
|
||
|
"AS3.c": {"1"},
|
||
|
"d": {"abc"},
|
||
|
},
|
||
|
}
|
||
|
for _, v := range patterns {
|
||
|
a := AS4{}
|
||
|
err := NewDecoder().Decode(&a, v)
|
||
|
if err != nil {
|
||
|
t.Errorf("Decode failed %s, %#v", err, v)
|
||
|
continue
|
||
|
}
|
||
|
if a.C != 1 {
|
||
|
t.Errorf("C: expected %v, got %v", 1, a.C)
|
||
|
}
|
||
|
if a.D != "abc" {
|
||
|
t.Errorf("D: expected %v, got %v", "abc", a.D)
|
||
|
}
|
||
|
if a.AS3.C != 1 {
|
||
|
t.Errorf("AS3.C: expected %v, got %v", 1, a.AS3.C)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAmbiguousStructField(t *testing.T) {
|
||
|
type I1 struct {
|
||
|
X int
|
||
|
}
|
||
|
type I2 struct {
|
||
|
I1
|
||
|
}
|
||
|
type B1 struct {
|
||
|
X bool
|
||
|
}
|
||
|
type B2 struct {
|
||
|
B1
|
||
|
}
|
||
|
type IB struct {
|
||
|
I1
|
||
|
B1
|
||
|
}
|
||
|
type S struct {
|
||
|
I1
|
||
|
I2
|
||
|
B1
|
||
|
B2
|
||
|
IB
|
||
|
}
|
||
|
dst := S{}
|
||
|
src := map[string][]string{
|
||
|
"X": {"123"},
|
||
|
"IB.X": {"123"},
|
||
|
}
|
||
|
dec := NewDecoder()
|
||
|
dec.IgnoreUnknownKeys(false)
|
||
|
err := dec.Decode(&dst, src)
|
||
|
e, ok := err.(MultiError)
|
||
|
if !ok || len(e) != 2 {
|
||
|
t.Errorf("Expected 2 errors, got %#v", err)
|
||
|
}
|
||
|
if expected := (UnknownKeyError{Key: "X"}); e["X"] != expected {
|
||
|
t.Errorf("X: expected %#v, got %#v", expected, e["X"])
|
||
|
}
|
||
|
if expected := (UnknownKeyError{Key: "IB.X"}); e["IB.X"] != expected {
|
||
|
t.Errorf("X: expected %#v, got %#v", expected, e["IB.X"])
|
||
|
}
|
||
|
dec.IgnoreUnknownKeys(true)
|
||
|
err = dec.Decode(&dst, src)
|
||
|
if err != nil {
|
||
|
t.Errorf("Decode failed %v", err)
|
||
|
}
|
||
|
|
||
|
expected := S{
|
||
|
I1: I1{X: 123},
|
||
|
I2: I2{I1: I1{X: 234}},
|
||
|
B1: B1{X: true},
|
||
|
B2: B2{B1: B1{X: true}},
|
||
|
IB: IB{I1: I1{X: 345}, B1: B1{X: true}},
|
||
|
}
|
||
|
patterns := []map[string][]string{
|
||
|
{
|
||
|
"I1.X": {"123"},
|
||
|
"I2.X": {"234"},
|
||
|
"B1.X": {"true"},
|
||
|
"B2.X": {"1"},
|
||
|
"IB.I1.X": {"345"},
|
||
|
"IB.B1.X": {"on"},
|
||
|
},
|
||
|
{
|
||
|
"I1.X": {"123"},
|
||
|
"I2.I1.X": {"234"},
|
||
|
"B1.X": {"true"},
|
||
|
"B2.B1.X": {"1"},
|
||
|
"IB.I1.X": {"345"},
|
||
|
"IB.B1.X": {"on"},
|
||
|
},
|
||
|
}
|
||
|
for _, src := range patterns {
|
||
|
dst := S{}
|
||
|
dec := NewDecoder()
|
||
|
dec.IgnoreUnknownKeys(false)
|
||
|
err := dec.Decode(&dst, src)
|
||
|
if err != nil {
|
||
|
t.Errorf("Decode failed %v, %#v", err, src)
|
||
|
}
|
||
|
if !reflect.DeepEqual(expected, dst) {
|
||
|
t.Errorf("Expected %+v, got %+v", expected, dst)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestComprehensiveDecodingErrors(t *testing.T) {
|
||
|
type I1 struct {
|
||
|
V int `schema:",required"`
|
||
|
P *int `schema:",required"`
|
||
|
}
|
||
|
type I2 struct {
|
||
|
I1
|
||
|
J I1
|
||
|
}
|
||
|
type S1 struct {
|
||
|
V string `schema:"v,required"`
|
||
|
P *string `schema:"p,required"`
|
||
|
}
|
||
|
type S2 struct {
|
||
|
S1 `schema:"s"`
|
||
|
T S1 `schema:"t"`
|
||
|
}
|
||
|
type D struct {
|
||
|
I2
|
||
|
X S2 `schema:"x"`
|
||
|
Y S2 `schema:"-"`
|
||
|
}
|
||
|
patterns := []map[string][]string{
|
||
|
{
|
||
|
"V": {"invalid"}, // invalid
|
||
|
"I2.I1.P": {}, // empty
|
||
|
"I2.J.V": {""}, // empty
|
||
|
"I2.J.P": {"123"}, // ok
|
||
|
"x.s.v": {""}, // empty
|
||
|
"x.s.p": {""}, // ok
|
||
|
"x.t.v": {"abc"}, // ok
|
||
|
"x.t.p": {}, // empty
|
||
|
"Y.s.v": {"ignored"}, // unknown
|
||
|
},
|
||
|
{
|
||
|
"V": {"invalid"}, // invalid
|
||
|
"P": {}, // empty
|
||
|
"J.V": {""}, // empty
|
||
|
"J.P": {"123"}, // ok
|
||
|
"x.v": {""}, // empty
|
||
|
"x.p": {""}, // ok
|
||
|
"x.t.v": {"abc"}, // ok
|
||
|
"x.t.p": {}, // empty
|
||
|
"Y.s.v": {"ignored"}, // unknown
|
||
|
},
|
||
|
}
|
||
|
for _, src := range patterns {
|
||
|
dst := D{}
|
||
|
dec := NewDecoder()
|
||
|
dec.IgnoreUnknownKeys(false)
|
||
|
err := dec.Decode(&dst, src)
|
||
|
e, ok := err.(MultiError)
|
||
|
if !ok || len(e) != 6 {
|
||
|
t.Errorf("Expected 6 errors, got %#v", err)
|
||
|
}
|
||
|
if cerr, ok := e["V"].(ConversionError); !ok {
|
||
|
t.Errorf("%s: expected %#v, got %#v", "I2.I1.V", ConversionError{Key: "V"}, cerr)
|
||
|
}
|
||
|
if key, expected := "I2.I1.P", (EmptyFieldError{Key: "I2.I1.P"}); e[key] != expected {
|
||
|
t.Errorf("%s: expected %#v, got %#v", key, expected, e[key])
|
||
|
}
|
||
|
if key, expected := "I2.J.V", (EmptyFieldError{Key: "I2.J.V"}); e[key] != expected {
|
||
|
t.Errorf("%s: expected %#v, got %#v", key, expected, e[key])
|
||
|
}
|
||
|
if key, expected := "x.s.v", (EmptyFieldError{Key: "x.s.v"}); e[key] != expected {
|
||
|
t.Errorf("%s: expected %#v, got %#v", key, expected, e[key])
|
||
|
}
|
||
|
if key, expected := "x.t.p", (EmptyFieldError{Key: "x.t.p"}); e[key] != expected {
|
||
|
t.Errorf("%s: expected %#v, got %#v", key, expected, e[key])
|
||
|
}
|
||
|
if key, expected := "Y.s.v", (UnknownKeyError{Key: "Y.s.v"}); e[key] != expected {
|
||
|
t.Errorf("%s: expected %#v, got %#v", key, expected, e[key])
|
||
|
}
|
||
|
if expected := 123; dst.I2.J.P == nil || *dst.I2.J.P != expected {
|
||
|
t.Errorf("I2.J.P: expected %#v, got %#v", expected, dst.I2.J.P)
|
||
|
}
|
||
|
if expected := ""; dst.X.S1.P == nil || *dst.X.S1.P != expected {
|
||
|
t.Errorf("X.S1.P: expected %#v, got %#v", expected, dst.X.S1.P)
|
||
|
}
|
||
|
if expected := "abc"; dst.X.T.V != expected {
|
||
|
t.Errorf("X.T.V: expected %#v, got %#v", expected, dst.X.T.V)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Test to ensure that a registered converter overrides the default text unmarshaler.
|
||
|
func TestRegisterConverterOverridesTextUnmarshaler(t *testing.T) {
|
||
|
type MyTime time.Time
|
||
|
s1 := &struct {
|
||
|
MyTime
|
||
|
}{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
ts := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||
|
decoder.RegisterConverter(s1.MyTime, func(s string) reflect.Value { return reflect.ValueOf(ts) })
|
||
|
|
||
|
v1 := map[string][]string{"MyTime": {"4"}}
|
||
|
err := decoder.Decode(s1, v1)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed to decode: %v", err)
|
||
|
}
|
||
|
|
||
|
if s1.MyTime != MyTime(ts) {
|
||
|
t.Errorf("s1.Aa: expected %v, got %v", ts, s1.MyTime)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S20E string
|
||
|
|
||
|
func (e *S20E) UnmarshalText(text []byte) error {
|
||
|
*e = S20E("x")
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type S20 []S20E
|
||
|
|
||
|
func (s *S20) UnmarshalText(text []byte) error {
|
||
|
*s = S20{"a", "b", "c"}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Test to ensure that when a custom type based on a slice implements an
|
||
|
// encoding.TextUnmarshaler interface that it takes precedence over any
|
||
|
// implementations by its elements.
|
||
|
func TestTextUnmarshalerTypeSlice(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value": []string{"a,b,c"},
|
||
|
}
|
||
|
s := struct {
|
||
|
Value S20
|
||
|
}{}
|
||
|
decoder := NewDecoder()
|
||
|
if err := decoder.Decode(&s, data); err != nil {
|
||
|
t.Fatal("Error while decoding:", err)
|
||
|
}
|
||
|
expected := S20{"a", "b", "c"}
|
||
|
if !reflect.DeepEqual(expected, s.Value) {
|
||
|
t.Errorf("Expected %v errors, got %v", expected, s.Value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S21E struct{ ElementValue string }
|
||
|
|
||
|
func (e *S21E) UnmarshalText(text []byte) error {
|
||
|
*e = S21E{"x"}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type S21 []S21E
|
||
|
|
||
|
func (s *S21) UnmarshalText(text []byte) error {
|
||
|
*s = S21{{"a"}}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type S21B []S21E
|
||
|
|
||
|
// Test to ensure that if custom type base on a slice of structs implements an
|
||
|
// encoding.TextUnmarshaler interface it is unaffected by the special path
|
||
|
// requirements imposed on a slice of structs.
|
||
|
func TestTextUnmarshalerTypeSliceOfStructs(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value": []string{"raw a"},
|
||
|
}
|
||
|
// Implements encoding.TextUnmarshaler, should not throw invalid path
|
||
|
// error.
|
||
|
s := struct {
|
||
|
Value S21
|
||
|
}{}
|
||
|
decoder := NewDecoder()
|
||
|
if err := decoder.Decode(&s, data); err != nil {
|
||
|
t.Fatal("Error while decoding:", err)
|
||
|
}
|
||
|
expected := S21{{"a"}}
|
||
|
if !reflect.DeepEqual(expected, s.Value) {
|
||
|
t.Errorf("Expected %v errors, got %v", expected, s.Value)
|
||
|
}
|
||
|
// Does not implement encoding.TextUnmarshaler, should throw invalid
|
||
|
// path error.
|
||
|
sb := struct {
|
||
|
Value S21B
|
||
|
}{}
|
||
|
if err := decoder.Decode(&sb, data); err == errInvalidPath {
|
||
|
t.Fatal("Expecting invalid path error", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S22 string
|
||
|
|
||
|
func (s *S22) UnmarshalText(text []byte) error {
|
||
|
*s = S22("a")
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Test to ensure that when a field that should be decoded into a type
|
||
|
// implementing the encoding.TextUnmarshaler interface is set to an empty value
|
||
|
// that the UnmarshalText method is utilized over other methods of decoding,
|
||
|
// especially including simply setting the zero value.
|
||
|
func TestTextUnmarshalerEmpty(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"Value": []string{""}, // empty value
|
||
|
}
|
||
|
// Implements encoding.TextUnmarshaler, should use the type's
|
||
|
// UnmarshalText method.
|
||
|
s := struct {
|
||
|
Value S22
|
||
|
}{}
|
||
|
decoder := NewDecoder()
|
||
|
if err := decoder.Decode(&s, data); err != nil {
|
||
|
t.Fatal("Error while decoding:", err)
|
||
|
}
|
||
|
expected := S22("a")
|
||
|
if expected != s.Value {
|
||
|
t.Errorf("Expected %v errors, got %v", expected, s.Value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type S23n struct {
|
||
|
F2 string `schema:"F2"`
|
||
|
F3 string `schema:"F3"`
|
||
|
}
|
||
|
|
||
|
type S23e struct {
|
||
|
*S23n
|
||
|
F1 string `schema:"F1"`
|
||
|
}
|
||
|
|
||
|
type S23 []*S23e
|
||
|
|
||
|
func TestUnmashalPointerToEmbedded(t *testing.T) {
|
||
|
data := map[string][]string{
|
||
|
"A.0.F2": []string{"raw a"},
|
||
|
"A.0.F3": []string{"raw b"},
|
||
|
}
|
||
|
|
||
|
// Implements encoding.TextUnmarshaler, should not throw invalid path
|
||
|
// error.
|
||
|
s := struct {
|
||
|
Value S23 `schema:"A"`
|
||
|
}{}
|
||
|
decoder := NewDecoder()
|
||
|
|
||
|
if err := decoder.Decode(&s, data); err != nil {
|
||
|
t.Fatal("Error while decoding:", err)
|
||
|
}
|
||
|
|
||
|
expected := S23{
|
||
|
&S23e{
|
||
|
S23n: &S23n{"raw a", "raw b"},
|
||
|
},
|
||
|
}
|
||
|
if !reflect.DeepEqual(expected, s.Value) {
|
||
|
t.Errorf("Expected %v errors, got %v", expected, s.Value)
|
||
|
}
|
||
|
}
|