1
16
17 package modes_test
18
19 import (
20 "fmt"
21 "math"
22 "reflect"
23 "testing"
24 "time"
25
26 "github.com/fxamacker/cbor/v2"
27 "github.com/google/go-cmp/cmp"
28 )
29
30 func nilPointerFor[T interface{}]() *T {
31 return nil
32 }
33
34
35 func TestRoundtrip(t *testing.T) {
36 type modePair struct {
37 enc cbor.EncMode
38 dec cbor.DecMode
39 }
40
41 for _, tc := range []struct {
42 name string
43 modePairs []modePair
44 obj interface{}
45 }{
46 {
47 name: "nil slice",
48 obj: []interface{}(nil),
49 },
50 {
51 name: "nil map",
52 obj: map[string]interface{}(nil),
53 },
54 {
55 name: "empty slice",
56 obj: []interface{}{},
57 },
58 {
59 name: "empty map",
60 obj: map[string]interface{}{},
61 },
62 {
63 name: "nil pointer to slice",
64 obj: nilPointerFor[[]interface{}](),
65 },
66 {
67 name: "nil pointer to map",
68 obj: nilPointerFor[map[string]interface{}](),
69 },
70 {
71 name: "nonempty string",
72 obj: "hello world",
73 },
74 {
75 name: "empty string",
76 obj: "",
77 },
78 {
79 name: "string containing invalid UTF-8 sequence",
80 obj: "\x80",
81 },
82 {
83 name: "true",
84 obj: true,
85 },
86 {
87 name: "false",
88 obj: false,
89 },
90 {
91 name: "int64",
92 obj: int64(5),
93 },
94 {
95 name: "int64 max",
96 obj: int64(math.MaxInt64),
97 },
98 {
99 name: "int64 min",
100 obj: int64(math.MinInt64),
101 },
102 {
103 name: "int64 zero",
104 obj: int64(math.MinInt64),
105 },
106 {
107 name: "uint64 zero",
108 obj: uint64(0),
109 },
110 {
111 name: "int32 max",
112 obj: int32(math.MaxInt32),
113 },
114 {
115 name: "int32 min",
116 obj: int32(math.MinInt32),
117 },
118 {
119 name: "int32 zero",
120 obj: int32(math.MinInt32),
121 },
122 {
123 name: "uint32 max",
124 obj: uint32(math.MaxUint32),
125 },
126 {
127 name: "uint32 zero",
128 obj: uint32(0),
129 },
130 {
131 name: "int16 max",
132 obj: int16(math.MaxInt16),
133 },
134 {
135 name: "int16 min",
136 obj: int16(math.MinInt16),
137 },
138 {
139 name: "int16 zero",
140 obj: int16(math.MinInt16),
141 },
142 {
143 name: "uint16 max",
144 obj: uint16(math.MaxUint16),
145 },
146 {
147 name: "uint16 zero",
148 obj: uint16(0),
149 },
150 {
151 name: "int8 max",
152 obj: int8(math.MaxInt8),
153 },
154 {
155 name: "int8 min",
156 obj: int8(math.MinInt8),
157 },
158 {
159 name: "int8 zero",
160 obj: int8(math.MinInt8),
161 },
162 {
163 name: "uint8 max",
164 obj: uint8(math.MaxUint8),
165 },
166 {
167 name: "uint8 zero",
168 obj: uint8(0),
169 },
170 {
171 name: "float64",
172 obj: float64(2.71),
173 },
174 {
175 name: "float64 max",
176 obj: float64(math.MaxFloat64),
177 },
178 {
179 name: "float64 smallest nonzero",
180 obj: float64(math.SmallestNonzeroFloat64),
181 },
182 {
183 name: "float64 no fractional component",
184 obj: float64(5),
185 },
186 {
187 name: "float32",
188 obj: float32(2.71),
189 },
190 {
191 name: "float32 max",
192 obj: float32(math.MaxFloat32),
193 },
194 {
195 name: "float32 smallest nonzero",
196 obj: float32(math.SmallestNonzeroFloat32),
197 },
198 {
199 name: "float32 no fractional component",
200 obj: float32(5),
201 },
202 {
203 name: "time.Time",
204 obj: time.Date(2222, time.May, 4, 12, 13, 14, 123, time.UTC),
205 },
206 {
207 name: "int64 omitempty",
208 obj: struct {
209 V int64 `json:"v,omitempty"`
210 }{},
211 },
212 {
213 name: "float64 omitempty",
214 obj: struct {
215 V float64 `json:"v,omitempty"`
216 }{},
217 },
218 {
219 name: "string omitempty",
220 obj: struct {
221 V string `json:"v,omitempty"`
222 }{},
223 },
224 {
225 name: "bool omitempty",
226 obj: struct {
227 V bool `json:"v,omitempty"`
228 }{},
229 },
230 {
231 name: "nil pointer omitempty",
232 obj: struct {
233 V *struct{} `json:"v,omitempty"`
234 }{},
235 },
236 {
237 name: "nil pointer to slice as struct field",
238 obj: struct {
239 V *[]interface{} `json:"v"`
240 }{},
241 },
242 {
243 name: "nil pointer to slice as struct field with omitempty",
244 obj: struct {
245 V *[]interface{} `json:"v,omitempty"`
246 }{},
247 },
248 {
249 name: "nil pointer to map as struct field",
250 obj: struct {
251 V *map[string]interface{} `json:"v"`
252 }{},
253 },
254 {
255 name: "nil pointer to map as struct field with omitempty",
256 obj: struct {
257 V *map[string]interface{} `json:"v,omitempty"`
258 }{},
259 },
260 } {
261 modePairs := tc.modePairs
262 if len(modePairs) == 0 {
263
264 modePairs = []modePair{}
265 for _, encMode := range allEncModes {
266 for _, decMode := range allDecModes {
267 modePairs = append(modePairs, modePair{enc: encMode, dec: decMode})
268 }
269 }
270 }
271
272 for _, modePair := range modePairs {
273 encModeName, ok := encModeNames[modePair.enc]
274 if !ok {
275 t.Fatal("test case configured to run against unrecognized encode mode")
276 }
277
278 decModeName, ok := decModeNames[modePair.dec]
279 if !ok {
280 t.Fatal("test case configured to run against unrecognized decode mode")
281 }
282
283 t.Run(fmt.Sprintf("enc=%s/dec=%s/%s", encModeName, decModeName, tc.name), func(t *testing.T) {
284 original := tc.obj
285
286 cborFromOriginal, err := modePair.enc.Marshal(original)
287 if err != nil {
288 t.Fatalf("unexpected error from Marshal of original: %v", err)
289 }
290
291 var iface interface{}
292 if err := modePair.dec.Unmarshal(cborFromOriginal, &iface); err != nil {
293 t.Fatalf("unexpected error from Unmarshal into %T: %v", &iface, err)
294 }
295
296 cborFromIface, err := modePair.enc.Marshal(iface)
297 if err != nil {
298 t.Fatalf("unexpected error from Marshal of iface: %v", err)
299 }
300
301 {
302
303 var iface2 interface{}
304 if err := modePair.dec.Unmarshal(cborFromIface, &iface2); err != nil {
305 t.Fatalf("unexpected error from Unmarshal into %T: %v", &iface2, err)
306 }
307 if diff := cmp.Diff(iface, iface2); diff != "" {
308 t.Errorf("unexpected difference on roundtrip from interface{} to interface{}:\n%s", diff)
309 }
310 }
311
312 {
313
314 final := reflect.New(reflect.TypeOf(original))
315 err = modePair.dec.Unmarshal(cborFromOriginal, final.Interface())
316 if err != nil {
317 t.Fatalf("unexpected error from Unmarshal into %T: %v", final.Interface(), err)
318 }
319 if diff := cmp.Diff(original, final.Elem().Interface()); diff != "" {
320 t.Errorf("unexpected difference on roundtrip from original to original:\n%s", diff)
321 }
322 }
323
324 {
325
326 finalViaIface := reflect.New(reflect.TypeOf(original))
327 err = modePair.dec.Unmarshal(cborFromIface, finalViaIface.Interface())
328 if err != nil {
329 t.Fatalf("unexpected error from Unmarshal into %T: %v", finalViaIface.Interface(), err)
330 }
331 if diff := cmp.Diff(original, finalViaIface.Elem().Interface()); diff != "" {
332 t.Errorf("unexpected difference on roundtrip from original to interface{} to original:\n%s", diff)
333 }
334 }
335 })
336 }
337 }
338 }
339
View as plain text