2021-12-04 16:42:11 +00:00

3593 lines
114 KiB
Go

// Copyright (c) Faye Amacker. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
package cbor
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"math"
"math/big"
"reflect"
"strings"
"testing"
"time"
)
type marshalTest struct {
cborData []byte
values []interface{}
}
type marshalErrorTest struct {
name string
value interface{}
wantErrorMsg string
}
type inner struct {
X, Y, z int64
}
type outer struct {
IntField int
FloatField float32
BoolField bool
StringField string
ByteStringField []byte
ArrayField []string
MapField map[string]bool
NestedStructField *inner
unexportedField int64
}
// CBOR test data are from https://tools.ietf.org/html/rfc7049#appendix-A.
var marshalTests = []marshalTest{
// positive integer
{hexDecode("00"), []interface{}{uint(0), uint8(0), uint16(0), uint32(0), uint64(0), int(0), int8(0), int16(0), int32(0), int64(0)}},
{hexDecode("01"), []interface{}{uint(1), uint8(1), uint16(1), uint32(1), uint64(1), int(1), int8(1), int16(1), int32(1), int64(1)}},
{hexDecode("0a"), []interface{}{uint(10), uint8(10), uint16(10), uint32(10), uint64(10), int(10), int8(10), int16(10), int32(10), int64(10)}},
{hexDecode("17"), []interface{}{uint(23), uint8(23), uint16(23), uint32(23), uint64(23), int(23), int8(23), int16(23), int32(23), int64(23)}},
{hexDecode("1818"), []interface{}{uint(24), uint8(24), uint16(24), uint32(24), uint64(24), int(24), int8(24), int16(24), int32(24), int64(24)}},
{hexDecode("1819"), []interface{}{uint(25), uint8(25), uint16(25), uint32(25), uint64(25), int(25), int8(25), int16(25), int32(25), int64(25)}},
{hexDecode("1864"), []interface{}{uint(100), uint8(100), uint16(100), uint32(100), uint64(100), int(100), int8(100), int16(100), int32(100), int64(100)}},
{hexDecode("18ff"), []interface{}{uint(255), uint8(255), uint16(255), uint32(255), uint64(255), int(255), int16(255), int32(255), int64(255)}},
{hexDecode("190100"), []interface{}{uint(256), uint16(256), uint32(256), uint64(256), int(256), int16(256), int32(256), int64(256)}},
{hexDecode("1903e8"), []interface{}{uint(1000), uint16(1000), uint32(1000), uint64(1000), int(1000), int16(1000), int32(1000), int64(1000)}},
{hexDecode("19ffff"), []interface{}{uint(65535), uint16(65535), uint32(65535), uint64(65535), int(65535), int32(65535), int64(65535)}},
{hexDecode("1a00010000"), []interface{}{uint(65536), uint32(65536), uint64(65536), int(65536), int32(65536), int64(65536)}},
{hexDecode("1a000f4240"), []interface{}{uint(1000000), uint32(1000000), uint64(1000000), int(1000000), int32(1000000), int64(1000000)}},
{hexDecode("1affffffff"), []interface{}{uint(4294967295), uint32(4294967295), uint64(4294967295), int64(4294967295)}},
{hexDecode("1b000000e8d4a51000"), []interface{}{uint64(1000000000000), int64(1000000000000)}},
{hexDecode("1bffffffffffffffff"), []interface{}{uint64(18446744073709551615)}},
// negative integer
{hexDecode("20"), []interface{}{int(-1), int8(-1), int16(-1), int32(-1), int64(-1)}},
{hexDecode("29"), []interface{}{int(-10), int8(-10), int16(-10), int32(-10), int64(-10)}},
{hexDecode("37"), []interface{}{int(-24), int8(-24), int16(-24), int32(-24), int64(-24)}},
{hexDecode("3818"), []interface{}{int(-25), int8(-25), int16(-25), int32(-25), int64(-25)}},
{hexDecode("3863"), []interface{}{int(-100), int8(-100), int16(-100), int32(-100), int64(-100)}},
{hexDecode("38ff"), []interface{}{int(-256), int16(-256), int32(-256), int64(-256)}},
{hexDecode("390100"), []interface{}{int(-257), int16(-257), int32(-257), int64(-257)}},
{hexDecode("3903e7"), []interface{}{int(-1000), int16(-1000), int32(-1000), int64(-1000)}},
{hexDecode("39ffff"), []interface{}{int(-65536), int32(-65536), int64(-65536)}},
{hexDecode("3a00010000"), []interface{}{int(-65537), int32(-65537), int64(-65537)}},
{hexDecode("3affffffff"), []interface{}{int64(-4294967296)}},
// byte string
{hexDecode("40"), []interface{}{[]byte{}}},
{hexDecode("4401020304"), []interface{}{[]byte{1, 2, 3, 4}, [...]byte{1, 2, 3, 4}}},
// text string
{hexDecode("60"), []interface{}{""}},
{hexDecode("6161"), []interface{}{"a"}},
{hexDecode("6449455446"), []interface{}{"IETF"}},
{hexDecode("62225c"), []interface{}{"\"\\"}},
{hexDecode("62c3bc"), []interface{}{"ü"}},
{hexDecode("63e6b0b4"), []interface{}{"水"}},
{hexDecode("64f0908591"), []interface{}{"𐅑"}},
// array
{
hexDecode("80"),
[]interface{}{
[0]int{},
[]uint{},
// []uint8{},
[]uint16{},
[]uint32{},
[]uint64{},
[]int{},
[]int8{},
[]int16{},
[]int32{},
[]int64{},
[]string{},
[]bool{}, []float32{}, []float64{}, []interface{}{},
},
},
{
hexDecode("83010203"),
[]interface{}{
[...]int{1, 2, 3},
[]uint{1, 2, 3},
// []uint8{1, 2, 3},
[]uint16{1, 2, 3},
[]uint32{1, 2, 3},
[]uint64{1, 2, 3},
[]int{1, 2, 3},
[]int8{1, 2, 3},
[]int16{1, 2, 3},
[]int32{1, 2, 3},
[]int64{1, 2, 3},
[]interface{}{1, 2, 3},
},
},
{
hexDecode("8301820203820405"),
[]interface{}{
[...]interface{}{1, [...]int{2, 3}, [...]int{4, 5}},
[]interface{}{1, []uint{2, 3}, []uint{4, 5}},
// []interface{}{1, []uint8{2, 3}, []uint8{4, 5}},
[]interface{}{1, []uint16{2, 3}, []uint16{4, 5}},
[]interface{}{1, []uint32{2, 3}, []uint32{4, 5}},
[]interface{}{1, []uint64{2, 3}, []uint64{4, 5}},
[]interface{}{1, []int{2, 3}, []int{4, 5}},
[]interface{}{1, []int8{2, 3}, []int8{4, 5}},
[]interface{}{1, []int16{2, 3}, []int16{4, 5}},
[]interface{}{1, []int32{2, 3}, []int32{4, 5}},
[]interface{}{1, []int64{2, 3}, []int64{4, 5}},
[]interface{}{1, []interface{}{2, 3}, []interface{}{4, 5}},
},
},
{
hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
[]interface{}{
[...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]uint{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
// []uint8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]uint16{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]int16{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
[]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
},
},
{
hexDecode("826161a161626163"),
[]interface{}{
[...]interface{}{"a", map[string]string{"b": "c"}},
[]interface{}{"a", map[string]string{"b": "c"}},
[]interface{}{"a", map[interface{}]interface{}{"b": "c"}},
},
},
// map
{
hexDecode("a0"),
[]interface{}{
map[uint]bool{},
map[uint8]bool{},
map[uint16]bool{},
map[uint32]bool{},
map[uint64]bool{},
map[int]bool{},
map[int8]bool{},
map[int16]bool{},
map[int32]bool{},
map[int64]bool{},
map[float32]bool{},
map[float64]bool{},
map[bool]bool{},
map[string]bool{},
map[interface{}]interface{}{},
},
},
{
hexDecode("a201020304"),
[]interface{}{
map[uint]uint{3: 4, 1: 2},
map[uint8]uint8{3: 4, 1: 2},
map[uint16]uint16{3: 4, 1: 2},
map[uint32]uint32{3: 4, 1: 2},
map[uint64]uint64{3: 4, 1: 2},
map[int]int{3: 4, 1: 2},
map[int8]int8{3: 4, 1: 2},
map[int16]int16{3: 4, 1: 2},
map[int32]int32{3: 4, 1: 2},
map[int64]int64{3: 4, 1: 2},
map[interface{}]interface{}{3: 4, 1: 2},
},
},
{
hexDecode("a26161016162820203"),
[]interface{}{
map[string]interface{}{"a": 1, "b": []interface{}{2, 3}},
map[interface{}]interface{}{"b": []interface{}{2, 3}, "a": 1},
},
},
{
hexDecode("a56161614161626142616361436164614461656145"),
[]interface{}{
map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"},
map[interface{}]interface{}{"b": "B", "a": "A", "c": "C", "e": "E", "d": "D"},
},
},
// tag
{
hexDecode("c074323031332d30332d32315432303a30343a30305a"),
[]interface{}{Tag{0, "2013-03-21T20:04:00Z"}, RawTag{0, hexDecode("74323031332d30332d32315432303a30343a30305a")}},
}, // 0: standard date/time
{
hexDecode("c11a514b67b0"),
[]interface{}{Tag{1, uint64(1363896240)}, RawTag{1, hexDecode("1a514b67b0")}},
}, // 1: epoch-based date/time
{
hexDecode("c249010000000000000000"),
[]interface{}{
bigIntOrPanic("18446744073709551616"),
Tag{2, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
RawTag{2, hexDecode("49010000000000000000")},
},
}, // 2: positive bignum: 18446744073709551616
{
hexDecode("c349010000000000000000"),
[]interface{}{
bigIntOrPanic("-18446744073709551617"),
Tag{3, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
RawTag{3, hexDecode("49010000000000000000")},
},
}, // 3: negative bignum: -18446744073709551617
{
hexDecode("c1fb41d452d9ec200000"),
[]interface{}{Tag{1, float64(1363896240.5)}, RawTag{1, hexDecode("fb41d452d9ec200000")}},
}, // 1: epoch-based date/time
{
hexDecode("d74401020304"),
[]interface{}{Tag{23, []byte{0x01, 0x02, 0x03, 0x04}}, RawTag{23, hexDecode("4401020304")}},
}, // 23: expected conversion to base16 encoding
{
hexDecode("d818456449455446"),
[]interface{}{Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}}, RawTag{24, hexDecode("456449455446")}},
}, // 24: encoded cborBytes data item
{
hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"),
[]interface{}{Tag{32, "http://www.example.com"}, RawTag{32, hexDecode("76687474703a2f2f7777772e6578616d706c652e636f6d")}},
}, // 32: URI
// primitives
{hexDecode("f4"), []interface{}{false}},
{hexDecode("f5"), []interface{}{true}},
{hexDecode("f6"), []interface{}{nil, []byte(nil), []int(nil), map[uint]bool(nil), (*int)(nil), io.Reader(nil)}},
// nan, positive and negative inf
{hexDecode("f97c00"), []interface{}{math.Inf(1)}},
{hexDecode("f97e00"), []interface{}{math.NaN()}},
{hexDecode("f9fc00"), []interface{}{math.Inf(-1)}},
// float32
{hexDecode("fa47c35000"), []interface{}{float32(100000.0)}},
{hexDecode("fa7f7fffff"), []interface{}{float32(3.4028234663852886e+38)}},
// float64
{hexDecode("fb3ff199999999999a"), []interface{}{float64(1.1)}},
{hexDecode("fb7e37e43c8800759c"), []interface{}{float64(1.0e+300)}},
{hexDecode("fbc010666666666666"), []interface{}{float64(-4.1)}},
// More testcases not covered by https://tools.ietf.org/html/rfc7049#appendix-A.
{
hexDecode("d83dd183010203"), // 61(17([1, 2, 3])), nested tags 61 and 17
[]interface{}{Tag{61, Tag{17, []interface{}{uint64(1), uint64(2), uint64(3)}}}, RawTag{61, hexDecode("d183010203")}},
},
}
var exMarshalTests = []marshalTest{
{
// array of nils
hexDecode("83f6f6f6"),
[]interface{}{
[]interface{}{nil, nil, nil},
},
},
}
func TestMarshal(t *testing.T) {
testMarshal(t, marshalTests)
testMarshal(t, exMarshalTests)
}
func TestInvalidTypeMarshal(t *testing.T) {
type s1 struct {
Chan chan bool
}
type s2 struct {
_ struct{} `cbor:",toarray"`
Chan chan bool
}
var marshalErrorTests = []marshalErrorTest{
{"channel cannot be marshaled", make(chan bool), "cbor: unsupported type: chan bool"},
{"slice of channel cannot be marshaled", make([]chan bool, 10), "cbor: unsupported type: []chan bool"},
{"slice of pointer to channel cannot be marshaled", make([]*chan bool, 10), "cbor: unsupported type: []*chan bool"},
{"map of channel cannot be marshaled", make(map[string]chan bool), "cbor: unsupported type: map[string]chan bool"},
{"struct of channel cannot be marshaled", s1{}, "cbor: unsupported type: cbor.s1"},
{"struct of channel cannot be marshaled", s2{}, "cbor: unsupported type: cbor.s2"},
{"function cannot be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"},
{"complex cannot be marshaled", complex(100, 8), "cbor: unsupported type: complex128"},
}
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range marshalErrorTests {
t.Run(tc.name, func(t *testing.T) {
b, err := Marshal(&tc.value)
if err == nil {
t.Errorf("Marshal(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg)
} else if _, ok := err.(*UnsupportedTypeError); !ok {
t.Errorf("Marshal(%v) error type %T, want *UnsupportedTypeError", tc.value, err)
} else if err.Error() != tc.wantErrorMsg {
t.Errorf("Marshal(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg)
} else if b != nil {
t.Errorf("Marshal(%v) = 0x%x, want nil", tc.value, b)
}
b, err = em.Marshal(&tc.value)
if err == nil {
t.Errorf("Marshal(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg)
} else if _, ok := err.(*UnsupportedTypeError); !ok {
t.Errorf("Marshal(%v) error type %T, want *UnsupportedTypeError", tc.value, err)
} else if err.Error() != tc.wantErrorMsg {
t.Errorf("Marshal(%v) error %q, want %q", tc.value, err.Error(), tc.wantErrorMsg)
} else if b != nil {
t.Errorf("Marshal(%v) = 0x%x, want nil", tc.value, b)
}
})
}
}
func TestMarshalLargeByteString(t *testing.T) {
// []byte{100, 100, 100, ...}
lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 10000000}
tests := make([]marshalTest, len(lengths))
for i, length := range lengths {
cborData := bytes.NewBuffer(encodeCborHeader(cborTypeByteString, uint64(length)))
value := make([]byte, length)
for j := 0; j < length; j++ {
cborData.WriteByte(100)
value[j] = 100
}
tests[i] = marshalTest{cborData.Bytes(), []interface{}{value}}
}
testMarshal(t, tests)
}
func TestMarshalLargeTextString(t *testing.T) {
// "ddd..."
lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 10000000}
tests := make([]marshalTest, len(lengths))
for i, length := range lengths {
cborData := bytes.NewBuffer(encodeCborHeader(cborTypeTextString, uint64(length)))
value := make([]byte, length)
for j := 0; j < length; j++ {
cborData.WriteByte(100)
value[j] = 100
}
tests[i] = marshalTest{cborData.Bytes(), []interface{}{string(value)}}
}
testMarshal(t, tests)
}
func TestMarshalLargeArray(t *testing.T) {
// []string{"水", "水", "水", ...}
lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 131072}
tests := make([]marshalTest, len(lengths))
for i, length := range lengths {
cborData := bytes.NewBuffer(encodeCborHeader(cborTypeArray, uint64(length)))
value := make([]string, length)
for j := 0; j < length; j++ {
cborData.Write([]byte{0x63, 0xe6, 0xb0, 0xb4})
value[j] = "水"
}
tests[i] = marshalTest{cborData.Bytes(), []interface{}{value}}
}
testMarshal(t, tests)
}
func TestMarshalLargeMapCanonical(t *testing.T) {
// map[int]int {0:0, 1:1, 2:2, ...}
lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 131072}
tests := make([]marshalTest, len(lengths))
for i, length := range lengths {
cborData := bytes.NewBuffer(encodeCborHeader(cborTypeMap, uint64(length)))
value := make(map[int]int, length)
for j := 0; j < length; j++ {
d := encodeCborHeader(cborTypePositiveInt, uint64(j))
cborData.Write(d)
cborData.Write(d)
value[j] = j
}
tests[i] = marshalTest{cborData.Bytes(), []interface{}{value}}
}
testMarshal(t, tests)
}
func TestMarshalLargeMap(t *testing.T) {
// map[int]int {0:0, 1:1, 2:2, ...}
lengths := []int{0, 1, 2, 22, 23, 24, 254, 255, 256, 65534, 65535, 65536, 131072}
for _, length := range lengths {
m1 := make(map[int]int, length)
for i := 0; i < length; i++ {
m1[i] = i
}
cborData, err := Marshal(m1)
if err != nil {
t.Fatalf("Marshal(%v) returned error %v", m1, err)
}
m2 := make(map[int]int)
if err = Unmarshal(cborData, &m2); err != nil {
t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err)
}
if !reflect.DeepEqual(m1, m2) {
t.Errorf("Unmarshal() = %v, want %v", m2, m1)
}
}
}
func encodeCborHeader(t cborType, n uint64) []byte {
b := make([]byte, 9)
if n <= 23 {
b[0] = byte(t) | byte(n)
return b[:1]
} else if n <= math.MaxUint8 {
b[0] = byte(t) | byte(24)
b[1] = byte(n)
return b[:2]
} else if n <= math.MaxUint16 {
b[0] = byte(t) | byte(25)
binary.BigEndian.PutUint16(b[1:], uint16(n))
return b[:3]
} else if n <= math.MaxUint32 {
b[0] = byte(t) | byte(26)
binary.BigEndian.PutUint32(b[1:], uint32(n))
return b[:5]
} else {
b[0] = byte(t) | byte(27)
binary.BigEndian.PutUint64(b[1:], n)
return b[:9]
}
}
func testMarshal(t *testing.T, testCases []marshalTest) {
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
for _, value := range tc.values {
if _, err := Marshal(value); err != nil {
t.Errorf("Marshal(%v) returned error %v", value, err)
}
if b, err := em.Marshal(value); err != nil {
t.Errorf("Marshal(%v) returned error %v", value, err)
} else if !bytes.Equal(b, tc.cborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", value, b, tc.cborData)
}
}
r := RawMessage(tc.cborData)
if b, err := Marshal(r); err != nil {
t.Errorf("Marshal(%v) returned error %v", r, err)
} else if !bytes.Equal(b, r) {
t.Errorf("Marshal(%v) returned %v, want %v", r, b, r)
}
}
}
func TestMarshalStruct(t *testing.T) {
v1 := outer{
IntField: 123,
FloatField: 100000.0,
BoolField: true,
StringField: "test",
ByteStringField: []byte{1, 3, 5},
ArrayField: []string{"hello", "world"},
MapField: map[string]bool{"afternoon": false, "morning": true},
NestedStructField: &inner{X: 1000, Y: 1000000, z: 10000000},
unexportedField: 6,
}
unmarshalWant := outer{
IntField: 123,
FloatField: 100000.0,
BoolField: true,
StringField: "test",
ByteStringField: []byte{1, 3, 5},
ArrayField: []string{"hello", "world"},
MapField: map[string]bool{"afternoon": false, "morning": true},
NestedStructField: &inner{X: 1000, Y: 1000000},
}
cborData, err := Marshal(v1)
if err != nil {
t.Fatalf("Marshal(%v) returned error %v", v1, err)
}
var v2 outer
if err = Unmarshal(cborData, &v2); err != nil {
t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err)
}
if !reflect.DeepEqual(unmarshalWant, v2) {
t.Errorf("Unmarshal() = %v, want %v", v2, unmarshalWant)
}
}
func TestMarshalStructCanonical(t *testing.T) {
v := outer{
IntField: 123,
FloatField: 100000.0,
BoolField: true,
StringField: "test",
ByteStringField: []byte{1, 3, 5},
ArrayField: []string{"hello", "world"},
MapField: map[string]bool{"afternoon": false, "morning": true},
NestedStructField: &inner{X: 1000, Y: 1000000, z: 10000000},
unexportedField: 6,
}
var cborData bytes.Buffer
cborData.WriteByte(byte(cborTypeMap) | 8) // CBOR header: map type with 8 items (exported fields)
cborData.WriteByte(byte(cborTypeTextString) | 8) // "IntField"
cborData.WriteString("IntField")
cborData.WriteByte(byte(cborTypePositiveInt) | 24)
cborData.WriteByte(123)
cborData.WriteByte(byte(cborTypeTextString) | 8) // "MapField"
cborData.WriteString("MapField")
cborData.WriteByte(byte(cborTypeMap) | 2)
cborData.WriteByte(byte(cborTypeTextString) | 7)
cborData.WriteString("morning")
cborData.WriteByte(byte(cborTypePrimitives) | 21)
cborData.WriteByte(byte(cborTypeTextString) | 9)
cborData.WriteString("afternoon")
cborData.WriteByte(byte(cborTypePrimitives) | 20)
cborData.WriteByte(byte(cborTypeTextString) | 9) // "BoolField"
cborData.WriteString("BoolField")
cborData.WriteByte(byte(cborTypePrimitives) | 21)
cborData.WriteByte(byte(cborTypeTextString) | 10) // "ArrayField"
cborData.WriteString("ArrayField")
cborData.WriteByte(byte(cborTypeArray) | 2)
cborData.WriteByte(byte(cborTypeTextString) | 5)
cborData.WriteString("hello")
cborData.WriteByte(byte(cborTypeTextString) | 5)
cborData.WriteString("world")
cborData.WriteByte(byte(cborTypeTextString) | 10) // "FloatField"
cborData.WriteString("FloatField")
cborData.Write([]byte{0xfa, 0x47, 0xc3, 0x50, 0x00})
cborData.WriteByte(byte(cborTypeTextString) | 11) // "StringField"
cborData.WriteString("StringField")
cborData.WriteByte(byte(cborTypeTextString) | 4)
cborData.WriteString("test")
cborData.WriteByte(byte(cborTypeTextString) | 15) // "ByteStringField"
cborData.WriteString("ByteStringField")
cborData.WriteByte(byte(cborTypeByteString) | 3)
cborData.Write([]byte{1, 3, 5})
cborData.WriteByte(byte(cborTypeTextString) | 17) // "NestedStructField"
cborData.WriteString("NestedStructField")
cborData.WriteByte(byte(cborTypeMap) | 2)
cborData.WriteByte(byte(cborTypeTextString) | 1)
cborData.WriteString("X")
cborData.WriteByte(byte(cborTypePositiveInt) | 25)
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, uint16(1000))
cborData.Write(b)
cborData.WriteByte(byte(cborTypeTextString) | 1)
cborData.WriteString("Y")
cborData.WriteByte(byte(cborTypePositiveInt) | 26)
b = make([]byte, 4)
binary.BigEndian.PutUint32(b, uint32(1000000))
cborData.Write(b)
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %q", err)
}
if b, err := em.Marshal(v); err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, cborData.Bytes()) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, cborData.Bytes())
}
}
func TestMarshalNullPointerToEmbeddedStruct(t *testing.T) {
type (
T1 struct {
X int
}
T2 struct {
*T1
}
)
v := T2{}
wantCborData := []byte{0xa0} // {}
cborData, err := Marshal(v)
if err != nil {
t.Fatalf("Marshal(%v) returned error %v", v, err)
}
if !bytes.Equal(wantCborData, cborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, cborData, wantCborData)
}
}
func TestMarshalNullPointerToStruct(t *testing.T) {
type (
T1 struct {
X int
}
T2 struct {
T *T1
}
)
v := T2{}
wantCborData := []byte{0xa1, 0x61, 0x54, 0xf6} // {X: nil}
cborData, err := Marshal(v)
if err != nil {
t.Fatalf("Marshal(%v) returned error %v", v, err)
}
if !bytes.Equal(wantCborData, cborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, cborData, wantCborData)
}
}
// Struct fields encoding follows the same struct fields visibility
// rules used by JSON encoding package. Some struct types are from
// tests in JSON encoding package to ensure that the same rules are
// followed.
func TestAnonymousFields1(t *testing.T) {
// Fields (T1.X, T2.X) with the same name at the same level are ignored
type (
T1 struct{ x, X int }
T2 struct{ x, X int }
T struct {
T1
T2
}
)
v := T{T1{1, 2}, T2{3, 4}}
want := []byte{0xa0} // {}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
}
func TestAnonymousFields2(t *testing.T) {
// Field (T.X) with the same name at a less nested level is serialized
type (
T1 struct{ x, X int }
T2 struct{ x, X int }
T struct {
T1
T2
x, X int
}
)
v := T{T1{1, 2}, T2{3, 4}, 5, 6}
want := []byte{0xa1, 0x61, 0x58, 0x06} // {X:6}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{X: 6}
if err := Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestAnonymousFields3(t *testing.T) {
// Unexported embedded field (myInt) of non-struct type is ignored
type (
myInt int
T struct {
myInt
}
)
v := T{5}
want := []byte{0xa0} // {}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
}
func TestAnonymousFields4(t *testing.T) {
// Exported embedded field (MyInt) of non-struct type is serialized
type (
MyInt int
T struct {
MyInt
}
)
v := T{5}
want := []byte{0xa1, 0x65, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x05} // {MyInt: 5}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v, v2) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v, v, v2, v2)
}
}
func TestAnonymousFields5(t *testing.T) {
// Unexported embedded field (*myInt) of pointer to non-struct type is ignored
type (
myInt int
T struct {
*myInt
}
)
v := T{new(myInt)}
*v.myInt = 5
want := []byte{0xa0} // {}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
}
func TestAnonymousFields6(t *testing.T) {
// Exported embedded field (*MyInt) of pointer to non-struct type should be serialized
type (
MyInt int
T struct {
*MyInt
}
)
v := T{new(MyInt)}
*v.MyInt = 5
want := []byte{0xa1, 0x65, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x05} // {MyInt: 5}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v, v2) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v, v, v2, v2)
}
}
func TestAnonymousFields7(t *testing.T) {
// Exported fields (t1.X, T2.Y) of embedded structs should have their exported fields be serialized
type (
t1 struct{ x, X int }
T2 struct{ y, Y int }
T struct {
t1
T2
}
)
v := T{t1{1, 2}, T2{3, 4}}
want := []byte{0xa2, 0x61, 0x58, 0x02, 0x61, 0x59, 0x04} // {X:2, Y:4}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{t1{X: 2}, T2{Y: 4}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestAnonymousFields8(t *testing.T) {
// Exported fields of pointers (t1.X, T2.Y)
type (
t1 struct{ x, X int }
T2 struct{ y, Y int }
T struct {
*t1
*T2
}
)
v := T{&t1{1, 2}, &T2{3, 4}}
want := []byte{0xa2, 0x61, 0x58, 0x02, 0x61, 0x59, 0x04} // {X:2, Y:4}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
// v1 cannot be unmarshaled to because reflect cannot allocate unexported field s1.
var v1 T
wantErrorMsg := "cannot set embedded pointer to unexported struct"
wantV := T{T2: &T2{Y: 4}}
err = Unmarshal(b, &v1)
if err == nil {
t.Errorf("Unmarshal(0x%x) didn't return an error, want error %q", b, wantErrorMsg)
} else if !strings.Contains(err.Error(), wantErrorMsg) {
t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", b, err.Error(), wantErrorMsg)
}
if !reflect.DeepEqual(v1, wantV) {
t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", b, v1, v1, wantV, wantV)
}
// v2 can be unmarshaled to because unexported field t1 is already allocated.
var v2 T
v2.t1 = &t1{}
unmarshalWant := T{&t1{X: 2}, &T2{Y: 4}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestAnonymousFields9(t *testing.T) {
// Multiple levels of nested anonymous fields
type (
MyInt1 int
MyInt2 int
myInt int
t2 struct {
MyInt2
myInt
}
t1 struct {
MyInt1
myInt
t2
}
T struct {
t1
myInt
}
)
v := T{t1{1, 2, t2{3, 4}}, 6}
want := []byte{0xa2, 0x66, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x31, 0x01, 0x66, 0x4d, 0x79, 0x49, 0x6e, 0x74, 0x32, 0x03} // {MyInt1: 1, MyInt2: 3}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{t1: t1{MyInt1: 1, t2: t2{MyInt2: 3}}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestAnonymousFields10(t *testing.T) {
// Fields of the same struct type at the same level
type (
t3 struct {
Z int
}
t1 struct {
X int
t3
}
t2 struct {
Y int
t3
}
T struct {
t1
t2
}
)
v := T{t1{1, t3{2}}, t2{3, t3{4}}}
want := []byte{0xa2, 0x61, 0x58, 0x01, 0x61, 0x59, 0x03} // {X: 1, Y: 3}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{t1: t1{X: 1}, t2: t2{Y: 3}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestAnonymousFields11(t *testing.T) {
// Fields (T.t2.X, T.t1.t2.X) of the same struct type at different levels
type (
t2 struct {
X int
}
t1 struct {
Y int
t2
}
T struct {
t1
t2
}
)
v := T{t1{1, t2{2}}, t2{3}}
want := []byte{0xa2, 0x61, 0x59, 0x01, 0x61, 0x58, 0x03} // {Y: 1, X: 3}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{t1: t1{Y: 1}, t2: t2{X: 3}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestOmitAndRenameStructField(t *testing.T) {
type T struct {
I int // never omit
Io int `cbor:",omitempty"` // omit empty
Iao int `cbor:"-"` // always omit
R int `cbor:"omitempty"` // renamed to omitempty
}
v1 := T{}
// {"I": 0, "omitempty": 0}
want1 := []byte{0xa2,
0x61, 0x49, 0x00,
0x69, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00}
v2 := T{I: 1, Io: 2, Iao: 0, R: 3}
// {"I": 1, "Io": 2, "omitempty": 3}
want2 := []byte{0xa3,
0x61, 0x49, 0x01,
0x62, 0x49, 0x6f, 0x02,
0x69, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x03}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
tests := []roundTripTest{
{"default values", v1, want1},
{"non-default values", v2, want2}}
testRoundTrip(t, tests, em, dm)
}
func TestOmitEmptyForBuiltinType(t *testing.T) {
type T struct {
B bool `cbor:"b"`
Bo bool `cbor:"bo,omitempty"`
UI uint `cbor:"ui"`
UIo uint `cbor:"uio,omitempty"`
I int `cbor:"i"`
Io int `cbor:"io,omitempty"`
F float64 `cbor:"f"`
Fo float64 `cbor:"fo,omitempty"`
S string `cbor:"s"`
So string `cbor:"so,omitempty"`
Slc []string `cbor:"slc"`
Slco []string `cbor:"slco,omitempty"`
M map[int]string `cbor:"m"`
Mo map[int]string `cbor:"mo,omitempty"`
P *int `cbor:"p"`
Po *int `cbor:"po,omitempty"`
Intf interface{} `cbor:"intf"`
Intfo interface{} `cbor:"intfo,omitempty"`
}
v := T{}
// {"b": false, "ui": 0, "i":0, "f": 0, "s": "", "slc": null, "m": {}, "p": nil, "intf": nil }
want := []byte{0xa9,
0x61, 0x62, 0xf4,
0x62, 0x75, 0x69, 0x00,
0x61, 0x69, 0x00,
0x61, 0x66, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x61, 0x73, 0x60,
0x63, 0x73, 0x6c, 0x63, 0xf6,
0x61, 0x6d, 0xf6,
0x61, 0x70, 0xf6,
0x64, 0x69, 0x6e, 0x74, 0x66, 0xf6,
}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm)
}
func TestOmitEmptyForAnonymousStruct(t *testing.T) {
type T struct {
Str struct{} `cbor:"str"`
Stro struct{} `cbor:"stro,omitempty"`
}
v := T{}
want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0} // {"str": {}}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm)
}
func TestOmitEmptyForStruct1(t *testing.T) {
type T1 struct {
Bo bool `cbor:"bo,omitempty"`
UIo uint `cbor:"uio,omitempty"`
Io int `cbor:"io,omitempty"`
Fo float64 `cbor:"fo,omitempty"`
So string `cbor:"so,omitempty"`
Slco []string `cbor:"slco,omitempty"`
Mo map[int]string `cbor:"mo,omitempty"`
Po *int `cbor:"po,omitempty"`
Intfo interface{} `cbor:"intfo,omitempty"`
}
type T struct {
Str T1 `cbor:"str"`
Stro T1 `cbor:"stro,omitempty"`
}
v := T{}
want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0} // {"str": {}}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm)
}
func TestOmitEmptyForStruct2(t *testing.T) {
type T1 struct {
Bo bool `cbor:"bo,omitempty"`
UIo uint `cbor:"uio,omitempty"`
Io int `cbor:"io,omitempty"`
Fo float64 `cbor:"fo,omitempty"`
So string `cbor:"so,omitempty"`
Slco []string `cbor:"slco,omitempty"`
Mo map[int]string `cbor:"mo,omitempty"`
Po *int `cbor:"po,omitempty"`
Intfo interface{} `cbor:"intfo"`
}
type T struct {
Stro T1 `cbor:"stro,omitempty"`
}
v := T{}
want := []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x65, 0x69, 0x6e, 0x74, 0x66, 0x6f, 0xf6} // {"stro": {intfo: nil}}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"non-default values", v, want}}, em, dm)
}
func TestOmitEmptyForNestedStruct(t *testing.T) {
type T1 struct {
Bo bool `cbor:"bo,omitempty"`
UIo uint `cbor:"uio,omitempty"`
Io int `cbor:"io,omitempty"`
Fo float64 `cbor:"fo,omitempty"`
So string `cbor:"so,omitempty"`
Slco []string `cbor:"slco,omitempty"`
Mo map[int]string `cbor:"mo,omitempty"`
Po *int `cbor:"po,omitempty"`
Intfo interface{} `cbor:"intfo,omitempty"`
}
type T2 struct {
Stro T1 `cbor:"stro,omitempty"`
}
type T struct {
Str T2 `cbor:"str"`
Stro T2 `cbor:"stro,omitempty"`
}
v := T{}
want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0} // {"str": {}}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm)
}
func TestOmitEmptyForToArrayStruct1(t *testing.T) {
type T1 struct {
_ struct{} `cbor:",toarray"`
b bool
ui uint
i int
f float64
s string
slc []string
m map[int]string
p *int
intf interface{}
}
type T struct {
Str T1 `cbor:"str"`
Stro T1 `cbor:"stro,omitempty"`
}
v := T{
Str: T1{b: false, ui: 0, i: 0, f: 0.0, s: "", slc: nil, m: nil, p: nil, intf: nil},
Stro: T1{b: false, ui: 0, i: 0, f: 0.0, s: "", slc: nil, m: nil, p: nil, intf: nil},
}
want := []byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0x80} // {"str": []}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"no exportable fields", v, want}}, em, dm)
}
func TestOmitEmptyForToArrayStruct2(t *testing.T) {
type T1 struct {
_ struct{} `cbor:",toarray"`
Bo bool `cbor:"bo"`
UIo uint `cbor:"uio"`
Io int `cbor:"io"`
Fo float64 `cbor:"fo"`
So string `cbor:"so"`
Slco []string `cbor:"slco"`
Mo map[int]string `cbor:"mo"`
Po *int `cbor:"po"`
Intfo interface{} `cbor:"intfo"`
}
type T struct {
Stro T1 `cbor:"stro,omitempty"`
}
v := T{}
// {"stro": [false, 0, 0, 0.0, "", [], {}, nil, nil]}
want := []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0x89, 0xf4, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf6, 0xf6, 0xf6, 0xf6}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"has exportable fields", v, want}}, em, dm)
}
func TestOmitEmptyForStructWithPtrToAnonymousField(t *testing.T) {
type (
T1 struct {
X int `cbor:"x,omitempty"`
Y int `cbor:"y,omitempty"`
}
T2 struct {
*T1
}
T struct {
Stro T2 `cbor:"stro,omitempty"`
}
)
testCases := []struct {
name string
obj interface{}
wantCborData []byte
}{
{
name: "null pointer to anonymous field",
obj: T{},
wantCborData: []byte{0xa0}, // {}
},
{
name: "not-null pointer to anonymous field",
obj: T{T2{&T1{}}},
wantCborData: []byte{0xa0}, // {}
},
{
name: "not empty value in field 1",
obj: T{T2{&T1{X: 1}}},
wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x78, 0x01}, // {stro:{x:1}}
},
{
name: "not empty value in field 2",
obj: T{T2{&T1{Y: 2}}},
wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x79, 0x02}, // {stro:{y:2}}
},
{
name: "not empty value in all fields",
obj: T{T2{&T1{X: 1, Y: 2}}},
wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa2, 0x61, 0x78, 0x01, 0x61, 0x79, 0x02}, // {stro:{x:1, y:2}}
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := Marshal(tc.obj)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.obj, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData)
}
})
}
}
func TestOmitEmptyForStructWithAnonymousField(t *testing.T) {
type (
T1 struct {
X int `cbor:"x,omitempty"`
Y int `cbor:"y,omitempty"`
}
T2 struct {
T1
}
T struct {
Stro T2 `cbor:"stro,omitempty"`
}
)
testCases := []struct {
name string
obj interface{}
wantCborData []byte
}{
{
name: "default values",
obj: T{},
wantCborData: []byte{0xa0}, // {}
},
{
name: "default values",
obj: T{T2{T1{}}},
wantCborData: []byte{0xa0}, // {}
},
{
name: "not empty value in field 1",
obj: T{T2{T1{X: 1}}},
wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x78, 0x01}, // {stro:{x:1}}
},
{
name: "not empty value in field 2",
obj: T{T2{T1{Y: 2}}},
wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa1, 0x61, 0x79, 0x02}, // {stro:{y:2}}
},
{
name: "not empty value in all fields",
obj: T{T2{T1{X: 1, Y: 2}}},
wantCborData: []byte{0xa1, 0x64, 0x73, 0x74, 0x72, 0x6f, 0xa2, 0x61, 0x78, 0x01, 0x61, 0x79, 0x02}, // {stro:{x:1, y:2}}
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := Marshal(tc.obj)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.obj, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData)
}
})
}
}
func TestOmitEmptyForBinaryMarshaler1(t *testing.T) {
type T1 struct {
No number `cbor:"no,omitempty"`
}
type T struct {
Str T1 `cbor:"str"`
Stro T1 `cbor:"stro,omitempty"`
}
testCases := []roundTripTest{
{
"empty BinaryMarshaler",
T1{},
[]byte{0xa0}, // {}
},
{
"empty struct containing empty BinaryMarshaler",
T{},
[]byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0}, // {str: {}}
},
}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, testCases, em, dm)
}
func TestOmitEmptyForBinaryMarshaler2(t *testing.T) {
type T1 struct {
So stru `cbor:"so,omitempty"`
}
type T struct {
Str T1 `cbor:"str"`
Stro T1 `cbor:"stro,omitempty"`
}
testCases := []roundTripTest{
{
"empty BinaryMarshaler",
T1{},
[]byte{0xa0}, // {}
},
{
"empty struct containing empty BinaryMarshaler",
T{},
[]byte{0xa1, 0x63, 0x73, 0x74, 0x72, 0xa0}, // {str: {}}
},
}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, testCases, em, dm)
}
// omitempty is a no-op for time.Time.
func TestOmitEmptyForTime(t *testing.T) {
type T struct {
Tm time.Time `cbor:"t,omitempty"`
}
v := T{}
want := []byte{0xa1, 0x61, 0x74, 0xf6} // {"t": nil}
em, _ := EncOptions{}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm)
}
// omitempty is a no-op for big.Int.
func TestOmitEmptyForBigInt(t *testing.T) {
type T struct {
I big.Int `cbor:"bi,omitempty"`
}
v := T{}
want := []byte{0xa1, 0x62, 0x62, 0x69, 0xc2, 0x40} // {"bi": 2([])}
em, _ := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode()
dm, _ := DecOptions{}.DecMode()
testRoundTrip(t, []roundTripTest{{"default values", v, want}}, em, dm)
}
func TestTaggedField(t *testing.T) {
// A field (T2.X) with a tag dominates untagged field.
type (
T1 struct {
S string
}
T2 struct {
X string `cbor:"S"`
}
T struct {
T1
T2
}
)
v := T{T1{"T1"}, T2{"T2"}}
want := []byte{0xa1, 0x61, 0x53, 0x62, 0x54, 0x32} // {"S":"T2"}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{T2: T2{"T2"}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestDuplicatedFields(t *testing.T) {
// Duplicate fields (T.T1.S, T.T2.S) are ignored.
type (
T1 struct {
S string
}
T2 struct {
S string
}
T3 struct {
X string `cbor:"S"`
}
T4 struct {
T1
T3
}
T struct {
T1
T2
T4 // Contains a tagged S field through T3; should not dominate.
}
)
v := T{
T1{"T1"},
T2{"T2"},
T4{
T1{"nested T1"},
T3{"nested T3"},
},
}
want := []byte{0xa0} // {}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, want)
}
}
type TReader struct {
X int
}
func (s TReader) Read(p []byte) (n int, err error) {
return 0, nil
}
func TestTaggedAnonymousField(t *testing.T) {
// Anonymous field with a name given in its CBOR tag is treated as having that name, rather than being anonymous.
type (
T1 struct {
X int
}
T struct {
X int
T1 `cbor:"T1"`
}
)
v := T{X: 1, T1: T1{X: 2}}
want := []byte{0xa2, 0x61, 0x58, 0x01, 0x62, 0x54, 0x31, 0xa1, 0x61, 0x58, 0x02} // {X: 1, T1: {X:2}}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
unmarshalWant := T{X: 1, T1: T1{X: 2}}
if err = Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if !reflect.DeepEqual(v2, unmarshalWant) {
t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", b, v2, v2, unmarshalWant, unmarshalWant)
}
}
func TestAnonymousInterfaceField(t *testing.T) {
// Anonymous field of interface type is treated the same as having that type as its name, rather than being anonymous.
type (
T struct {
X int
io.Reader
}
)
v := T{X: 1, Reader: TReader{X: 2}}
want := []byte{0xa2, 0x61, 0x58, 0x01, 0x66, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0xa1, 0x61, 0x58, 0x02} // {X: 1, Reader: {X:2}}
b, err := Marshal(v)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, want)
}
var v2 T
const wantErrorMsg = "cannot unmarshal map into Go struct field cbor.T.Reader of type io.Reader"
if err = Unmarshal(b, &v2); err == nil {
t.Errorf("Unmarshal(0x%x) didn't return an error, want error (*UnmarshalTypeError)", b)
} else {
if typeError, ok := err.(*UnmarshalTypeError); !ok {
t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", b, err)
} else if !strings.Contains(typeError.Error(), wantErrorMsg) {
t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", b, err.Error(), wantErrorMsg)
}
}
}
func TestEncodeInterface(t *testing.T) {
var r io.Reader = TReader{X: 2}
want := []byte{0xa1, 0x61, 0x58, 0x02} // {X:2}
b, err := Marshal(r)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", r, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", r, b, want)
}
var v io.Reader
const wantErrorMsg = "cannot unmarshal map into Go value of type io.Reader"
if err = Unmarshal(b, &v); err == nil {
t.Errorf("Unmarshal(0x%x) didn't return an error, want error (*UnmarshalTypeError)", b)
} else {
if typeError, ok := err.(*UnmarshalTypeError); !ok {
t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", b, err)
} else if !strings.Contains(typeError.Error(), wantErrorMsg) {
t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", b, err.Error(), wantErrorMsg)
}
}
}
func TestEncodeTime(t *testing.T) {
timeUnixOpt := EncOptions{Time: TimeUnix}
timeUnixMicroOpt := EncOptions{Time: TimeUnixMicro}
timeUnixDynamicOpt := EncOptions{Time: TimeUnixDynamic}
timeRFC3339Opt := EncOptions{Time: TimeRFC3339}
timeRFC3339NanoOpt := EncOptions{Time: TimeRFC3339Nano}
type timeConvert struct {
opt EncOptions
wantCborData []byte
}
testCases := []struct {
name string
tm time.Time
convert []timeConvert
}{
{
name: "zero time",
tm: time.Time{},
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
},
},
{
name: "time without fractional seconds",
tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("1a514b67b0"), // 1363896240
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("fb41d452d9ec000000"), // 1363896240.0
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("1a514b67b0"), // 1363896240
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("74323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("74323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
},
},
},
{
name: "time with fractional seconds",
tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"),
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("1a514b67b0"), // 1363896240
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("fb41d452d9ec200000"), // 1363896240.5
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("fb41d452d9ec200000"), // 1363896240.5
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("74323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("76323031332d30332d32315432303a30343a30302e355a"), // "2013-03-21T20:04:00.5Z"
},
},
},
{
name: "time before January 1, 1970 UTC without fractional seconds",
tm: parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"),
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("3a0177f2cf"), // -24638160
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("fbc1777f2d00000000"), // -24638160.0
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("3a0177f2cf"), // -24638160
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("74313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z"
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("74313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z"
},
},
},
}
for _, tc := range testCases {
for _, convert := range tc.convert {
var convertName string
switch convert.opt.Time {
case TimeUnix:
convertName = "TimeUnix"
case TimeUnixMicro:
convertName = "TimeUnixMicro"
case TimeUnixDynamic:
convertName = "TimeUnixDynamic"
case TimeRFC3339:
convertName = "TimeRFC3339"
case TimeRFC3339Nano:
convertName = "TimeRFC3339Nano"
}
name := tc.name + " with " + convertName + " option"
t.Run(name, func(t *testing.T) {
em, err := convert.opt.EncMode()
if err != nil {
t.Errorf("EncMode() returned error %v", err)
}
b, err := em.Marshal(tc.tm)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.tm, err)
} else if !bytes.Equal(b, convert.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.tm, b, convert.wantCborData)
}
})
}
}
}
func TestEncodeTimeWithTag(t *testing.T) {
timeUnixOpt := EncOptions{Time: TimeUnix, TimeTag: EncTagRequired}
timeUnixMicroOpt := EncOptions{Time: TimeUnixMicro, TimeTag: EncTagRequired}
timeUnixDynamicOpt := EncOptions{Time: TimeUnixDynamic, TimeTag: EncTagRequired}
timeRFC3339Opt := EncOptions{Time: TimeRFC3339, TimeTag: EncTagRequired}
timeRFC3339NanoOpt := EncOptions{Time: TimeRFC3339Nano, TimeTag: EncTagRequired}
type timeConvert struct {
opt EncOptions
wantCborData []byte
}
testCases := []struct {
name string
tm time.Time
convert []timeConvert
}{
{
name: "zero time",
tm: time.Time{},
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("f6"), // encode as CBOR null
},
},
},
{
name: "time without fractional seconds",
tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("c11a514b67b0"), // 1363896240
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("c1fb41d452d9ec000000"), // 1363896240.0
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("c11a514b67b0"), // 1363896240
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
},
},
},
{
name: "time with fractional seconds",
tm: parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"),
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("c11a514b67b0"), // 1363896240
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("c1fb41d452d9ec200000"), // 1363896240.5
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("c1fb41d452d9ec200000"), // 1363896240.5
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("c076323031332d30332d32315432303a30343a30302e355a"), // "2013-03-21T20:04:00.5Z"
},
},
},
{
name: "time before January 1, 1970 UTC without fractional seconds",
tm: parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"),
convert: []timeConvert{
{
opt: timeUnixOpt,
wantCborData: hexDecode("c13a0177f2cf"), // -24638160
},
{
opt: timeUnixMicroOpt,
wantCborData: hexDecode("c1fbc1777f2d00000000"), // -24638160.0
},
{
opt: timeUnixDynamicOpt,
wantCborData: hexDecode("c13a0177f2cf"), // -24638160
},
{
opt: timeRFC3339Opt,
wantCborData: hexDecode("c074313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z"
},
{
opt: timeRFC3339NanoOpt,
wantCborData: hexDecode("c074313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z"
},
},
},
}
for _, tc := range testCases {
for _, convert := range tc.convert {
var convertName string
switch convert.opt.Time {
case TimeUnix:
convertName = "TimeUnix"
case TimeUnixMicro:
convertName = "TimeUnixMicro"
case TimeUnixDynamic:
convertName = "TimeUnixDynamic"
case TimeRFC3339:
convertName = "TimeRFC3339"
case TimeRFC3339Nano:
convertName = "TimeRFC3339Nano"
}
name := tc.name + " with " + convertName + " option"
t.Run(name, func(t *testing.T) {
em, err := convert.opt.EncMode()
if err != nil {
t.Errorf("EncMode() returned error %v", err)
}
b, err := em.Marshal(tc.tm)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.tm, err)
} else if !bytes.Equal(b, convert.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.tm, b, convert.wantCborData)
}
})
}
}
}
func parseTime(layout string, value string) time.Time {
tm, err := time.Parse(layout, value)
if err != nil {
panic(err)
}
return tm
}
func TestInvalidTimeMode(t *testing.T) {
wantErrorMsg := "cbor: invalid TimeMode 100"
_, err := EncOptions{Time: TimeMode(100)}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestMarshalStructTag1(t *testing.T) {
type strc struct {
A string `cbor:"a"`
B string `cbor:"b"`
C string `cbor:"c"`
}
v := strc{
A: "A",
B: "B",
C: "C",
}
want := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"}
if b, err := Marshal(v); err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = %v, want %v", v, b, want)
}
}
func TestMarshalStructTag2(t *testing.T) {
type strc struct {
A string `json:"a"`
B string `json:"b"`
C string `json:"c"`
}
v := strc{
A: "A",
B: "B",
C: "C",
}
want := hexDecode("a3616161416162614261636143") // {"a":"A", "b":"B", "c":"C"}
if b, err := Marshal(v); err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = %v, want %v", v, b, want)
}
}
func TestMarshalStructTag3(t *testing.T) {
type strc struct {
A string `json:"x" cbor:"a"`
B string `json:"y" cbor:"b"`
C string `json:"z"`
}
v := strc{
A: "A",
B: "B",
C: "C",
}
want := hexDecode("a36161614161626142617a6143") // {"a":"A", "b":"B", "z":"C"}
if b, err := Marshal(v); err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = %v, want %v", v, b, want)
}
}
func TestMarshalStructTag4(t *testing.T) {
type strc struct {
A string `json:"x" cbor:"a"`
B string `json:"y" cbor:"b"`
C string `json:"-"`
}
v := strc{
A: "A",
B: "B",
C: "C",
}
want := hexDecode("a26161614161626142") // {"a":"A", "b":"B"}
if b, err := Marshal(v); err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = %v, want %v", v, b, want)
}
}
func TestMarshalStructLongFieldName(t *testing.T) {
type strc struct {
A string `cbor:"a"`
B string `cbor:"abcdefghijklmnopqrstuvwxyz"`
C string `cbor:"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn"`
}
v := strc{
A: "A",
B: "B",
C: "C",
}
want := hexDecode("a361616141781a6162636465666768696a6b6c6d6e6f707172737475767778797a614278426162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6143") // {"a":"A", "abcdefghijklmnopqrstuvwxyz":"B", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn":"C"}
if b, err := Marshal(v); err != nil {
t.Errorf("Marshal(%+v) returned error %v", v, err)
} else if !bytes.Equal(b, want) {
t.Errorf("Marshal(%+v) = %v, want %v", v, b, want)
}
}
func TestMarshalRawMessageValue(t *testing.T) {
type (
T1 struct {
M RawMessage `cbor:",omitempty"`
}
T2 struct {
M *RawMessage `cbor:",omitempty"`
}
)
var (
rawNil = RawMessage(nil)
rawEmpty = RawMessage([]byte{})
raw = RawMessage([]byte{0x01})
)
tests := []struct {
obj interface{}
want []byte
}{
// Test with nil RawMessage.
{rawNil, []byte{0xf6}},
{&rawNil, []byte{0xf6}},
{[]interface{}{rawNil}, []byte{0x81, 0xf6}},
{&[]interface{}{rawNil}, []byte{0x81, 0xf6}},
{[]interface{}{&rawNil}, []byte{0x81, 0xf6}},
{&[]interface{}{&rawNil}, []byte{0x81, 0xf6}},
{struct{ M RawMessage }{rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&struct{ M RawMessage }{rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{struct{ M *RawMessage }{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&struct{ M *RawMessage }{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{map[string]interface{}{"M": rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&map[string]interface{}{"M": rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{map[string]interface{}{"M": &rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&map[string]interface{}{"M": &rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{T1{rawNil}, []byte{0xa0}},
{T2{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&T1{rawNil}, []byte{0xa0}},
{&T2{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
// Test with empty, but non-nil, RawMessage.
{rawEmpty, []byte{0xf6}},
{&rawEmpty, []byte{0xf6}},
{[]interface{}{rawEmpty}, []byte{0x81, 0xf6}},
{&[]interface{}{rawEmpty}, []byte{0x81, 0xf6}},
{[]interface{}{&rawEmpty}, []byte{0x81, 0xf6}},
{&[]interface{}{&rawEmpty}, []byte{0x81, 0xf6}},
{struct{ M RawMessage }{rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&struct{ M RawMessage }{rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{struct{ M *RawMessage }{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&struct{ M *RawMessage }{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{map[string]interface{}{"M": rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&map[string]interface{}{"M": rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{map[string]interface{}{"M": &rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&map[string]interface{}{"M": &rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{T1{rawEmpty}, []byte{0xa0}},
{T2{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
{&T1{rawEmpty}, []byte{0xa0}},
{&T2{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
// Test with RawMessage with some data.
{raw, []byte{0x01}},
{&raw, []byte{0x01}},
{[]interface{}{raw}, []byte{0x81, 0x01}},
{&[]interface{}{raw}, []byte{0x81, 0x01}},
{[]interface{}{&raw}, []byte{0x81, 0x01}},
{&[]interface{}{&raw}, []byte{0x81, 0x01}},
{struct{ M RawMessage }{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{&struct{ M RawMessage }{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{struct{ M *RawMessage }{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{&struct{ M *RawMessage }{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{map[string]interface{}{"M": raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{&map[string]interface{}{"M": raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{map[string]interface{}{"M": &raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{&map[string]interface{}{"M": &raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{T1{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{T2{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{&T1{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
{&T2{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
}
for _, tc := range tests {
b, err := Marshal(tc.obj)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.obj, err)
}
if !bytes.Equal(b, tc.want) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.want)
}
}
}
func TestCyclicDataStructure(t *testing.T) {
type Node struct {
V int `cbor:"v"`
N *Node `cbor:"n,omitempty"`
}
v := Node{1, &Node{2, &Node{3, nil}}} // linked list: 1, 2, 3
wantCborData := []byte{0xa2, 0x61, 0x76, 0x01, 0x61, 0x6e, 0xa2, 0x61, 0x76, 0x02, 0x61, 0x6e, 0xa1, 0x61, 0x76, 0x03} // {v: 1, n: {v: 2, n: {v: 3}}}
cborData, err := Marshal(v)
if err != nil {
t.Fatalf("Marshal(%v) returned error %v", v, err)
}
if !bytes.Equal(wantCborData, cborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, cborData, wantCborData)
}
var v1 Node
if err = Unmarshal(cborData, &v1); err != nil {
t.Fatalf("Unmarshal(0x%x) returned error %v", cborData, err)
}
if !reflect.DeepEqual(v, v1) {
t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", cborData, v1, v)
}
}
func TestMarshalUnmarshalStructKeyAsInt(t *testing.T) {
type T struct {
F1 int `cbor:"1,omitempty,keyasint"`
F2 int `cbor:"2,omitempty"`
F3 int `cbor:"-3,omitempty,keyasint"`
}
testCases := []struct {
name string
obj interface{}
wantCborData []byte
}{
{
"Zero value struct",
T{},
hexDecode("a0"), // {}
},
{
"Initialized value struct",
T{F1: 1, F2: 2, F3: 3},
hexDecode("a301012203613202"), // {1: 1, -3: 3, "2": 2}
},
}
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := em.Marshal(tc.obj)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.obj, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData)
}
var v2 T
if err := Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
}
if !reflect.DeepEqual(tc.obj, v2) {
t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", b, v2, tc.obj)
}
})
}
}
func TestMarshalStructKeyAsIntNumError(t *testing.T) {
type T1 struct {
F1 int `cbor:"2.0,keyasint"`
}
type T2 struct {
F1 int `cbor:"-18446744073709551616,keyasint"`
}
testCases := []struct {
name string
obj interface{}
wantErrorMsg string
}{
{
name: "float as key",
obj: T1{},
wantErrorMsg: "cbor: failed to parse field name \"2.0\" to int",
},
{
name: "out of range int as key",
obj: T2{},
wantErrorMsg: "cbor: failed to parse field name \"-18446744073709551616\" to int",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := Marshal(tc.obj)
if err == nil {
t.Errorf("Marshal(%+v) didn't return an error, want error %q", tc.obj, tc.wantErrorMsg)
} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
t.Errorf("Marshal(%v) error %v, want %v", tc.obj, err.Error(), tc.wantErrorMsg)
} else if b != nil {
t.Errorf("Marshal(%v) = 0x%x, want nil", tc.obj, b)
}
})
}
}
func TestMarshalUnmarshalStructToArray(t *testing.T) {
type T1 struct {
M int `cbor:",omitempty"`
}
type T2 struct {
N int `cbor:",omitempty"`
O int `cbor:",omitempty"`
}
type T struct {
_ struct{} `cbor:",toarray"`
A int `cbor:",omitempty"`
B T1 // nested struct
T1 // embedded struct
*T2 // embedded struct
}
testCases := []struct {
name string
obj T
wantCborData []byte
}{
{
"Zero value struct (test omitempty)",
T{},
hexDecode("8500a000f6f6"), // [0, {}, 0, nil, nil]
},
{
"Initialized struct",
T{A: 24, B: T1{M: 1}, T1: T1{M: 2}, T2: &T2{N: 3, O: 4}},
hexDecode("851818a1614d01020304"), // [24, {M: 1}, 2, 3, 4]
},
{
"Null pointer to embedded struct",
T{A: 24, B: T1{M: 1}, T1: T1{M: 2}},
hexDecode("851818a1614d0102f6f6"), // [24, {M: 1}, 2, nil, nil]
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := Marshal(tc.obj)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.obj, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData)
}
// SortMode should be ignored for struct to array encoding
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned error %v", err)
}
b, err = em.Marshal(tc.obj)
if err != nil {
t.Errorf("Marshal(%+v) returned error %v", tc.obj, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.obj, b, tc.wantCborData)
}
var v2 T
if err := Unmarshal(b, &v2); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
}
if tc.obj.T2 == nil {
if !reflect.DeepEqual(*(v2.T2), T2{}) {
t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", b, v2, tc.obj)
}
v2.T2 = nil
}
if !reflect.DeepEqual(tc.obj, v2) {
t.Errorf("Unmarshal(0x%x) returned %+v, want %+v", b, v2, tc.obj)
}
})
}
}
func TestMapSort(t *testing.T) {
m := make(map[interface{}]bool)
m[10] = true
m[100] = true
m[-1] = true
m["z"] = true
m["aa"] = true
m[[1]int{100}] = true
m[[1]int{-1}] = true
m[false] = true
lenFirstSortedCborData := hexDecode("a80af520f5f4f51864f5617af58120f5626161f5811864f5") // sorted keys: 10, -1, false, 100, "z", [-1], "aa", [100]
bytewiseSortedCborData := hexDecode("a80af51864f520f5617af5626161f5811864f58120f5f4f5") // sorted keys: 10, 100, -1, "z", "aa", [100], [-1], false
testCases := []struct {
name string
opts EncOptions
wantCborData []byte
}{
{"Length first sort", EncOptions{Sort: SortLengthFirst}, lenFirstSortedCborData},
{"Bytewise sort", EncOptions{Sort: SortBytewiseLexical}, bytewiseSortedCborData},
{"CBOR canonical sort", EncOptions{Sort: SortCanonical}, lenFirstSortedCborData},
{"CTAP2 canonical sort", EncOptions{Sort: SortCTAP2}, bytewiseSortedCborData},
{"Core deterministic sort", EncOptions{Sort: SortCoreDeterministic}, bytewiseSortedCborData},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
em, err := tc.opts.EncMode()
if err != nil {
t.Errorf("EncMode() returned error %v", err)
}
b, err := em.Marshal(m)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", m, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", m, b, tc.wantCborData)
}
})
}
}
func TestStructSort(t *testing.T) {
type T struct {
A bool `cbor:"aa"`
B bool `cbor:"z"`
C bool `cbor:"-1,keyasint"`
D bool `cbor:"100,keyasint"`
E bool `cbor:"10,keyasint"`
}
var v T
unsortedCborData := hexDecode("a5626161f4617af420f41864f40af4") // unsorted fields: "aa", "z", -1, 100, 10
lenFirstSortedCborData := hexDecode("a50af420f41864f4617af4626161f4") // sorted fields: 10, -1, 100, "z", "aa",
bytewiseSortedCborData := hexDecode("a50af41864f420f4617af4626161f4") // sorted fields: 10, 100, -1, "z", "aa"
testCases := []struct {
name string
opts EncOptions
wantCborData []byte
}{
{"No sort", EncOptions{}, unsortedCborData},
{"No sort", EncOptions{Sort: SortNone}, unsortedCborData},
{"Length first sort", EncOptions{Sort: SortLengthFirst}, lenFirstSortedCborData},
{"Bytewise sort", EncOptions{Sort: SortBytewiseLexical}, bytewiseSortedCborData},
{"CBOR canonical sort", EncOptions{Sort: SortCanonical}, lenFirstSortedCborData},
{"CTAP2 canonical sort", EncOptions{Sort: SortCTAP2}, bytewiseSortedCborData},
{"Core deterministic sort", EncOptions{Sort: SortCoreDeterministic}, bytewiseSortedCborData},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
em, err := tc.opts.EncMode()
if err != nil {
t.Errorf("EncMode() returned error %v", err)
}
b, err := em.Marshal(v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", v, err)
}
if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, tc.wantCborData)
}
})
}
}
func TestInvalidSort(t *testing.T) {
wantErrorMsg := "cbor: invalid SortMode 100"
_, err := EncOptions{Sort: SortMode(100)}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestTypeAlias(t *testing.T) { //nolint:dupl,unconvert
type myBool = bool
type myUint = uint
type myUint8 = uint8
type myUint16 = uint16
type myUint32 = uint32
type myUint64 = uint64
type myInt = int
type myInt8 = int8
type myInt16 = int16
type myInt32 = int32
type myInt64 = int64
type myFloat32 = float32
type myFloat64 = float64
type myString = string
type myByteSlice = []byte
type myIntSlice = []int
type myIntArray = [4]int
type myMapIntInt = map[int]int
testCases := []roundTripTest{
{
name: "bool alias",
obj: myBool(true),
wantCborData: hexDecode("f5"),
},
{
name: "uint alias",
obj: myUint(0),
wantCborData: hexDecode("00"),
},
{
name: "uint8 alias",
obj: myUint8(0),
wantCborData: hexDecode("00"),
},
{
name: "uint16 alias",
obj: myUint16(1000),
wantCborData: hexDecode("1903e8"),
},
{
name: "uint32 alias",
obj: myUint32(1000000),
wantCborData: hexDecode("1a000f4240"),
},
{
name: "uint64 alias",
obj: myUint64(1000000000000),
wantCborData: hexDecode("1b000000e8d4a51000"),
},
{
name: "int alias",
obj: myInt(-1),
wantCborData: hexDecode("20"),
},
{
name: "int8 alias",
obj: myInt8(-1),
wantCborData: hexDecode("20"),
},
{
name: "int16 alias",
obj: myInt16(-1000),
wantCborData: hexDecode("3903e7"),
},
{
name: "int32 alias",
obj: myInt32(-1000),
wantCborData: hexDecode("3903e7"),
},
{
name: "int64 alias",
obj: myInt64(-1000),
wantCborData: hexDecode("3903e7"),
},
{
name: "float32 alias",
obj: myFloat32(100000.0),
wantCborData: hexDecode("fa47c35000"),
},
{
name: "float64 alias",
obj: myFloat64(1.1),
wantCborData: hexDecode("fb3ff199999999999a"),
},
{
name: "string alias",
obj: myString("a"),
wantCborData: hexDecode("6161"),
},
{
name: "[]byte alias",
obj: myByteSlice([]byte{1, 2, 3, 4}), //nolint:unconvert
wantCborData: hexDecode("4401020304"),
},
{
name: "[]int alias",
obj: myIntSlice([]int{1, 2, 3, 4}), //nolint:unconvert
wantCborData: hexDecode("8401020304"),
},
{
name: "[4]int alias",
obj: myIntArray([...]int{1, 2, 3, 4}), //nolint:unconvert
wantCborData: hexDecode("8401020304"),
},
{
name: "map[int]int alias",
obj: myMapIntInt(map[int]int{1: 2, 3: 4}), //nolint:unconvert
wantCborData: hexDecode("a201020304"),
},
}
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
dm, err := DecOptions{}.DecMode()
if err != nil {
t.Errorf("DecMode() returned an error %v", err)
}
testRoundTrip(t, testCases, em, dm)
}
func TestNewTypeWithBuiltinUnderlyingType(t *testing.T) { //nolint:dupl
type myBool bool
type myUint uint
type myUint8 uint8
type myUint16 uint16
type myUint32 uint32
type myUint64 uint64
type myInt int
type myInt8 int8
type myInt16 int16
type myInt32 int32
type myInt64 int64
type myFloat32 float32
type myFloat64 float64
type myString string
type myByteSlice []byte
type myIntSlice []int
type myIntArray [4]int
type myMapIntInt map[int]int
testCases := []roundTripTest{
{
name: "bool alias",
obj: myBool(true),
wantCborData: hexDecode("f5"),
},
{
name: "uint alias",
obj: myUint(0),
wantCborData: hexDecode("00"),
},
{
name: "uint8 alias",
obj: myUint8(0),
wantCborData: hexDecode("00"),
},
{
name: "uint16 alias",
obj: myUint16(1000),
wantCborData: hexDecode("1903e8"),
},
{
name: "uint32 alias",
obj: myUint32(1000000),
wantCborData: hexDecode("1a000f4240"),
},
{
name: "uint64 alias",
obj: myUint64(1000000000000),
wantCborData: hexDecode("1b000000e8d4a51000"),
},
{
name: "int alias",
obj: myInt(-1),
wantCborData: hexDecode("20"),
},
{
name: "int8 alias",
obj: myInt8(-1),
wantCborData: hexDecode("20"),
},
{
name: "int16 alias",
obj: myInt16(-1000),
wantCborData: hexDecode("3903e7"),
},
{
name: "int32 alias",
obj: myInt32(-1000),
wantCborData: hexDecode("3903e7"),
},
{
name: "int64 alias",
obj: myInt64(-1000),
wantCborData: hexDecode("3903e7"),
},
{
name: "float32 alias",
obj: myFloat32(100000.0),
wantCborData: hexDecode("fa47c35000"),
},
{
name: "float64 alias",
obj: myFloat64(1.1),
wantCborData: hexDecode("fb3ff199999999999a"),
},
{
name: "string alias",
obj: myString("a"),
wantCborData: hexDecode("6161"),
},
{
name: "[]byte alias",
obj: myByteSlice([]byte{1, 2, 3, 4}),
wantCborData: hexDecode("4401020304"),
},
{
name: "[]int alias",
obj: myIntSlice([]int{1, 2, 3, 4}),
wantCborData: hexDecode("8401020304"),
},
{
name: "[4]int alias",
obj: myIntArray([...]int{1, 2, 3, 4}),
wantCborData: hexDecode("8401020304"),
},
{
name: "map[int]int alias",
obj: myMapIntInt(map[int]int{1: 2, 3: 4}),
wantCborData: hexDecode("a201020304"),
},
}
em, err := EncOptions{Sort: SortCanonical}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
dm, err := DecOptions{}.DecMode()
if err != nil {
t.Errorf("DecMode() returned an error %v", err)
}
testRoundTrip(t, testCases, em, dm)
}
func TestShortestFloat16(t *testing.T) {
testCases := []struct {
name string
f64 float64
wantCborData []byte
}{
// Data from RFC 7049 appendix A
{"Shrink to float16", 0.0, hexDecode("f90000")},
{"Shrink to float16", 1.0, hexDecode("f93c00")},
{"Shrink to float16", 1.5, hexDecode("f93e00")},
{"Shrink to float16", 65504.0, hexDecode("f97bff")},
{"Shrink to float16", 5.960464477539063e-08, hexDecode("f90001")},
{"Shrink to float16", 6.103515625e-05, hexDecode("f90400")},
{"Shrink to float16", -4.0, hexDecode("f9c400")},
// Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
{"Shrink to float16", 0.333251953125, hexDecode("f93555")},
// Data from 7049bis 4.2.1 and 5.5
{"Shrink to float16", 5.5, hexDecode("f94580")},
// Data from RFC 7049 appendix A
{"Shrink to float32", 100000.0, hexDecode("fa47c35000")},
{"Shrink to float32", 3.4028234663852886e+38, hexDecode("fa7f7fffff")},
// Data from 7049bis 4.2.1 and 5.5
{"Shrink to float32", 5555.5, hexDecode("fa45ad9c00")},
{"Shrink to float32", 1000000.5, hexDecode("fa49742408")},
// Data from RFC 7049 appendix A
{"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")},
}
em, err := EncOptions{ShortestFloat: ShortestFloat16}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := em.Marshal(tc.f64)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.f64, err)
} else if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData)
}
var f64 float64
if err = Unmarshal(b, &f64); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if f64 != tc.f64 {
t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64)
}
})
}
}
/*
func TestShortestFloat32(t *testing.T) {
testCases := []struct {
name string
f64 float64
wantCborData []byte
}{
// Data from RFC 7049 appendix A
{"Shrink to float32", 0.0, hexDecode("fa00000000")},
{"Shrink to float32", 1.0, hexDecode("fa3f800000")},
{"Shrink to float32", 1.5, hexDecode("fa3fc00000")},
{"Shrink to float32", 65504.0, hexDecode("fa477fe000")},
{"Shrink to float32", 5.960464477539063e-08, hexDecode("fa33800000")},
{"Shrink to float32", 6.103515625e-05, hexDecode("fa38800000")},
{"Shrink to float32", -4.0, hexDecode("fac0800000")},
// Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
{"Shrink to float32", 0.333251953125, hexDecode("fa3eaaa000")},
// Data from 7049bis 4.2.1 and 5.5
{"Shrink to float32", 5.5, hexDecode("fa40b00000")},
// Data from RFC 7049 appendix A
{"Shrink to float32", 100000.0, hexDecode("fa47c35000")},
{"Shrink to float32", 3.4028234663852886e+38, hexDecode("fa7f7fffff")},
// Data from 7049bis 4.2.1 and 5.5
{"Shrink to float32", 5555.5, hexDecode("fa45ad9c00")},
{"Shrink to float32", 1000000.5, hexDecode("fa49742408")},
// Data from RFC 7049 appendix A
{"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")},
}
em, err := EncOptions{ShortestFloat: ShortestFloat32}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := em.Marshal(tc.f64)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.f64, err)
} else if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData)
}
var f64 float64
if err = Unmarshal(b, &f64); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if f64 != tc.f64 {
t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64)
}
})
}
}
func TestShortestFloat64(t *testing.T) {
testCases := []struct {
name string
f64 float64
wantCborData []byte
}{
// Data from RFC 7049 appendix A
{"Shrink to float64", 0.0, hexDecode("fb0000000000000000")},
{"Shrink to float64", 1.0, hexDecode("fb3ff0000000000000")},
{"Shrink to float64", 1.5, hexDecode("fb3ff8000000000000")},
{"Shrink to float64", 65504.0, hexDecode("fb40effc0000000000")},
{"Shrink to float64", 5.960464477539063e-08, hexDecode("fb3e70000000000000")},
{"Shrink to float64", 6.103515625e-05, hexDecode("fb3f10000000000000")},
{"Shrink to float64", -4.0, hexDecode("fbc010000000000000")},
// Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
{"Shrink to float64", 0.333251953125, hexDecode("fb3fd5540000000000")},
// Data from 7049bis 4.2.1 and 5.5
{"Shrink to float64", 5.5, hexDecode("fb4016000000000000")},
// Data from RFC 7049 appendix A
{"Shrink to float64", 100000.0, hexDecode("fb40f86a0000000000")},
{"Shrink to float64", 3.4028234663852886e+38, hexDecode("fb47efffffe0000000")},
// Data from 7049bis 4.2.1 and 5.5
{"Shrink to float64", 5555.5, hexDecode("fb40b5b38000000000")},
{"Shrink to float64", 1000000.5, hexDecode("fb412e848100000000")},
// Data from RFC 7049 appendix A
{"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")},
}
em, err := EncOptions{ShortestFloat: ShortestFloat64}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := em.Marshal(tc.f64)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.f64, err)
} else if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData)
}
var f64 float64
if err = Unmarshal(b, &f64); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if f64 != tc.f64 {
t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64)
}
})
}
}
*/
func TestShortestFloatNone(t *testing.T) {
testCases := []struct {
name string
f interface{}
wantCborData []byte
}{
// Data from RFC 7049 appendix A
{"float32", float32(0.0), hexDecode("fa00000000")},
{"float64", float64(0.0), hexDecode("fb0000000000000000")},
{"float32", float32(1.0), hexDecode("fa3f800000")},
{"float64", float64(1.0), hexDecode("fb3ff0000000000000")},
{"float32", float32(1.5), hexDecode("fa3fc00000")},
{"float64", float64(1.5), hexDecode("fb3ff8000000000000")},
{"float32", float32(65504.0), hexDecode("fa477fe000")},
{"float64", float64(65504.0), hexDecode("fb40effc0000000000")},
{"float32", float32(5.960464477539063e-08), hexDecode("fa33800000")},
{"float64", float64(5.960464477539063e-08), hexDecode("fb3e70000000000000")},
{"float32", float32(6.103515625e-05), hexDecode("fa38800000")},
{"float64", float64(6.103515625e-05), hexDecode("fb3f10000000000000")},
{"float32", float32(-4.0), hexDecode("fac0800000")},
{"float64", float64(-4.0), hexDecode("fbc010000000000000")},
// Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
{"float32", float32(0.333251953125), hexDecode("fa3eaaa000")},
{"float64", float64(0.333251953125), hexDecode("fb3fd5540000000000")},
// Data from 7049bis 4.2.1 and 5.5
{"float32", float32(5.5), hexDecode("fa40b00000")},
{"float64", float64(5.5), hexDecode("fb4016000000000000")},
// Data from RFC 7049 appendix A
{"float32", float32(100000.0), hexDecode("fa47c35000")},
{"float64", float64(100000.0), hexDecode("fb40f86a0000000000")},
{"float32", float32(3.4028234663852886e+38), hexDecode("fa7f7fffff")},
{"float64", float64(3.4028234663852886e+38), hexDecode("fb47efffffe0000000")},
// Data from 7049bis 4.2.1 and 5.5
{"float32", float32(5555.5), hexDecode("fa45ad9c00")},
{"float64", float64(5555.5), hexDecode("fb40b5b38000000000")},
{"float32", float32(1000000.5), hexDecode("fa49742408")},
{"float64", float64(1000000.5), hexDecode("fb412e848100000000")},
{"float64", float64(1.0e+300), hexDecode("fb7e37e43c8800759c")},
}
em, err := EncOptions{ShortestFloat: ShortestFloatNone}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b, err := em.Marshal(tc.f)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.f, err)
} else if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f, b, tc.wantCborData)
}
if reflect.ValueOf(tc.f).Kind() == reflect.Float32 {
var f32 float32
if err = Unmarshal(b, &f32); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if f32 != tc.f {
t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f32, tc.f)
}
} else {
var f64 float64
if err = Unmarshal(b, &f64); err != nil {
t.Errorf("Unmarshal(0x%x) returned error %v", b, err)
} else if f64 != tc.f {
t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f)
}
}
})
}
}
func TestInvalidShortestFloat(t *testing.T) {
wantErrorMsg := "cbor: invalid ShortestFloatMode 100"
_, err := EncOptions{ShortestFloat: ShortestFloatMode(100)}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestInfConvert(t *testing.T) {
infConvertNoneOpt := EncOptions{InfConvert: InfConvertNone}
infConvertFloat16Opt := EncOptions{InfConvert: InfConvertFloat16}
testCases := []struct {
name string
v interface{}
opts EncOptions
wantCborData []byte
}{
{"float32 -inf no conversion", float32(math.Inf(-1)), infConvertNoneOpt, hexDecode("faff800000")},
{"float32 +inf no conversion", float32(math.Inf(1)), infConvertNoneOpt, hexDecode("fa7f800000")},
{"float64 -inf no conversion", math.Inf(-1), infConvertNoneOpt, hexDecode("fbfff0000000000000")},
{"float64 +inf no conversion", math.Inf(1), infConvertNoneOpt, hexDecode("fb7ff0000000000000")},
{"float32 -inf to float16", float32(math.Inf(-1)), infConvertFloat16Opt, hexDecode("f9fc00")},
{"float32 +inf to float16", float32(math.Inf(1)), infConvertFloat16Opt, hexDecode("f97c00")},
{"float64 -inf to float16", math.Inf(-1), infConvertFloat16Opt, hexDecode("f9fc00")},
{"float64 +inf to float16", math.Inf(1), infConvertFloat16Opt, hexDecode("f97c00")},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
em, err := tc.opts.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
b, err := em.Marshal(tc.v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.v, err)
} else if !bytes.Equal(b, tc.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.v, b, tc.wantCborData)
}
})
}
}
func TestInvalidInfConvert(t *testing.T) {
wantErrorMsg := "cbor: invalid InfConvertMode 100"
_, err := EncOptions{InfConvert: InfConvertMode(100)}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
// Keith Randall's workaround for constant propagation issue https://github.com/golang/go/issues/36400
const (
// qnan 32 bits constants
qnanConst0xffc00001 uint32 = 0xffc00001
qnanConst0x7fc00001 uint32 = 0x7fc00001
qnanConst0xffc02000 uint32 = 0xffc02000
qnanConst0x7fc02000 uint32 = 0x7fc02000
// snan 32 bits constants
snanConst0xff800001 uint32 = 0xff800001
snanConst0x7f800001 uint32 = 0x7f800001
snanConst0xff802000 uint32 = 0xff802000
snanConst0x7f802000 uint32 = 0x7f802000
// qnan 64 bits constants
qnanConst0xfff8000000000001 uint64 = 0xfff8000000000001
qnanConst0x7ff8000000000001 uint64 = 0x7ff8000000000001
qnanConst0xfff8000020000000 uint64 = 0xfff8000020000000
qnanConst0x7ff8000020000000 uint64 = 0x7ff8000020000000
qnanConst0xfffc000000000000 uint64 = 0xfffc000000000000
qnanConst0x7ffc000000000000 uint64 = 0x7ffc000000000000
// snan 64 bits constants
snanConst0xfff0000000000001 uint64 = 0xfff0000000000001
snanConst0x7ff0000000000001 uint64 = 0x7ff0000000000001
snanConst0xfff0000020000000 uint64 = 0xfff0000020000000
snanConst0x7ff0000020000000 uint64 = 0x7ff0000020000000
snanConst0xfff4000000000000 uint64 = 0xfff4000000000000
snanConst0x7ff4000000000000 uint64 = 0x7ff4000000000000
)
var (
// qnan 32 bits variables
qnanVar0xffc00001 uint32 = qnanConst0xffc00001
qnanVar0x7fc00001 uint32 = qnanConst0x7fc00001
qnanVar0xffc02000 uint32 = qnanConst0xffc02000
qnanVar0x7fc02000 uint32 = qnanConst0x7fc02000
// snan 32 bits variables
snanVar0xff800001 uint32 = snanConst0xff800001
snanVar0x7f800001 uint32 = snanConst0x7f800001
snanVar0xff802000 uint32 = snanConst0xff802000
snanVar0x7f802000 uint32 = snanConst0x7f802000
// qnan 64 bits variables
qnanVar0xfff8000000000001 uint64 = qnanConst0xfff8000000000001
qnanVar0x7ff8000000000001 uint64 = qnanConst0x7ff8000000000001
qnanVar0xfff8000020000000 uint64 = qnanConst0xfff8000020000000
qnanVar0x7ff8000020000000 uint64 = qnanConst0x7ff8000020000000
qnanVar0xfffc000000000000 uint64 = qnanConst0xfffc000000000000
qnanVar0x7ffc000000000000 uint64 = qnanConst0x7ffc000000000000
// snan 64 bits variables
snanVar0xfff0000000000001 uint64 = snanConst0xfff0000000000001
snanVar0x7ff0000000000001 uint64 = snanConst0x7ff0000000000001
snanVar0xfff0000020000000 uint64 = snanConst0xfff0000020000000
snanVar0x7ff0000020000000 uint64 = snanConst0x7ff0000020000000
snanVar0xfff4000000000000 uint64 = snanConst0xfff4000000000000
snanVar0x7ff4000000000000 uint64 = snanConst0x7ff4000000000000
)
func TestNaNConvert(t *testing.T) {
nanConvert7e00Opt := EncOptions{NaNConvert: NaNConvert7e00}
nanConvertNoneOpt := EncOptions{NaNConvert: NaNConvertNone}
nanConvertPreserveSignalOpt := EncOptions{NaNConvert: NaNConvertPreserveSignal}
nanConvertQuietOpt := EncOptions{NaNConvert: NaNConvertQuiet}
type nanConvert struct {
opt EncOptions
wantCborData []byte
}
testCases := []struct {
v interface{}
convert []nanConvert
}{
// float32 qNaN dropped payload not zero
{math.Float32frombits(qnanVar0xffc00001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("faffc00001")},
{nanConvertPreserveSignalOpt, hexDecode("faffc00001")},
{nanConvertQuietOpt, hexDecode("faffc00001")},
}},
// float32 qNaN dropped payload not zero
{math.Float32frombits(qnanVar0x7fc00001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fa7fc00001")},
{nanConvertPreserveSignalOpt, hexDecode("fa7fc00001")},
{nanConvertQuietOpt, hexDecode("fa7fc00001")},
}},
// float32 -qNaN dropped payload zero
{math.Float32frombits(qnanVar0xffc02000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("faffc02000")},
{nanConvertPreserveSignalOpt, hexDecode("f9fe01")},
{nanConvertQuietOpt, hexDecode("f9fe01")},
}},
// float32 qNaN dropped payload zero
{math.Float32frombits(qnanVar0x7fc02000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fa7fc02000")},
{nanConvertPreserveSignalOpt, hexDecode("f97e01")},
{nanConvertQuietOpt, hexDecode("f97e01")},
}},
// float32 -sNaN dropped payload not zero
{math.Float32frombits(snanVar0xff800001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("faff800001")},
{nanConvertPreserveSignalOpt, hexDecode("faff800001")},
{nanConvertQuietOpt, hexDecode("faffc00001")},
}},
// float32 sNaN dropped payload not zero
{math.Float32frombits(snanVar0x7f800001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fa7f800001")},
{nanConvertPreserveSignalOpt, hexDecode("fa7f800001")},
{nanConvertQuietOpt, hexDecode("fa7fc00001")},
}},
// float32 -sNaN dropped payload zero
{math.Float32frombits(snanVar0xff802000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("faff802000")},
{nanConvertPreserveSignalOpt, hexDecode("f9fc01")},
{nanConvertQuietOpt, hexDecode("f9fe01")},
}},
// float32 sNaN dropped payload zero
{math.Float32frombits(snanVar0x7f802000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fa7f802000")},
{nanConvertPreserveSignalOpt, hexDecode("f97c01")},
{nanConvertQuietOpt, hexDecode("f97e01")},
}},
// float64 -qNaN dropped payload not zero
{math.Float64frombits(qnanVar0xfff8000000000001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fbfff8000000000001")},
{nanConvertPreserveSignalOpt, hexDecode("fbfff8000000000001")},
{nanConvertQuietOpt, hexDecode("fbfff8000000000001")},
}},
// float64 qNaN dropped payload not zero
{math.Float64frombits(qnanVar0x7ff8000000000001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fb7ff8000000000001")},
{nanConvertPreserveSignalOpt, hexDecode("fb7ff8000000000001")},
{nanConvertQuietOpt, hexDecode("fb7ff8000000000001")},
}},
// float64 -qNaN dropped payload zero
{math.Float64frombits(qnanVar0xfff8000020000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fbfff8000020000000")},
{nanConvertPreserveSignalOpt, hexDecode("faffc00001")},
{nanConvertQuietOpt, hexDecode("faffc00001")},
}},
// float64 qNaN dropped payload zero
{math.Float64frombits(qnanVar0x7ff8000020000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fb7ff8000020000000")},
{nanConvertPreserveSignalOpt, hexDecode("fa7fc00001")},
{nanConvertQuietOpt, hexDecode("fa7fc00001")},
}},
// float64 -qNaN dropped payload zero
{math.Float64frombits(qnanVar0xfffc000000000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fbfffc000000000000")},
{nanConvertPreserveSignalOpt, hexDecode("f9ff00")},
{nanConvertQuietOpt, hexDecode("f9ff00")},
}},
// float64 qNaN dropped payload zero
{math.Float64frombits(qnanVar0x7ffc000000000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fb7ffc000000000000")},
{nanConvertPreserveSignalOpt, hexDecode("f97f00")},
{nanConvertQuietOpt, hexDecode("f97f00")},
}},
// float64 -sNaN dropped payload not zero
{math.Float64frombits(snanVar0xfff0000000000001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fbfff0000000000001")},
{nanConvertPreserveSignalOpt, hexDecode("fbfff0000000000001")},
{nanConvertQuietOpt, hexDecode("fbfff8000000000001")},
}},
// float64 sNaN dropped payload not zero
{math.Float64frombits(snanVar0x7ff0000000000001), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fb7ff0000000000001")},
{nanConvertPreserveSignalOpt, hexDecode("fb7ff0000000000001")},
{nanConvertQuietOpt, hexDecode("fb7ff8000000000001")},
}},
// float64 -sNaN dropped payload zero
{math.Float64frombits(snanVar0xfff0000020000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fbfff0000020000000")},
{nanConvertPreserveSignalOpt, hexDecode("faff800001")},
{nanConvertQuietOpt, hexDecode("faffc00001")},
}},
// float64 sNaN dropped payload zero
{math.Float64frombits(snanVar0x7ff0000020000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fb7ff0000020000000")},
{nanConvertPreserveSignalOpt, hexDecode("fa7f800001")},
{nanConvertQuietOpt, hexDecode("fa7fc00001")},
}},
// float64 -sNaN dropped payload zero
{math.Float64frombits(snanVar0xfff4000000000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fbfff4000000000000")},
{nanConvertPreserveSignalOpt, hexDecode("f9fd00")},
{nanConvertQuietOpt, hexDecode("f9ff00")},
}},
// float64 sNaN dropped payload zero
{math.Float64frombits(snanVar0x7ff4000000000000), []nanConvert{
{nanConvert7e00Opt, hexDecode("f97e00")},
{nanConvertNoneOpt, hexDecode("fb7ff4000000000000")},
{nanConvertPreserveSignalOpt, hexDecode("f97d00")},
{nanConvertQuietOpt, hexDecode("f97f00")},
}},
}
for _, tc := range testCases {
for _, convert := range tc.convert {
var convertName string
switch convert.opt.NaNConvert {
case NaNConvert7e00:
convertName = "Convert7e00"
case NaNConvertNone:
convertName = "ConvertNone"
case NaNConvertPreserveSignal:
convertName = "ConvertPreserveSignal"
case NaNConvertQuiet:
convertName = "ConvertQuiet"
}
var vName string
switch v := tc.v.(type) {
case float32:
vName = fmt.Sprintf("0x%x", math.Float32bits(v))
case float64:
vName = fmt.Sprintf("0x%x", math.Float64bits(v))
}
name := convertName + "_" + vName
t.Run(name, func(t *testing.T) {
em, err := convert.opt.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
b, err := em.Marshal(tc.v)
if err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.v, err)
} else if !bytes.Equal(b, convert.wantCborData) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.v, b, convert.wantCborData)
}
})
}
}
}
func TestInvalidNaNConvert(t *testing.T) {
wantErrorMsg := "cbor: invalid NaNConvertMode 100"
_, err := EncOptions{NaNConvert: NaNConvertMode(100)}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestMarshalSenML(t *testing.T) {
// Data from https://tools.ietf.org/html/rfc8428#section-6
// Data contains 13 floating-point numbers.
cborData := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
testCases := []struct {
name string
opts EncOptions
}{
{"EncOptions ShortestFloatNone", EncOptions{}},
{"EncOptions ShortestFloat16", EncOptions{ShortestFloat: ShortestFloat16}},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var v []SenMLRecord
if err := Unmarshal(cborData, &v); err != nil {
t.Errorf("Marshal() returned error %v", err)
}
em, err := tc.opts.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
b, err := em.Marshal(v)
if err != nil {
t.Errorf("Unmarshal() returned error %v ", err)
}
var v2 []SenMLRecord
if err := Unmarshal(b, &v2); err != nil {
t.Errorf("Marshal() returned error %v", err)
}
if !reflect.DeepEqual(v, v2) {
t.Errorf("SenML round-trip failed: v1 %+v, v2 %+v", v, v2)
}
})
}
}
func TestCanonicalEncOptions(t *testing.T) { //nolint:dupl
wantSortMode := SortCanonical
wantShortestFloat := ShortestFloat16
wantNaNConvert := NaNConvert7e00
wantInfConvert := InfConvertFloat16
wantErrorMsg := "cbor: indefinite-length array isn't allowed"
em, err := CanonicalEncOptions().EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
opts := em.EncOptions()
if opts.Sort != wantSortMode {
t.Errorf("CanonicalEncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode)
}
if opts.ShortestFloat != wantShortestFloat {
t.Errorf("CanonicalEncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat)
}
if opts.NaNConvert != wantNaNConvert {
t.Errorf("CanonicalEncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert)
}
if opts.InfConvert != wantInfConvert {
t.Errorf("CanonicalEncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert)
}
enc := em.NewEncoder(ioutil.Discard)
if err := enc.StartIndefiniteArray(); err == nil {
t.Errorf("StartIndefiniteArray() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestCTAP2EncOptions(t *testing.T) { //nolint:dupl
wantSortMode := SortCTAP2
wantShortestFloat := ShortestFloatNone
wantNaNConvert := NaNConvertNone
wantInfConvert := InfConvertNone
wantErrorMsg := "cbor: indefinite-length array isn't allowed"
em, err := CTAP2EncOptions().EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
opts := em.EncOptions()
if opts.Sort != wantSortMode {
t.Errorf("CTAP2EncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode)
}
if opts.ShortestFloat != wantShortestFloat {
t.Errorf("CTAP2EncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat)
}
if opts.NaNConvert != wantNaNConvert {
t.Errorf("CTAP2EncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert)
}
if opts.InfConvert != wantInfConvert {
t.Errorf("CTAP2EncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert)
}
enc := em.NewEncoder(ioutil.Discard)
if err := enc.StartIndefiniteArray(); err == nil {
t.Errorf("StartIndefiniteArray() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestCoreDetEncOptions(t *testing.T) { //nolint:dupl
wantSortMode := SortCoreDeterministic
wantShortestFloat := ShortestFloat16
wantNaNConvert := NaNConvert7e00
wantInfConvert := InfConvertFloat16
wantErrorMsg := "cbor: indefinite-length array isn't allowed"
em, err := CoreDetEncOptions().EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
opts := em.EncOptions()
if opts.Sort != wantSortMode {
t.Errorf("CoreDetEncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode)
}
if opts.ShortestFloat != wantShortestFloat {
t.Errorf("CoreDetEncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat)
}
if opts.NaNConvert != wantNaNConvert {
t.Errorf("CoreDetEncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert)
}
if opts.InfConvert != wantInfConvert {
t.Errorf("CoreDetEncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert)
}
enc := em.NewEncoder(ioutil.Discard)
if err := enc.StartIndefiniteArray(); err == nil {
t.Errorf("StartIndefiniteArray() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestPreferredUnsortedEncOptions(t *testing.T) {
wantSortMode := SortNone
wantShortestFloat := ShortestFloat16
wantNaNConvert := NaNConvert7e00
wantInfConvert := InfConvertFloat16
em, err := PreferredUnsortedEncOptions().EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
opts := em.EncOptions()
if opts.Sort != wantSortMode {
t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with Sort %d, want %d", opts.Sort, wantSortMode)
}
if opts.ShortestFloat != wantShortestFloat {
t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with ShortestFloat %d, want %d", opts.ShortestFloat, wantShortestFloat)
}
if opts.NaNConvert != wantNaNConvert {
t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with NaNConvert %d, want %d", opts.NaNConvert, wantNaNConvert)
}
if opts.InfConvert != wantInfConvert {
t.Errorf("PreferredUnsortedEncOptions() returned EncOptions with InfConvert %d, want %d", opts.InfConvert, wantInfConvert)
}
enc := em.NewEncoder(ioutil.Discard)
if err := enc.StartIndefiniteArray(); err != nil {
t.Errorf("StartIndefiniteArray() returned error %v", err)
}
}
func TestEncModeInvalidIndefiniteLengthMode(t *testing.T) {
wantErrorMsg := "cbor: invalid IndefLength 101"
_, err := EncOptions{IndefLength: 101}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestEncModeInvalidTagsMode(t *testing.T) {
wantErrorMsg := "cbor: invalid TagsMd 101"
_, err := EncOptions{TagsMd: 101}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestEncModeInvalidBigIntConvertMode(t *testing.T) {
wantErrorMsg := "cbor: invalid BigIntConvertMode 101"
_, err := EncOptions{BigIntConvert: 101}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestEncOptions(t *testing.T) {
opts1 := EncOptions{
Sort: SortBytewiseLexical,
ShortestFloat: ShortestFloat16,
NaNConvert: NaNConvertPreserveSignal,
InfConvert: InfConvertNone,
BigIntConvert: BigIntConvertNone,
Time: TimeRFC3339Nano,
TimeTag: EncTagRequired,
IndefLength: IndefLengthForbidden,
TagsMd: TagsAllowed,
}
em, err := opts1.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
} else {
opts2 := em.EncOptions()
if !reflect.DeepEqual(opts1, opts2) {
t.Errorf("EncOptions->EncMode->EncOptions returned different values: %v, %v", opts1, opts2)
}
}
}
func TestEncModeInvalidTimeTag(t *testing.T) {
wantErrorMsg := "cbor: invalid TimeTag 100"
_, err := EncOptions{TimeTag: 100}.EncMode()
if err == nil {
t.Errorf("EncMode() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestEncIndefiniteLengthOption(t *testing.T) {
// Default option allows indefinite length items
var buf bytes.Buffer
enc := NewEncoder(&buf)
if err := enc.StartIndefiniteByteString(); err != nil {
t.Errorf("StartIndefiniteByteString() returned an error %v", err)
}
if err := enc.StartIndefiniteTextString(); err != nil {
t.Errorf("StartIndefiniteTextString() returned an error %v", err)
}
if err := enc.StartIndefiniteArray(); err != nil {
t.Errorf("StartIndefiniteArray() returned an error %v", err)
}
if err := enc.StartIndefiniteMap(); err != nil {
t.Errorf("StartIndefiniteMap() returned an error %v", err)
}
// StartIndefiniteXXX returns error when IndefLength = IndefLengthForbidden
em, _ := EncOptions{IndefLength: IndefLengthForbidden}.EncMode()
enc = em.NewEncoder(&buf)
wantErrorMsg := "cbor: indefinite-length byte string isn't allowed"
if err := enc.StartIndefiniteByteString(); err == nil {
t.Errorf("StartIndefiniteByteString() didn't return an error")
} else if _, ok := err.(*IndefiniteLengthError); !ok {
t.Errorf("StartIndefiniteByteString() error type %T, want *IndefiniteLengthError", err)
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteByteString() returned error %q, want %q", err.Error(), wantErrorMsg)
}
wantErrorMsg = "cbor: indefinite-length UTF-8 text string isn't allowed"
if err := enc.StartIndefiniteTextString(); err == nil {
t.Errorf("StartIndefiniteTextString() didn't return an error")
} else if _, ok := err.(*IndefiniteLengthError); !ok {
t.Errorf("StartIndefiniteTextString() error type %T, want *IndefiniteLengthError", err)
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteTextString() returned error %q, want %q", err.Error(), wantErrorMsg)
}
wantErrorMsg = "cbor: indefinite-length array isn't allowed"
if err := enc.StartIndefiniteArray(); err == nil {
t.Errorf("StartIndefiniteArray() didn't return an error")
} else if _, ok := err.(*IndefiniteLengthError); !ok {
t.Errorf("StartIndefiniteArray() error type %T, want *IndefiniteLengthError", err)
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteArray() returned error %q, want %q", err.Error(), wantErrorMsg)
}
wantErrorMsg = "cbor: indefinite-length map isn't allowed"
if err := enc.StartIndefiniteMap(); err == nil {
t.Errorf("StartIndefiniteMap() didn't return an error")
} else if _, ok := err.(*IndefiniteLengthError); !ok {
t.Errorf("StartIndefiniteMap() error type %T, want *IndefiniteLengthError", err)
} else if err.Error() != wantErrorMsg {
t.Errorf("StartIndefiniteMap() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestEncTagsMdOption(t *testing.T) {
// Default option allows encoding CBOR tags
tag := Tag{123, "hello"}
if _, err := Marshal(tag); err != nil {
t.Errorf("Marshal() returned an error %v", err)
}
// Create EncMode with TimeTag = EncTagRequired and TagsForbidden option returns error
wantErrorMsg := "cbor: cannot set TagsMd to TagsForbidden when TimeTag is EncTagRequired"
_, err := EncOptions{TimeTag: EncTagRequired, TagsMd: TagsForbidden}.EncMode()
if err == nil {
t.Errorf("EncModeWithTags() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg)
}
// Create EncMode with TagSet and TagsForbidden option returns error
wantErrorMsg = "cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden"
tags := NewTagSet()
_, err = EncOptions{TagsMd: TagsForbidden}.EncModeWithTags(tags)
if err == nil {
t.Errorf("EncModeWithTags() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg)
}
_, err = EncOptions{TagsMd: TagsForbidden}.EncModeWithSharedTags(tags)
if err == nil {
t.Errorf("EncModeWithSharedTags() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("EncModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg)
}
// Encoding Tag and TagsForbidden option returns error
wantErrorMsg = "cbor: cannot encode cbor.Tag when TagsMd is TagsForbidden"
em, _ := EncOptions{TagsMd: TagsForbidden}.EncMode()
if _, err := em.Marshal(&tag); err == nil {
t.Errorf("Marshal() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("Marshal() returned error %q, want %q", err.Error(), wantErrorMsg)
}
// Encoding RawTag and TagsForbidden option returns error
wantErrorMsg = "cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden"
rawTag := RawTag{123, []byte{01}}
if _, err := em.Marshal(&rawTag); err == nil {
t.Errorf("Marshal() didn't return an error")
} else if err.Error() != wantErrorMsg {
t.Errorf("Marshal() returned error %q, want %q", err.Error(), wantErrorMsg)
}
}
func TestMarshalPosBigInt(t *testing.T) {
testCases := []struct {
name string
cborDataShortest []byte
cborDataBigInt []byte
value big.Int
}{
{
name: "fit uint8",
cborDataShortest: hexDecode("00"),
cborDataBigInt: hexDecode("c240"),
value: bigIntOrPanic("0"),
},
{
name: "fit uint16",
cborDataShortest: hexDecode("1903e8"),
cborDataBigInt: hexDecode("c24203e8"),
value: bigIntOrPanic("1000"),
},
{
name: "fit uint32",
cborDataShortest: hexDecode("1a000f4240"),
cborDataBigInt: hexDecode("c2430f4240"),
value: bigIntOrPanic("1000000"),
},
{
name: "fit uint64",
cborDataShortest: hexDecode("1b000000e8d4a51000"),
cborDataBigInt: hexDecode("c245e8d4a51000"),
value: bigIntOrPanic("1000000000000"),
},
{
name: "max uint64",
cborDataShortest: hexDecode("1bffffffffffffffff"),
cborDataBigInt: hexDecode("c248ffffffffffffffff"),
value: bigIntOrPanic("18446744073709551615"),
},
{
name: "overflow uint64",
cborDataShortest: hexDecode("c249010000000000000000"),
cborDataBigInt: hexDecode("c249010000000000000000"),
value: bigIntOrPanic("18446744073709551616"),
},
}
dmShortest, err := EncOptions{}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
dmBigInt, err := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if b, err := dmShortest.Marshal(tc.value); err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.value, err)
} else if !bytes.Equal(b, tc.cborDataShortest) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataShortest)
}
if b, err := dmBigInt.Marshal(tc.value); err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.value, err)
} else if !bytes.Equal(b, tc.cborDataBigInt) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataBigInt)
}
})
}
}
func TestMarshalNegBigInt(t *testing.T) {
testCases := []struct {
name string
cborDataShortest []byte
cborDataBigInt []byte
value big.Int
}{
{
name: "fit int8",
cborDataShortest: hexDecode("20"),
cborDataBigInt: hexDecode("c340"),
value: bigIntOrPanic("-1"),
},
{
name: "fit int16",
cborDataShortest: hexDecode("3903e7"),
cborDataBigInt: hexDecode("c34203e7"),
value: bigIntOrPanic("-1000"),
},
{
name: "fit int32",
cborDataShortest: hexDecode("3a000f423f"),
cborDataBigInt: hexDecode("c3430f423f"),
value: bigIntOrPanic("-1000000"),
},
{
name: "fit int64",
cborDataShortest: hexDecode("3b000000e8d4a50fff"),
cborDataBigInt: hexDecode("c345e8d4a50fff"),
value: bigIntOrPanic("-1000000000000"),
},
{
name: "min int64",
cborDataShortest: hexDecode("3b7fffffffffffffff"),
cborDataBigInt: hexDecode("c3487fffffffffffffff"),
value: bigIntOrPanic("-9223372036854775808"),
},
{
name: "overflow Go int64 fit CBOR neg int",
cborDataShortest: hexDecode("3b8000000000000000"),
cborDataBigInt: hexDecode("c3488000000000000000"),
value: bigIntOrPanic("-9223372036854775809"),
},
{
name: "min CBOR neg int",
cborDataShortest: hexDecode("3bffffffffffffffff"),
cborDataBigInt: hexDecode("c348ffffffffffffffff"),
value: bigIntOrPanic("-18446744073709551616"),
},
{
name: "overflow CBOR neg int",
cborDataShortest: hexDecode("c349010000000000000000"),
cborDataBigInt: hexDecode("c349010000000000000000"),
value: bigIntOrPanic("-18446744073709551617"),
},
}
dmShortest, err := EncOptions{}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
dmBigInt, err := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode()
if err != nil {
t.Errorf("EncMode() returned an error %v", err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if b, err := dmShortest.Marshal(tc.value); err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.value, err)
} else if !bytes.Equal(b, tc.cborDataShortest) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataShortest)
}
if b, err := dmBigInt.Marshal(tc.value); err != nil {
t.Errorf("Marshal(%v) returned error %v", tc.value, err)
} else if !bytes.Equal(b, tc.cborDataBigInt) {
t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.value, b, tc.cborDataBigInt)
}
})
}
}