// Copyright 2016 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package config import ( "encoding/json" "fmt" "reflect" "testing" "time" ) func TestLoad(t *testing.T) { type NestedNested struct { Ccc int Ddd string } type Nested struct { Aaa int Bbb string More NestedNested } type Config struct { Foo int Bar string Baz string `json:"-"` Raw json.RawMessage Qux []string Box Nested Boq *Nested Arr []Nested T time.Time } tests := []struct { input string output Config err string }{ { `{"foo": 42}`, Config{ Foo: 42, }, "", }, { `{"BAR": "Baz", "foo": 42}`, Config{ Foo: 42, Bar: "Baz", }, "", }, { `{"foobar": 42}`, Config{}, "unknown field 'foobar' in config", }, { `{"foo": 1, "baz": "baz", "bar": "bar"}`, Config{}, "unknown field 'baz' in config", }, { `{"foo": 1, "box": {"aaa": 12, "bbb": "bbb"}}`, Config{ Foo: 1, Box: Nested{ Aaa: 12, Bbb: "bbb", }, }, "", }, { `{"qux": ["aaa", "bbb"]}`, Config{ Qux: []string{"aaa", "bbb"}, }, "", }, { `{"box": {"aaa": 12, "ccc": "bbb"}}`, Config{}, "unknown field 'box.ccc' in config", }, { `{"foo": 1, "boq": {"aaa": 12, "bbb": "bbb"}}`, Config{ Foo: 1, Boq: &Nested{ Aaa: 12, Bbb: "bbb", }, }, "", }, { `{"boq": {"aaa": 12, "ccc": "bbb"}}`, Config{}, "unknown field 'boq.ccc' in config", }, { `{"foo": 1, "arr": []}`, Config{ Foo: 1, Arr: []Nested{}, }, "", }, { `{"foo": 1, "arr": [{"aaa": 12, "bbb": "bbb"}, {"aaa": 13, "bbb": "ccc"}]}`, Config{ Foo: 1, Arr: []Nested{ { Aaa: 12, Bbb: "bbb", }, { Aaa: 13, Bbb: "ccc", }, }, }, "", }, { `{"arr": [{"aaa": 12, "ccc": "bbb"}]}`, Config{}, "unknown field 'arr[0].ccc' in config", }, { `{"foo": 1, "boq": {"aaa": 12, "more": {"ccc": 13, "ddd": "ddd"}}}`, Config{ Foo: 1, Boq: &Nested{ Aaa: 12, More: NestedNested{ Ccc: 13, Ddd: "ddd", }, }, }, "", }, { `{"foo": 1, "boq": {"aaa": 12, "more": {"ccc": 13, "eee": "eee"}}}`, Config{}, "unknown field 'boq.more.eee' in config", }, { `{"raw": {"zux": 11}}`, Config{ Raw: []byte(`{"zux": 11}`), }, "", }, { `{"foo": null, "qux": null}`, Config{}, "", }, { `{"t": "2000-01-02T03:04:05Z"}`, Config{ T: time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), }, "", }, } for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { var cfg Config err := LoadData([]byte(test.input), &cfg) errStr := "" if err != nil { errStr = err.Error() } if test.err != errStr { t.Fatalf("bad err: want '%v', got '%v'", test.err, errStr) } if !reflect.DeepEqual(test.output, cfg) { t.Fatalf("bad output: want:\n%#v\n, got:\n%#v", test.output, cfg) } }) } } func TestLoadBadType(t *testing.T) { want := "config type is not pointer to struct" if err := LoadData([]byte("{}"), 1); err == nil || err.Error() != want { t.Fatalf("got '%v', want '%v'", err, want) } i := 0 if err := LoadData([]byte("{}"), &i); err == nil || err.Error() != want { t.Fatalf("got '%v', want '%v'", err, want) } s := struct{}{} if err := LoadData([]byte("{}"), s); err == nil || err.Error() != want { t.Fatalf("got '%v', want '%v'", err, want) } }