Merge pull request #123268 from benluddy/cbor-appendix-a-tests
KEP-4222: Add roundtrip tests for all CBOR examples in RFC 8949 Appendix A.
This commit is contained in:
		@@ -0,0 +1,646 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2024 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package modes_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/conversion"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/google/go-cmp/cmp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestAppendixA roundtrips the examples of encoded CBOR data items in RFC 8949 Appendix A. For
 | 
				
			||||||
 | 
					// completeness, all examples from the appendix are included, even those those that are rejected by
 | 
				
			||||||
 | 
					// this decoder or are re-encoded to a different sequence of CBOR bytes (with explanation).
 | 
				
			||||||
 | 
					func TestAppendixA(t *testing.T) {
 | 
				
			||||||
 | 
						hex := func(h string) []byte {
 | 
				
			||||||
 | 
							b, err := hex.DecodeString(h)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eq := conversion.EqualitiesOrDie(
 | 
				
			||||||
 | 
							// NaN float64 values are always inequal and have multiple representations. RFC 8949
 | 
				
			||||||
 | 
							// Section 4.2.2 recommends protocols not supporting NaN payloads or signaling NaNs
 | 
				
			||||||
 | 
							// choose a single representation for all NaN values. For the purposes of this test,
 | 
				
			||||||
 | 
							// all NaN representations are equivalent.
 | 
				
			||||||
 | 
							func(a float64, b float64) bool {
 | 
				
			||||||
 | 
								if math.IsNaN(a) && math.IsNaN(b) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return math.Float64bits(a) == math.Float64bits(b)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							reasonArrayFixedLength  = "indefinite-length arrays are re-encoded with fixed length"
 | 
				
			||||||
 | 
							reasonByteString        = "strings are encoded as the byte string major type"
 | 
				
			||||||
 | 
							reasonFloatPacked       = "floats are packed into the smallest value-preserving width"
 | 
				
			||||||
 | 
							reasonNaN               = "all NaN values are represented with a single encoding"
 | 
				
			||||||
 | 
							reasonMapFixedLength    = "indefinite-length maps are re-encoded with fixed length"
 | 
				
			||||||
 | 
							reasonMapSorted         = "map entries are sorted"
 | 
				
			||||||
 | 
							reasonStringFixedLength = "indefinite-length strings are re-encoded with fixed length"
 | 
				
			||||||
 | 
							reasonTagIgnored        = "unrecognized tag numbers are ignored"
 | 
				
			||||||
 | 
							reasonTimeToInterface   = "times decode to interface{} as RFC3339 timestamps for JSON interoperability"
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range []struct {
 | 
				
			||||||
 | 
							example []byte // example data item
 | 
				
			||||||
 | 
							decoded interface{}
 | 
				
			||||||
 | 
							reject  string   // reason the decoder rejects the example
 | 
				
			||||||
 | 
							encoded []byte   // re-encoded object (only if different from example encoding)
 | 
				
			||||||
 | 
							reasons []string // reasons for re-encode difference
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO: The cases with nonempty fixme are known to be not working and fixing them
 | 
				
			||||||
 | 
							// is an alpha criteria. They're present and skipped for visibility.
 | 
				
			||||||
 | 
							fixme string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("00"),
 | 
				
			||||||
 | 
								decoded: int64(0),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("01"),
 | 
				
			||||||
 | 
								decoded: int64(1),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("0a"),
 | 
				
			||||||
 | 
								decoded: int64(10),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("17"),
 | 
				
			||||||
 | 
								decoded: int64(23),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1818"),
 | 
				
			||||||
 | 
								decoded: int64(24),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1819"),
 | 
				
			||||||
 | 
								decoded: int64(25),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1864"),
 | 
				
			||||||
 | 
								decoded: int64(100),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1903e8"),
 | 
				
			||||||
 | 
								decoded: int64(1000),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1a000f4240"),
 | 
				
			||||||
 | 
								decoded: int64(1000000),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1b000000e8d4a51000"),
 | 
				
			||||||
 | 
								decoded: int64(1000000000000),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("1bffffffffffffffff"),
 | 
				
			||||||
 | 
								reject:  "2^64-1 overflows int64 and falling back to float64 (as with JSON) loses distinction between float and integer",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("c249010000000000000000"),
 | 
				
			||||||
 | 
								reject:  "decoding tagged positive bigint value to interface{} can't reproduce this value without losing distinction between float and integer",
 | 
				
			||||||
 | 
								fixme:   "decoding bigint to interface{} must not produce math/big.Int",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("3bffffffffffffffff"),
 | 
				
			||||||
 | 
								reject:  "-2^64-1 overflows int64 and falling back to float64 (as with JSON) loses distinction between float and integer",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("c349010000000000000000"),
 | 
				
			||||||
 | 
								reject:  "-18446744073709551617 overflows int64 and falling back to float64 (as with JSON) loses distinction between float and integer",
 | 
				
			||||||
 | 
								fixme:   "decoding negative bigint to interface{} must not produce math/big.Int",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("20"),
 | 
				
			||||||
 | 
								decoded: int64(-1),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("29"),
 | 
				
			||||||
 | 
								decoded: int64(-10),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("3863"),
 | 
				
			||||||
 | 
								decoded: int64(-100),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("3903e7"),
 | 
				
			||||||
 | 
								decoded: int64(-1000),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f90000"),
 | 
				
			||||||
 | 
								decoded: 0.0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f98000"),
 | 
				
			||||||
 | 
								decoded: math.Copysign(0, -1),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f93c00"),
 | 
				
			||||||
 | 
								decoded: 1.0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fb3ff199999999999a"),
 | 
				
			||||||
 | 
								decoded: 1.1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f93e00"),
 | 
				
			||||||
 | 
								decoded: 1.5,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f97bff"),
 | 
				
			||||||
 | 
								decoded: 65504.0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fa47c35000"),
 | 
				
			||||||
 | 
								decoded: 100000.0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fa7f7fffff"),
 | 
				
			||||||
 | 
								decoded: 3.4028234663852886e+38,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fb7e37e43c8800759c"),
 | 
				
			||||||
 | 
								decoded: 1.0e+300,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f90001"),
 | 
				
			||||||
 | 
								decoded: 5.960464477539063e-8,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f90400"),
 | 
				
			||||||
 | 
								decoded: 0.00006103515625,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f9c400"),
 | 
				
			||||||
 | 
								decoded: -4.0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fbc010666666666666"),
 | 
				
			||||||
 | 
								decoded: -4.1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							// TODO: Should Inf/-Inf/NaN be supported? Current Protobuf will encode this, but
 | 
				
			||||||
 | 
							// JSON will produce an error.  This is less than ideal -- we can't transcode
 | 
				
			||||||
 | 
							// everything to JSON.
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f97c00"),
 | 
				
			||||||
 | 
								decoded: math.Inf(1),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f97e00"),
 | 
				
			||||||
 | 
								decoded: math.Float64frombits(0x7ff8000000000000),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f9fc00"),
 | 
				
			||||||
 | 
								decoded: math.Inf(-1),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fa7f800000"),
 | 
				
			||||||
 | 
								decoded: math.Inf(1),
 | 
				
			||||||
 | 
								encoded: hex("f97c00"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonFloatPacked,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fa7fc00000"),
 | 
				
			||||||
 | 
								decoded: math.NaN(),
 | 
				
			||||||
 | 
								encoded: hex("f97e00"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonNaN,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("faff800000"),
 | 
				
			||||||
 | 
								decoded: math.Inf(-1),
 | 
				
			||||||
 | 
								encoded: hex("f9fc00"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonFloatPacked,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fb7ff0000000000000"),
 | 
				
			||||||
 | 
								decoded: math.Inf(1),
 | 
				
			||||||
 | 
								encoded: hex("f97c00"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonFloatPacked,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fb7ff8000000000000"),
 | 
				
			||||||
 | 
								decoded: math.NaN(),
 | 
				
			||||||
 | 
								encoded: hex("f97e00"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonNaN,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("fbfff0000000000000"),
 | 
				
			||||||
 | 
								decoded: math.Inf(-1),
 | 
				
			||||||
 | 
								encoded: hex("f9fc00"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonFloatPacked,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f4"),
 | 
				
			||||||
 | 
								decoded: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f5"),
 | 
				
			||||||
 | 
								decoded: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f6"),
 | 
				
			||||||
 | 
								decoded: nil,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f7"),
 | 
				
			||||||
 | 
								reject:  "only simple values false, true, and null have a clear analog",
 | 
				
			||||||
 | 
								fixme:   "the undefined simple value should not successfully decode as nil",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f0"),
 | 
				
			||||||
 | 
								reject:  "only simple values false, true, and null have a clear analog",
 | 
				
			||||||
 | 
								fixme:   "simple values other than false, true, and null should be rejected",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("f8ff"),
 | 
				
			||||||
 | 
								reject:  "only simple values false, true, and null have a clear analog",
 | 
				
			||||||
 | 
								fixme:   "simple values other than false, true, and null should be rejected",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("c074323031332d30332d32315432303a30343a30305a"),
 | 
				
			||||||
 | 
								decoded: "2013-03-21T20:04:00Z",
 | 
				
			||||||
 | 
								encoded: hex("54323031332d30332d32315432303a30343a30305a"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonTimeToInterface,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								fixme: "decoding of tagged time into interface{} must produce RFC3339 timestamp compatible with JSON, not time.Time",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("c11a514b67b0"),
 | 
				
			||||||
 | 
								decoded: "2013-03-21T16:04:00Z",
 | 
				
			||||||
 | 
								encoded: hex("54323031332d30332d32315431363a30343a30305a"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonTimeToInterface,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								fixme: "decoding of tagged time into interface{} must produce RFC3339 timestamp compatible with JSON, not time.Time",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("c1fb41d452d9ec200000"),
 | 
				
			||||||
 | 
								decoded: "2013-03-21T20:04:00.5Z",
 | 
				
			||||||
 | 
								encoded: hex("56323031332d30332d32315432303a30343a30302e355a"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonTimeToInterface,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								fixme: "decoding of tagged time into interface{} must produce RFC3339 timestamp compatible with JSON, not time.Time",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("d74401020304"),
 | 
				
			||||||
 | 
								decoded: "\x01\x02\x03\x04",
 | 
				
			||||||
 | 
								encoded: hex("4401020304"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonTagIgnored,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("d818456449455446"),
 | 
				
			||||||
 | 
								decoded: "dIETF",
 | 
				
			||||||
 | 
								encoded: hex("456449455446"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonTagIgnored,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("d82076687474703a2f2f7777772e6578616d706c652e636f6d"),
 | 
				
			||||||
 | 
								decoded: "http://www.example.com",
 | 
				
			||||||
 | 
								encoded: hex("56687474703a2f2f7777772e6578616d706c652e636f6d"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonTagIgnored,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("40"),
 | 
				
			||||||
 | 
								decoded: "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("4401020304"),
 | 
				
			||||||
 | 
								decoded: "\x01\x02\x03\x04",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("60"),
 | 
				
			||||||
 | 
								decoded: "",
 | 
				
			||||||
 | 
								encoded: hex("40"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("6161"),
 | 
				
			||||||
 | 
								decoded: "a",
 | 
				
			||||||
 | 
								encoded: hex("4161"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("6449455446"),
 | 
				
			||||||
 | 
								decoded: "IETF",
 | 
				
			||||||
 | 
								encoded: hex("4449455446"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("62225c"),
 | 
				
			||||||
 | 
								decoded: "\"\\",
 | 
				
			||||||
 | 
								encoded: hex("42225c"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("62c3bc"),
 | 
				
			||||||
 | 
								decoded: "ü",
 | 
				
			||||||
 | 
								encoded: hex("42c3bc"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("63e6b0b4"),
 | 
				
			||||||
 | 
								decoded: "水",
 | 
				
			||||||
 | 
								encoded: hex("43e6b0b4"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("64f0908591"),
 | 
				
			||||||
 | 
								decoded: "𐅑",
 | 
				
			||||||
 | 
								encoded: hex("44f0908591"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("80"),
 | 
				
			||||||
 | 
								decoded: []interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("83010203"),
 | 
				
			||||||
 | 
								decoded: []interface{}{int64(1), int64(2), int64(3)},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("8301820203820405"),
 | 
				
			||||||
 | 
								decoded: []interface{}{int64(1), []interface{}{int64(2), int64(3)}, []interface{}{int64(4), int64(5)}},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
 | 
				
			||||||
 | 
								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)},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("a0"),
 | 
				
			||||||
 | 
								decoded: map[string]interface{}{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("a201020304"),
 | 
				
			||||||
 | 
								reject:  "integer map keys don't correspond with field names or unstructured keys",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("a26161016162820203"),
 | 
				
			||||||
 | 
								decoded: map[string]interface{}{
 | 
				
			||||||
 | 
									"a": int64(1),
 | 
				
			||||||
 | 
									"b": []interface{}{int64(2), int64(3)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("a24161014162820203"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("826161a161626163"),
 | 
				
			||||||
 | 
								decoded: []interface{}{
 | 
				
			||||||
 | 
									"a",
 | 
				
			||||||
 | 
									map[string]interface{}{"b": "c"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("824161a141624163"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("a56161614161626142616361436164614461656145"),
 | 
				
			||||||
 | 
								decoded: map[string]interface{}{
 | 
				
			||||||
 | 
									"a": "A",
 | 
				
			||||||
 | 
									"b": "B",
 | 
				
			||||||
 | 
									"c": "C",
 | 
				
			||||||
 | 
									"d": "D",
 | 
				
			||||||
 | 
									"e": "E",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("a54161414141624142416341434164414441654145"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("5f42010243030405ff"),
 | 
				
			||||||
 | 
								decoded: "\x01\x02\x03\x04\x05",
 | 
				
			||||||
 | 
								encoded: hex("450102030405"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonStringFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("7f657374726561646d696e67ff"),
 | 
				
			||||||
 | 
								decoded: "streaming",
 | 
				
			||||||
 | 
								encoded: hex("4973747265616d696e67"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonStringFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("9fff"),
 | 
				
			||||||
 | 
								decoded: []interface{}{},
 | 
				
			||||||
 | 
								encoded: hex("80"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("9f018202039f0405ffff"),
 | 
				
			||||||
 | 
								decoded: []interface{}{
 | 
				
			||||||
 | 
									int64(1),
 | 
				
			||||||
 | 
									[]interface{}{int64(2), int64(3)},
 | 
				
			||||||
 | 
									[]interface{}{int64(4), int64(5)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("8301820203820405"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("9f01820203820405ff"),
 | 
				
			||||||
 | 
								decoded: []interface{}{
 | 
				
			||||||
 | 
									int64(1),
 | 
				
			||||||
 | 
									[]interface{}{int64(2), int64(3)},
 | 
				
			||||||
 | 
									[]interface{}{int64(4), int64(5)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("8301820203820405"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("83018202039f0405ff"),
 | 
				
			||||||
 | 
								decoded: []interface{}{
 | 
				
			||||||
 | 
									int64(1),
 | 
				
			||||||
 | 
									[]interface{}{int64(2), int64(3)},
 | 
				
			||||||
 | 
									[]interface{}{int64(4), int64(5)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("8301820203820405"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("83019f0203ff820405"),
 | 
				
			||||||
 | 
								decoded: []interface{}{
 | 
				
			||||||
 | 
									int64(1),
 | 
				
			||||||
 | 
									[]interface{}{int64(2), int64(3)},
 | 
				
			||||||
 | 
									[]interface{}{int64(4), int64(5)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("8301820203820405"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"),
 | 
				
			||||||
 | 
								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),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("bf61610161629f0203ffff"),
 | 
				
			||||||
 | 
								decoded: map[string]interface{}{
 | 
				
			||||||
 | 
									"a": int64(1),
 | 
				
			||||||
 | 
									"b": []interface{}{int64(2), int64(3)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("a24161014162820203"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonArrayFixedLength,
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonMapFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("826161bf61626163ff"),
 | 
				
			||||||
 | 
								decoded: []interface{}{"a", map[string]interface{}{"b": "c"}},
 | 
				
			||||||
 | 
								encoded: hex("824161a141624163"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonMapFixedLength,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								example: hex("bf6346756ef563416d7421ff"),
 | 
				
			||||||
 | 
								decoded: map[string]interface{}{
 | 
				
			||||||
 | 
									"Amt": int64(-2),
 | 
				
			||||||
 | 
									"Fun": true,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								encoded: hex("a243416d74214346756ef5"),
 | 
				
			||||||
 | 
								reasons: []string{
 | 
				
			||||||
 | 
									reasonByteString,
 | 
				
			||||||
 | 
									reasonMapFixedLength,
 | 
				
			||||||
 | 
									reasonMapSorted,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							t.Run(fmt.Sprintf("%x", tc.example), func(t *testing.T) {
 | 
				
			||||||
 | 
								if tc.fixme != "" {
 | 
				
			||||||
 | 
									t.Skip(tc.fixme) // TODO: Remove once all cases are fixed.
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var decoded interface{}
 | 
				
			||||||
 | 
								err := modes.Decode.Unmarshal(tc.example, &decoded)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if tc.reject != "" {
 | 
				
			||||||
 | 
										t.Logf("expected decode error (%s) occurred: %v", tc.reject, err)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									t.Fatalf("unexpected decode error: %v", err)
 | 
				
			||||||
 | 
								} else if tc.reject != "" {
 | 
				
			||||||
 | 
									t.Fatalf("expected decode error (%v) did not occur", tc.reject)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !eq.DeepEqual(tc.decoded, decoded) {
 | 
				
			||||||
 | 
									t.Fatal(cmp.Diff(tc.decoded, decoded))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								actual, err := modes.Encode.Marshal(decoded)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								expected := tc.example
 | 
				
			||||||
 | 
								if tc.encoded != nil {
 | 
				
			||||||
 | 
									expected = tc.encoded
 | 
				
			||||||
 | 
									if len(tc.reasons) == 0 {
 | 
				
			||||||
 | 
										t.Fatal("invalid test case: missing reasons for difference between the example encoding and the actual encoding")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									diff := cmp.Diff(tc.example, tc.encoded)
 | 
				
			||||||
 | 
									if diff == "" {
 | 
				
			||||||
 | 
										t.Fatal("invalid test case: no difference between the example encoding and the expected re-encoding")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									t.Logf("expecting the following differences from the example encoding on re-encode:\n%s", diff)
 | 
				
			||||||
 | 
									t.Logf("reasons for encoding differences:")
 | 
				
			||||||
 | 
									for _, reason := range tc.reasons {
 | 
				
			||||||
 | 
										t.Logf("- %s", reason)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if diff := cmp.Diff(expected, actual); diff != "" {
 | 
				
			||||||
 | 
									t.Errorf("re-encoded object differs from expected:\n%s", diff)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user