1
16
17 package modes_test
18
19 import (
20 "encoding/hex"
21 "fmt"
22 "math"
23 "testing"
24
25 "k8s.io/apimachinery/pkg/conversion"
26 "k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
27
28 "github.com/google/go-cmp/cmp"
29 )
30
31
32
33
34 func TestAppendixA(t *testing.T) {
35 hex := func(h string) []byte {
36 b, err := hex.DecodeString(h)
37 if err != nil {
38 t.Fatal(err)
39 }
40 return b
41 }
42
43 eq := conversion.EqualitiesOrDie(
44
45
46
47
48 func(a float64, b float64) bool {
49 if math.IsNaN(a) && math.IsNaN(b) {
50 return true
51 }
52 return math.Float64bits(a) == math.Float64bits(b)
53 },
54 )
55
56 const (
57 reasonArrayFixedLength = "indefinite-length arrays are re-encoded with fixed length"
58 reasonByteString = "strings are encoded as the byte string major type"
59 reasonFloatPacked = "floats are packed into the smallest value-preserving width"
60 reasonNaN = "all NaN values are represented with a single encoding"
61 reasonMapFixedLength = "indefinite-length maps are re-encoded with fixed length"
62 reasonMapSorted = "map entries are sorted"
63 reasonStringFixedLength = "indefinite-length strings are re-encoded with fixed length"
64 reasonTagIgnored = "unrecognized tag numbers are ignored"
65 reasonTimeToInterface = "times decode to interface{} as RFC3339 timestamps for JSON interoperability"
66 )
67
68 for _, tc := range []struct {
69 example []byte
70 decoded interface{}
71 reject string
72 encoded []byte
73 reasons []string
74
75
76
77 fixme string
78 }{
79 {
80 example: hex("00"),
81 decoded: int64(0),
82 },
83 {
84 example: hex("01"),
85 decoded: int64(1),
86 },
87 {
88 example: hex("0a"),
89 decoded: int64(10),
90 },
91 {
92 example: hex("17"),
93 decoded: int64(23),
94 },
95 {
96 example: hex("1818"),
97 decoded: int64(24),
98 },
99 {
100 example: hex("1819"),
101 decoded: int64(25),
102 },
103 {
104 example: hex("1864"),
105 decoded: int64(100),
106 },
107 {
108 example: hex("1903e8"),
109 decoded: int64(1000),
110 },
111 {
112 example: hex("1a000f4240"),
113 decoded: int64(1000000),
114 },
115 {
116 example: hex("1b000000e8d4a51000"),
117 decoded: int64(1000000000000),
118 },
119 {
120 example: hex("1bffffffffffffffff"),
121 reject: "2^64-1 overflows int64 and falling back to float64 (as with JSON) loses distinction between float and integer",
122 },
123 {
124 example: hex("c249010000000000000000"),
125 reject: "decoding tagged positive bigint value to interface{} can't reproduce this value without losing distinction between float and integer",
126 fixme: "decoding bigint to interface{} must not produce math/big.Int",
127 },
128 {
129 example: hex("3bffffffffffffffff"),
130 reject: "-2^64-1 overflows int64 and falling back to float64 (as with JSON) loses distinction between float and integer",
131 },
132 {
133 example: hex("c349010000000000000000"),
134 reject: "-18446744073709551617 overflows int64 and falling back to float64 (as with JSON) loses distinction between float and integer",
135 fixme: "decoding negative bigint to interface{} must not produce math/big.Int",
136 },
137 {
138 example: hex("20"),
139 decoded: int64(-1),
140 },
141 {
142 example: hex("29"),
143 decoded: int64(-10),
144 },
145 {
146 example: hex("3863"),
147 decoded: int64(-100),
148 },
149 {
150 example: hex("3903e7"),
151 decoded: int64(-1000),
152 },
153 {
154 example: hex("f90000"),
155 decoded: 0.0,
156 },
157 {
158 example: hex("f98000"),
159 decoded: math.Copysign(0, -1),
160 },
161 {
162 example: hex("f93c00"),
163 decoded: 1.0,
164 },
165 {
166 example: hex("fb3ff199999999999a"),
167 decoded: 1.1,
168 },
169 {
170 example: hex("f93e00"),
171 decoded: 1.5,
172 },
173 {
174 example: hex("f97bff"),
175 decoded: 65504.0,
176 },
177 {
178 example: hex("fa47c35000"),
179 decoded: 100000.0,
180 },
181 {
182 example: hex("fa7f7fffff"),
183 decoded: 3.4028234663852886e+38,
184 },
185 {
186 example: hex("fb7e37e43c8800759c"),
187 decoded: 1.0e+300,
188 },
189 {
190 example: hex("f90001"),
191 decoded: 5.960464477539063e-8,
192 },
193 {
194 example: hex("f90400"),
195 decoded: 0.00006103515625,
196 },
197 {
198 example: hex("f9c400"),
199 decoded: -4.0,
200 },
201 {
202 example: hex("fbc010666666666666"),
203 decoded: -4.1,
204 },
205
206
207
208 {
209 example: hex("f97c00"),
210 decoded: math.Inf(1),
211 },
212 {
213 example: hex("f97e00"),
214 decoded: math.Float64frombits(0x7ff8000000000000),
215 },
216 {
217 example: hex("f9fc00"),
218 decoded: math.Inf(-1),
219 },
220 {
221 example: hex("fa7f800000"),
222 decoded: math.Inf(1),
223 encoded: hex("f97c00"),
224 reasons: []string{
225 reasonFloatPacked,
226 },
227 },
228 {
229 example: hex("fa7fc00000"),
230 decoded: math.NaN(),
231 encoded: hex("f97e00"),
232 reasons: []string{
233 reasonNaN,
234 },
235 },
236 {
237 example: hex("faff800000"),
238 decoded: math.Inf(-1),
239 encoded: hex("f9fc00"),
240 reasons: []string{
241 reasonFloatPacked,
242 },
243 },
244 {
245 example: hex("fb7ff0000000000000"),
246 decoded: math.Inf(1),
247 encoded: hex("f97c00"),
248 reasons: []string{
249 reasonFloatPacked,
250 },
251 },
252 {
253 example: hex("fb7ff8000000000000"),
254 decoded: math.NaN(),
255 encoded: hex("f97e00"),
256 reasons: []string{
257 reasonNaN,
258 },
259 },
260 {
261 example: hex("fbfff0000000000000"),
262 decoded: math.Inf(-1),
263 encoded: hex("f9fc00"),
264 reasons: []string{
265 reasonFloatPacked,
266 },
267 },
268 {
269 example: hex("f4"),
270 decoded: false,
271 },
272 {
273 example: hex("f5"),
274 decoded: true,
275 },
276 {
277 example: hex("f6"),
278 decoded: nil,
279 },
280 {
281 example: hex("f7"),
282 reject: "only simple values false, true, and null have a clear analog",
283 fixme: "the undefined simple value should not successfully decode as nil",
284 },
285 {
286 example: hex("f0"),
287 reject: "only simple values false, true, and null have a clear analog",
288 fixme: "simple values other than false, true, and null should be rejected",
289 },
290 {
291 example: hex("f8ff"),
292 reject: "only simple values false, true, and null have a clear analog",
293 fixme: "simple values other than false, true, and null should be rejected",
294 },
295 {
296 example: hex("c074323031332d30332d32315432303a30343a30305a"),
297 decoded: "2013-03-21T20:04:00Z",
298 encoded: hex("54323031332d30332d32315432303a30343a30305a"),
299 reasons: []string{
300 reasonByteString,
301 reasonTimeToInterface,
302 },
303 fixme: "decoding of tagged time into interface{} must produce RFC3339 timestamp compatible with JSON, not time.Time",
304 },
305 {
306 example: hex("c11a514b67b0"),
307 decoded: "2013-03-21T16:04:00Z",
308 encoded: hex("54323031332d30332d32315431363a30343a30305a"),
309 reasons: []string{
310 reasonByteString,
311 reasonTimeToInterface,
312 },
313 fixme: "decoding of tagged time into interface{} must produce RFC3339 timestamp compatible with JSON, not time.Time",
314 },
315 {
316 example: hex("c1fb41d452d9ec200000"),
317 decoded: "2013-03-21T20:04:00.5Z",
318 encoded: hex("56323031332d30332d32315432303a30343a30302e355a"),
319 reasons: []string{
320 reasonByteString,
321 reasonTimeToInterface,
322 },
323 fixme: "decoding of tagged time into interface{} must produce RFC3339 timestamp compatible with JSON, not time.Time",
324 },
325 {
326 example: hex("d74401020304"),
327 decoded: "\x01\x02\x03\x04",
328 encoded: hex("4401020304"),
329 reasons: []string{
330 reasonTagIgnored,
331 },
332 },
333 {
334 example: hex("d818456449455446"),
335 decoded: "dIETF",
336 encoded: hex("456449455446"),
337 reasons: []string{
338 reasonTagIgnored,
339 },
340 },
341 {
342 example: hex("d82076687474703a2f2f7777772e6578616d706c652e636f6d"),
343 decoded: "http://www.example.com",
344 encoded: hex("56687474703a2f2f7777772e6578616d706c652e636f6d"),
345 reasons: []string{
346 reasonByteString,
347 reasonTagIgnored,
348 },
349 },
350 {
351 example: hex("40"),
352 decoded: "",
353 },
354 {
355 example: hex("4401020304"),
356 decoded: "\x01\x02\x03\x04",
357 },
358 {
359 example: hex("60"),
360 decoded: "",
361 encoded: hex("40"),
362 reasons: []string{
363 reasonByteString,
364 },
365 },
366 {
367 example: hex("6161"),
368 decoded: "a",
369 encoded: hex("4161"),
370 reasons: []string{
371 reasonByteString,
372 },
373 },
374 {
375 example: hex("6449455446"),
376 decoded: "IETF",
377 encoded: hex("4449455446"),
378 reasons: []string{
379 reasonByteString,
380 },
381 },
382 {
383 example: hex("62225c"),
384 decoded: "\"\\",
385 encoded: hex("42225c"),
386 reasons: []string{
387 reasonByteString,
388 },
389 },
390 {
391 example: hex("62c3bc"),
392 decoded: "ü",
393 encoded: hex("42c3bc"),
394 reasons: []string{
395 reasonByteString,
396 },
397 },
398 {
399 example: hex("63e6b0b4"),
400 decoded: "水",
401 encoded: hex("43e6b0b4"),
402 reasons: []string{
403 reasonByteString,
404 },
405 },
406 {
407 example: hex("64f0908591"),
408 decoded: "𐅑",
409 encoded: hex("44f0908591"),
410 reasons: []string{
411 reasonByteString,
412 },
413 },
414 {
415 example: hex("80"),
416 decoded: []interface{}{},
417 },
418 {
419 example: hex("83010203"),
420 decoded: []interface{}{int64(1), int64(2), int64(3)},
421 },
422 {
423 example: hex("8301820203820405"),
424 decoded: []interface{}{int64(1), []interface{}{int64(2), int64(3)}, []interface{}{int64(4), int64(5)}},
425 },
426 {
427 example: hex("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
428 decoded: []interface{}{int64(1), int64(2), int64(3), int64(4), int64(5), int64(6), int64(7), int64(8), int64(9), int64(10), int64(11), int64(12), int64(13), int64(14), int64(15), int64(16), int64(17), int64(18), int64(19), int64(20), int64(21), int64(22), int64(23), int64(24), int64(25)},
429 },
430 {
431 example: hex("a0"),
432 decoded: map[string]interface{}{},
433 },
434 {
435 example: hex("a201020304"),
436 reject: "integer map keys don't correspond with field names or unstructured keys",
437 },
438 {
439 example: hex("a26161016162820203"),
440 decoded: map[string]interface{}{
441 "a": int64(1),
442 "b": []interface{}{int64(2), int64(3)},
443 },
444 encoded: hex("a24161014162820203"),
445 reasons: []string{
446 reasonByteString,
447 },
448 },
449 {
450 example: hex("826161a161626163"),
451 decoded: []interface{}{
452 "a",
453 map[string]interface{}{"b": "c"},
454 },
455 encoded: hex("824161a141624163"),
456 reasons: []string{
457 reasonByteString,
458 },
459 },
460 {
461 example: hex("a56161614161626142616361436164614461656145"),
462 decoded: map[string]interface{}{
463 "a": "A",
464 "b": "B",
465 "c": "C",
466 "d": "D",
467 "e": "E",
468 },
469 encoded: hex("a54161414141624142416341434164414441654145"),
470 reasons: []string{
471 reasonByteString,
472 },
473 },
474 {
475 example: hex("5f42010243030405ff"),
476 decoded: "\x01\x02\x03\x04\x05",
477 encoded: hex("450102030405"),
478 reasons: []string{
479 reasonStringFixedLength,
480 },
481 },
482 {
483 example: hex("7f657374726561646d696e67ff"),
484 decoded: "streaming",
485 encoded: hex("4973747265616d696e67"),
486 reasons: []string{
487 reasonByteString,
488 reasonStringFixedLength,
489 },
490 },
491 {
492 example: hex("9fff"),
493 decoded: []interface{}{},
494 encoded: hex("80"),
495 reasons: []string{
496 reasonArrayFixedLength,
497 },
498 },
499 {
500 example: hex("9f018202039f0405ffff"),
501 decoded: []interface{}{
502 int64(1),
503 []interface{}{int64(2), int64(3)},
504 []interface{}{int64(4), int64(5)},
505 },
506 encoded: hex("8301820203820405"),
507 reasons: []string{
508 reasonArrayFixedLength,
509 },
510 },
511 {
512 example: hex("9f01820203820405ff"),
513 decoded: []interface{}{
514 int64(1),
515 []interface{}{int64(2), int64(3)},
516 []interface{}{int64(4), int64(5)},
517 },
518 encoded: hex("8301820203820405"),
519 reasons: []string{
520 reasonArrayFixedLength,
521 },
522 },
523 {
524 example: hex("83018202039f0405ff"),
525 decoded: []interface{}{
526 int64(1),
527 []interface{}{int64(2), int64(3)},
528 []interface{}{int64(4), int64(5)},
529 },
530 encoded: hex("8301820203820405"),
531 reasons: []string{
532 reasonArrayFixedLength,
533 },
534 },
535 {
536 example: hex("83019f0203ff820405"),
537 decoded: []interface{}{
538 int64(1),
539 []interface{}{int64(2), int64(3)},
540 []interface{}{int64(4), int64(5)},
541 },
542 encoded: hex("8301820203820405"),
543 reasons: []string{
544 reasonArrayFixedLength,
545 },
546 },
547 {
548 example: hex("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"),
549 decoded: []interface{}{
550 int64(1), int64(2), int64(3), int64(4), int64(5),
551 int64(6), int64(7), int64(8), int64(9), int64(10),
552 int64(11), int64(12), int64(13), int64(14), int64(15),
553 int64(16), int64(17), int64(18), int64(19), int64(20),
554 int64(21), int64(22), int64(23), int64(24), int64(25),
555 },
556 encoded: hex("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
557 reasons: []string{
558 reasonArrayFixedLength,
559 },
560 },
561 {
562 example: hex("bf61610161629f0203ffff"),
563 decoded: map[string]interface{}{
564 "a": int64(1),
565 "b": []interface{}{int64(2), int64(3)},
566 },
567 encoded: hex("a24161014162820203"),
568 reasons: []string{
569 reasonArrayFixedLength,
570 reasonByteString,
571 reasonMapFixedLength,
572 },
573 },
574 {
575 example: hex("826161bf61626163ff"),
576 decoded: []interface{}{"a", map[string]interface{}{"b": "c"}},
577 encoded: hex("824161a141624163"),
578 reasons: []string{
579 reasonByteString,
580 reasonMapFixedLength,
581 },
582 },
583 {
584 example: hex("bf6346756ef563416d7421ff"),
585 decoded: map[string]interface{}{
586 "Amt": int64(-2),
587 "Fun": true,
588 },
589 encoded: hex("a243416d74214346756ef5"),
590 reasons: []string{
591 reasonByteString,
592 reasonMapFixedLength,
593 reasonMapSorted,
594 },
595 },
596 } {
597 t.Run(fmt.Sprintf("%x", tc.example), func(t *testing.T) {
598 if tc.fixme != "" {
599 t.Skip(tc.fixme)
600 }
601
602 var decoded interface{}
603 err := modes.Decode.Unmarshal(tc.example, &decoded)
604 if err != nil {
605 if tc.reject != "" {
606 t.Logf("expected decode error (%s) occurred: %v", tc.reject, err)
607 return
608 }
609 t.Fatalf("unexpected decode error: %v", err)
610 } else if tc.reject != "" {
611 t.Fatalf("expected decode error (%v) did not occur", tc.reject)
612 }
613
614 if !eq.DeepEqual(tc.decoded, decoded) {
615 t.Fatal(cmp.Diff(tc.decoded, decoded))
616 }
617
618 actual, err := modes.Encode.Marshal(decoded)
619 if err != nil {
620 t.Fatal(err)
621 }
622
623 expected := tc.example
624 if tc.encoded != nil {
625 expected = tc.encoded
626 if len(tc.reasons) == 0 {
627 t.Fatal("invalid test case: missing reasons for difference between the example encoding and the actual encoding")
628 }
629 diff := cmp.Diff(tc.example, tc.encoded)
630 if diff == "" {
631 t.Fatal("invalid test case: no difference between the example encoding and the expected re-encoding")
632 }
633 t.Logf("expecting the following differences from the example encoding on re-encode:\n%s", diff)
634 t.Logf("reasons for encoding differences:")
635 for _, reason := range tc.reasons {
636 t.Logf("- %s", reason)
637 }
638
639 }
640
641 if diff := cmp.Diff(expected, actual); diff != "" {
642 t.Errorf("re-encoded object differs from expected:\n%s", diff)
643 }
644 })
645 }
646 }
647
View as plain text