// Copyright 2017 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package properties import ( "reflect" "testing" "time" ) func TestDecodeValues(t *testing.T) { type S struct { S string BT bool BF bool I int I8 int8 I16 int16 I32 int32 I64 int64 U uint U8 uint8 U16 uint16 U32 uint32 U64 uint64 F32 float32 F64 float64 D time.Duration TM time.Time } in := ` S=abc BT=true BF=false I=-1 I8=-8 I16=-16 I32=-32 I64=-64 U=1 U8=8 U16=16 U32=32 U64=64 F32=3.2 F64=6.4 D=5s TM=2015-01-02T12:34:56Z ` out := &S{ S: "abc", BT: true, BF: false, I: -1, I8: -8, I16: -16, I32: -32, I64: -64, U: 1, U8: 8, U16: 16, U32: 32, U64: 64, F32: 3.2, F64: 6.4, D: 5 * time.Second, TM: tm(t, time.RFC3339, "2015-01-02T12:34:56Z"), } testDecode(t, in, &S{}, out) } func TestDecodeValueDefaults(t *testing.T) { type S struct { S string `properties:",default=abc"` BT bool `properties:",default=true"` BF bool `properties:",default=false"` I int `properties:",default=-1"` I8 int8 `properties:",default=-8"` I16 int16 `properties:",default=-16"` I32 int32 `properties:",default=-32"` I64 int64 `properties:",default=-64"` U uint `properties:",default=1"` U8 uint8 `properties:",default=8"` U16 uint16 `properties:",default=16"` U32 uint32 `properties:",default=32"` U64 uint64 `properties:",default=64"` F32 float32 `properties:",default=3.2"` F64 float64 `properties:",default=6.4"` D time.Duration `properties:",default=5s"` TM time.Time `properties:",default=2015-01-02T12:34:56Z"` } out := &S{ S: "abc", BT: true, BF: false, I: -1, I8: -8, I16: -16, I32: -32, I64: -64, U: 1, U8: 8, U16: 16, U32: 32, U64: 64, F32: 3.2, F64: 6.4, D: 5 * time.Second, TM: tm(t, time.RFC3339, "2015-01-02T12:34:56Z"), } testDecode(t, "", &S{}, out) } func TestDecodeArrays(t *testing.T) { type S struct { S []string B []bool I []int I8 []int8 I16 []int16 I32 []int32 I64 []int64 U []uint U8 []uint8 U16 []uint16 U32 []uint32 U64 []uint64 F32 []float32 F64 []float64 D []time.Duration TM []time.Time } in := ` S=a;b B=true;false I=-1;-2 I8=-8;-9 I16=-16;-17 I32=-32;-33 I64=-64;-65 U=1;2 U8=8;9 U16=16;17 U32=32;33 U64=64;65 F32=3.2;3.3 F64=6.4;6.5 D=4s;5s TM=2015-01-01T00:00:00Z;2016-01-01T00:00:00Z ` out := &S{ S: []string{"a", "b"}, B: []bool{true, false}, I: []int{-1, -2}, I8: []int8{-8, -9}, I16: []int16{-16, -17}, I32: []int32{-32, -33}, I64: []int64{-64, -65}, U: []uint{1, 2}, U8: []uint8{8, 9}, U16: []uint16{16, 17}, U32: []uint32{32, 33}, U64: []uint64{64, 65}, F32: []float32{3.2, 3.3}, F64: []float64{6.4, 6.5}, D: []time.Duration{4 * time.Second, 5 * time.Second}, TM: []time.Time{tm(t, time.RFC3339, "2015-01-01T00:00:00Z"), tm(t, time.RFC3339, "2016-01-01T00:00:00Z")}, } testDecode(t, in, &S{}, out) } func TestDecodeArrayDefaults(t *testing.T) { type S struct { S []string `properties:",default=a;b"` B []bool `properties:",default=true;false"` I []int `properties:",default=-1;-2"` I8 []int8 `properties:",default=-8;-9"` I16 []int16 `properties:",default=-16;-17"` I32 []int32 `properties:",default=-32;-33"` I64 []int64 `properties:",default=-64;-65"` U []uint `properties:",default=1;2"` U8 []uint8 `properties:",default=8;9"` U16 []uint16 `properties:",default=16;17"` U32 []uint32 `properties:",default=32;33"` U64 []uint64 `properties:",default=64;65"` F32 []float32 `properties:",default=3.2;3.3"` F64 []float64 `properties:",default=6.4;6.5"` D []time.Duration `properties:",default=4s;5s"` TM []time.Time `properties:",default=2015-01-01T00:00:00Z;2016-01-01T00:00:00Z"` } out := &S{ S: []string{"a", "b"}, B: []bool{true, false}, I: []int{-1, -2}, I8: []int8{-8, -9}, I16: []int16{-16, -17}, I32: []int32{-32, -33}, I64: []int64{-64, -65}, U: []uint{1, 2}, U8: []uint8{8, 9}, U16: []uint16{16, 17}, U32: []uint32{32, 33}, U64: []uint64{64, 65}, F32: []float32{3.2, 3.3}, F64: []float64{6.4, 6.5}, D: []time.Duration{4 * time.Second, 5 * time.Second}, TM: []time.Time{tm(t, time.RFC3339, "2015-01-01T00:00:00Z"), tm(t, time.RFC3339, "2016-01-01T00:00:00Z")}, } testDecode(t, "", &S{}, out) } func TestDecodeSkipUndef(t *testing.T) { type S struct { X string `properties:"-"` Undef string `properties:",default=some value"` } in := `X=ignore` out := &S{"", "some value"} testDecode(t, in, &S{}, out) } func TestDecodeStruct(t *testing.T) { type A struct { S string T string `properties:"t"` U string `properties:"u,default=uuu"` } type S struct { A A B A `properties:"b"` } in := ` A.S=sss A.t=ttt b.S=SSS b.t=TTT ` out := &S{ A{S: "sss", T: "ttt", U: "uuu"}, A{S: "SSS", T: "TTT", U: "uuu"}, } testDecode(t, in, &S{}, out) } func TestDecodeMap(t *testing.T) { type S struct { A string `properties:"a"` } type X struct { A map[string]string B map[string][]string C map[string]map[string]string D map[string]S E map[string]int F map[string]int `properties:"-"` } in := ` A.foo=bar A.bar=bang B.foo=a;b;c B.bar=1;2;3 C.foo.one=1 C.foo.two=2 C.bar.three=3 C.bar.four=4 D.foo.a=bar ` out := &X{ A: map[string]string{"foo": "bar", "bar": "bang"}, B: map[string][]string{"foo": []string{"a", "b", "c"}, "bar": []string{"1", "2", "3"}}, C: map[string]map[string]string{"foo": map[string]string{"one": "1", "two": "2"}, "bar": map[string]string{"three": "3", "four": "4"}}, D: map[string]S{"foo": S{"bar"}}, E: map[string]int{}, } testDecode(t, in, &X{}, out) } func testDecode(t *testing.T, in string, v, out interface{}) { p, err := parse(in) if err != nil { t.Fatalf("got %v want nil", err) } if err := p.Decode(v); err != nil { t.Fatalf("got %v want nil", err) } if got, want := v, out; !reflect.DeepEqual(got, want) { t.Fatalf("\ngot %+v\nwant %+v", got, want) } } func tm(t *testing.T, layout, s string) time.Time { tm, err := time.Parse(layout, s) if err != nil { t.Fatalf("got %v want nil", err) } return tm }