1 // Copyright 2016 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package config 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "reflect" 10 "testing" 11 "time" 12 ) 13 14 func TestLoad(t *testing.T) { 15 type NestedNested struct { 16 Ccc int 17 Ddd string 18 } 19 type Nested struct { 20 Aaa int 21 Bbb string 22 More NestedNested 23 } 24 type Config struct { 25 Foo int 26 Bar string 27 Baz string `json:"-"` 28 Raw json.RawMessage 29 Qux []string 30 Box Nested 31 Boq *Nested 32 Arr []Nested 33 T time.Time 34 } 35 36 tests := []struct { 37 input string 38 output Config 39 err string 40 }{ 41 { 42 `{"foo": 42}`, 43 Config{ 44 Foo: 42, 45 }, 46 "", 47 }, 48 { 49 `{"BAR": "Baz", "foo": 42}`, 50 Config{ 51 Foo: 42, 52 Bar: "Baz", 53 }, 54 "", 55 }, 56 { 57 `{"foobar": 42}`, 58 Config{}, 59 "unknown field 'foobar' in config", 60 }, 61 { 62 `{"foo": 1, "baz": "baz", "bar": "bar"}`, 63 Config{}, 64 "unknown field 'baz' in config", 65 }, 66 { 67 `{"foo": 1, "box": {"aaa": 12, "bbb": "bbb"}}`, 68 Config{ 69 Foo: 1, 70 Box: Nested{ 71 Aaa: 12, 72 Bbb: "bbb", 73 }, 74 }, 75 "", 76 }, 77 { 78 `{"qux": ["aaa", "bbb"]}`, 79 Config{ 80 Qux: []string{"aaa", "bbb"}, 81 }, 82 "", 83 }, 84 { 85 `{"box": {"aaa": 12, "ccc": "bbb"}}`, 86 Config{}, 87 "unknown field 'box.ccc' in config", 88 }, 89 { 90 `{"foo": 1, "boq": {"aaa": 12, "bbb": "bbb"}}`, 91 Config{ 92 Foo: 1, 93 Boq: &Nested{ 94 Aaa: 12, 95 Bbb: "bbb", 96 }, 97 }, 98 "", 99 }, 100 { 101 `{"boq": {"aaa": 12, "ccc": "bbb"}}`, 102 Config{}, 103 "unknown field 'boq.ccc' in config", 104 }, 105 106 { 107 `{"foo": 1, "arr": []}`, 108 Config{ 109 Foo: 1, 110 Arr: []Nested{}, 111 }, 112 "", 113 }, 114 { 115 `{"foo": 1, "arr": [{"aaa": 12, "bbb": "bbb"}, {"aaa": 13, "bbb": "ccc"}]}`, 116 Config{ 117 Foo: 1, 118 Arr: []Nested{ 119 { 120 Aaa: 12, 121 Bbb: "bbb", 122 }, 123 { 124 Aaa: 13, 125 Bbb: "ccc", 126 }, 127 }, 128 }, 129 "", 130 }, 131 { 132 `{"arr": [{"aaa": 12, "ccc": "bbb"}]}`, 133 Config{}, 134 "unknown field 'arr[0].ccc' in config", 135 }, 136 { 137 `{"foo": 1, "boq": {"aaa": 12, "more": {"ccc": 13, "ddd": "ddd"}}}`, 138 Config{ 139 Foo: 1, 140 Boq: &Nested{ 141 Aaa: 12, 142 More: NestedNested{ 143 Ccc: 13, 144 Ddd: "ddd", 145 }, 146 }, 147 }, 148 "", 149 }, 150 { 151 `{"foo": 1, "boq": {"aaa": 12, "more": {"ccc": 13, "eee": "eee"}}}`, 152 Config{}, 153 "unknown field 'boq.more.eee' in config", 154 }, 155 { 156 `{"raw": {"zux": 11}}`, 157 Config{ 158 Raw: []byte(`{"zux": 11}`), 159 }, 160 "", 161 }, 162 { 163 `{"foo": null, "qux": null}`, 164 Config{}, 165 "", 166 }, 167 { 168 `{"t": "2000-01-02T03:04:05Z"}`, 169 Config{ 170 T: time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), 171 }, 172 "", 173 }, 174 } 175 for i, test := range tests { 176 t.Run(fmt.Sprint(i), func(t *testing.T) { 177 var cfg Config 178 err := LoadData([]byte(test.input), &cfg) 179 errStr := "" 180 if err != nil { 181 errStr = err.Error() 182 } 183 if test.err != errStr { 184 t.Fatalf("bad err: want '%v', got '%v'", test.err, errStr) 185 } 186 if !reflect.DeepEqual(test.output, cfg) { 187 t.Fatalf("bad output: want:\n%#v\n, got:\n%#v", test.output, cfg) 188 } 189 }) 190 } 191 } 192 193 func TestLoadBadType(t *testing.T) { 194 want := "config type is not pointer to struct" 195 if err := LoadData([]byte("{}"), 1); err == nil || err.Error() != want { 196 t.Fatalf("got '%v', want '%v'", err, want) 197 } 198 i := 0 199 if err := LoadData([]byte("{}"), &i); err == nil || err.Error() != want { 200 t.Fatalf("got '%v', want '%v'", err, want) 201 } 202 s := struct{}{} 203 if err := LoadData([]byte("{}"), s); err == nil || err.Error() != want { 204 t.Fatalf("got '%v', want '%v'", err, want) 205 } 206 } 207