1151 lines
24 KiB
Go
1151 lines
24 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"
|
||
"fmt"
|
||
"io"
|
||
"reflect"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestDiagnosticNotationExamples(t *testing.T) {
|
||
// https://www.rfc-editor.org/rfc/rfc8949.html#name-examples-of-encoded-cbor-da
|
||
testCases := []struct {
|
||
cbor []byte
|
||
diag string
|
||
}{
|
||
{
|
||
hexDecode("00"),
|
||
`0`,
|
||
},
|
||
{
|
||
hexDecode("01"),
|
||
`1`,
|
||
},
|
||
{
|
||
hexDecode("0a"),
|
||
`10`,
|
||
},
|
||
{
|
||
hexDecode("17"),
|
||
`23`,
|
||
},
|
||
{
|
||
hexDecode("1818"),
|
||
`24`,
|
||
},
|
||
{
|
||
hexDecode("1819"),
|
||
`25`,
|
||
},
|
||
{
|
||
hexDecode("1864"),
|
||
`100`,
|
||
},
|
||
{
|
||
hexDecode("1903e8"),
|
||
`1000`,
|
||
},
|
||
{
|
||
hexDecode("1a000f4240"),
|
||
`1000000`,
|
||
},
|
||
{
|
||
hexDecode("1b000000e8d4a51000"),
|
||
`1000000000000`,
|
||
},
|
||
{
|
||
hexDecode("1bffffffffffffffff"),
|
||
`18446744073709551615`,
|
||
},
|
||
{
|
||
hexDecode("c249010000000000000000"),
|
||
`18446744073709551616`,
|
||
},
|
||
{
|
||
hexDecode("3bffffffffffffffff"),
|
||
`-18446744073709551616`,
|
||
},
|
||
{
|
||
hexDecode("c349010000000000000000"),
|
||
`-18446744073709551617`,
|
||
},
|
||
{
|
||
hexDecode("20"),
|
||
`-1`,
|
||
},
|
||
{
|
||
hexDecode("29"),
|
||
`-10`,
|
||
},
|
||
{
|
||
hexDecode("3863"),
|
||
`-100`,
|
||
},
|
||
{
|
||
hexDecode("3903e7"),
|
||
`-1000`,
|
||
},
|
||
{
|
||
hexDecode("f90000"),
|
||
`0.0`,
|
||
},
|
||
{
|
||
hexDecode("f98000"),
|
||
`-0.0`,
|
||
},
|
||
{
|
||
hexDecode("f93c00"),
|
||
`1.0`,
|
||
},
|
||
{
|
||
hexDecode("fb3ff199999999999a"),
|
||
`1.1`,
|
||
},
|
||
{
|
||
hexDecode("f93e00"),
|
||
`1.5`,
|
||
},
|
||
{
|
||
hexDecode("f97bff"),
|
||
`65504.0`,
|
||
},
|
||
{
|
||
hexDecode("fa47c35000"),
|
||
`100000.0`,
|
||
},
|
||
{
|
||
hexDecode("fa7f7fffff"),
|
||
`3.4028234663852886e+38`,
|
||
},
|
||
{
|
||
hexDecode("fb7e37e43c8800759c"),
|
||
`1.0e+300`,
|
||
},
|
||
{
|
||
hexDecode("f90001"),
|
||
`5.960464477539063e-8`,
|
||
},
|
||
{
|
||
hexDecode("f90400"),
|
||
`0.00006103515625`,
|
||
},
|
||
{
|
||
hexDecode("f9c400"),
|
||
`-4.0`,
|
||
},
|
||
{
|
||
hexDecode("fbc010666666666666"),
|
||
`-4.1`,
|
||
},
|
||
{
|
||
hexDecode("f97c00"),
|
||
`Infinity`,
|
||
},
|
||
{
|
||
hexDecode("f97e00"),
|
||
`NaN`,
|
||
},
|
||
{
|
||
hexDecode("f9fc00"),
|
||
`-Infinity`,
|
||
},
|
||
{
|
||
hexDecode("fa7f800000"),
|
||
`Infinity`,
|
||
},
|
||
{
|
||
hexDecode("fa7fc00000"),
|
||
`NaN`,
|
||
},
|
||
{
|
||
hexDecode("faff800000"),
|
||
`-Infinity`,
|
||
},
|
||
{
|
||
hexDecode("fb7ff0000000000000"),
|
||
`Infinity`,
|
||
},
|
||
{
|
||
hexDecode("fb7ff8000000000000"),
|
||
`NaN`,
|
||
},
|
||
{
|
||
hexDecode("fbfff0000000000000"),
|
||
`-Infinity`,
|
||
},
|
||
{
|
||
hexDecode("f4"),
|
||
`false`,
|
||
},
|
||
{
|
||
hexDecode("f5"),
|
||
`true`,
|
||
},
|
||
{
|
||
hexDecode("f6"),
|
||
`null`,
|
||
},
|
||
{
|
||
hexDecode("f7"),
|
||
`undefined`,
|
||
},
|
||
{
|
||
hexDecode("f0"),
|
||
`simple(16)`,
|
||
},
|
||
{
|
||
hexDecode("f8ff"),
|
||
`simple(255)`,
|
||
},
|
||
{
|
||
hexDecode("c074323031332d30332d32315432303a30343a30305a"),
|
||
`0("2013-03-21T20:04:00Z")`,
|
||
},
|
||
{
|
||
hexDecode("c11a514b67b0"),
|
||
`1(1363896240)`,
|
||
},
|
||
{
|
||
hexDecode("c1fb41d452d9ec200000"),
|
||
`1(1363896240.5)`,
|
||
},
|
||
{
|
||
hexDecode("d74401020304"),
|
||
`23(h'01020304')`,
|
||
},
|
||
{
|
||
hexDecode("d818456449455446"),
|
||
`24(h'6449455446')`,
|
||
},
|
||
{
|
||
hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"),
|
||
`32("http://www.example.com")`,
|
||
},
|
||
{
|
||
hexDecode("40"),
|
||
`h''`,
|
||
},
|
||
{
|
||
hexDecode("4401020304"),
|
||
`h'01020304'`,
|
||
},
|
||
{
|
||
hexDecode("60"),
|
||
`""`,
|
||
},
|
||
{
|
||
hexDecode("6161"),
|
||
`"a"`,
|
||
},
|
||
{
|
||
hexDecode("6449455446"),
|
||
`"IETF"`,
|
||
},
|
||
{
|
||
hexDecode("62225c"),
|
||
`"\"\\"`,
|
||
},
|
||
{
|
||
hexDecode("62c3bc"),
|
||
`"\u00fc"`,
|
||
},
|
||
{
|
||
hexDecode("63e6b0b4"),
|
||
`"\u6c34"`,
|
||
},
|
||
{
|
||
hexDecode("64f0908591"),
|
||
`"\ud800\udd51"`,
|
||
},
|
||
{
|
||
hexDecode("80"),
|
||
`[]`,
|
||
},
|
||
{
|
||
hexDecode("83010203"),
|
||
`[1, 2, 3]`,
|
||
},
|
||
{
|
||
hexDecode("8301820203820405"),
|
||
`[1, [2, 3], [4, 5]]`,
|
||
},
|
||
{
|
||
hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
|
||
`[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("a0"),
|
||
`{}`,
|
||
},
|
||
{
|
||
hexDecode("a201020304"),
|
||
`{1: 2, 3: 4}`,
|
||
},
|
||
{
|
||
hexDecode("a26161016162820203"),
|
||
`{"a": 1, "b": [2, 3]}`,
|
||
},
|
||
{
|
||
hexDecode("826161a161626163"),
|
||
`["a", {"b": "c"}]`,
|
||
},
|
||
{
|
||
hexDecode("a56161614161626142616361436164614461656145"),
|
||
`{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}`,
|
||
},
|
||
{
|
||
hexDecode("5f42010243030405ff"),
|
||
`(_ h'0102', h'030405')`,
|
||
},
|
||
{
|
||
hexDecode("7f657374726561646d696e67ff"),
|
||
`(_ "strea", "ming")`,
|
||
},
|
||
{
|
||
hexDecode("9fff"),
|
||
`[_ ]`,
|
||
},
|
||
{
|
||
hexDecode("9f018202039f0405ffff"),
|
||
`[_ 1, [2, 3], [_ 4, 5]]`,
|
||
},
|
||
{
|
||
hexDecode("9f01820203820405ff"),
|
||
`[_ 1, [2, 3], [4, 5]]`,
|
||
},
|
||
{
|
||
hexDecode("83018202039f0405ff"),
|
||
`[1, [2, 3], [_ 4, 5]]`,
|
||
},
|
||
{
|
||
hexDecode("83019f0203ff820405"),
|
||
`[1, [_ 2, 3], [4, 5]]`,
|
||
},
|
||
{
|
||
hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"),
|
||
`[_ 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("bf61610161629f0203ffff"),
|
||
`{_ "a": 1, "b": [_ 2, 3]}`,
|
||
},
|
||
{
|
||
hexDecode("826161bf61626163ff"),
|
||
`["a", {_ "b": "c"}]`,
|
||
},
|
||
{
|
||
hexDecode("bf6346756ef563416d7421ff"),
|
||
`{_ "Fun": true, "Amt": -2}`,
|
||
},
|
||
}
|
||
|
||
for i, tc := range testCases {
|
||
t.Run(fmt.Sprintf("Diagnostic %d", i), func(t *testing.T) {
|
||
str, err := Diagnose(tc.cbor)
|
||
if err != nil {
|
||
t.Errorf("Diagnostic(0x%x) returned error %q", tc.cbor, err)
|
||
} else if str != tc.diag {
|
||
t.Errorf("Diagnostic(0x%x) returned `%s`, want `%s`", tc.cbor, str, tc.diag)
|
||
}
|
||
|
||
str, rest, err := DiagnoseFirst(tc.cbor)
|
||
if err != nil {
|
||
t.Errorf("Diagnostic(0x%x) returned error %q", tc.cbor, err)
|
||
} else if str != tc.diag {
|
||
t.Errorf("Diagnostic(0x%x) returned `%s`, want `%s`", tc.cbor, str, tc.diag)
|
||
}
|
||
|
||
if rest == nil {
|
||
t.Errorf("Diagnostic(0x%x) returned nil rest", tc.cbor)
|
||
} else if len(rest) != 0 {
|
||
t.Errorf("Diagnostic(0x%x) returned non-empty rest '%x'", tc.cbor, rest)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseByteString(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
diag string
|
||
opts *DiagOptions
|
||
}{
|
||
{
|
||
"base16",
|
||
hexDecode("4412345678"),
|
||
`h'12345678'`,
|
||
&DiagOptions{
|
||
ByteStringEncoding: ByteStringBase16Encoding,
|
||
},
|
||
},
|
||
{
|
||
"base32",
|
||
hexDecode("4412345678"),
|
||
`b32'CI2FM6A'`,
|
||
&DiagOptions{
|
||
ByteStringEncoding: ByteStringBase32Encoding,
|
||
},
|
||
},
|
||
{
|
||
"base32hex",
|
||
hexDecode("4412345678"),
|
||
`h32'28Q5CU0'`,
|
||
&DiagOptions{
|
||
ByteStringEncoding: ByteStringBase32HexEncoding,
|
||
},
|
||
},
|
||
{
|
||
"base64",
|
||
hexDecode("4412345678"),
|
||
`b64'EjRWeA'`,
|
||
&DiagOptions{
|
||
ByteStringEncoding: ByteStringBase64Encoding,
|
||
},
|
||
},
|
||
{
|
||
"without ByteStringHexWhitespace option",
|
||
hexDecode("4b48656c6c6f20776f726c64"),
|
||
`h'48656c6c6f20776f726c64'`,
|
||
&DiagOptions{
|
||
ByteStringHexWhitespace: false,
|
||
},
|
||
},
|
||
{
|
||
"with ByteStringHexWhitespace option",
|
||
hexDecode("4b48656c6c6f20776f726c64"),
|
||
`h'48 65 6c 6c 6f 20 77 6f 72 6c 64'`,
|
||
&DiagOptions{
|
||
ByteStringHexWhitespace: true,
|
||
},
|
||
},
|
||
{
|
||
"without ByteStringText option",
|
||
hexDecode("4b68656c6c6f20776f726c64"),
|
||
`h'68656c6c6f20776f726c64'`,
|
||
&DiagOptions{
|
||
ByteStringText: false,
|
||
},
|
||
},
|
||
{
|
||
"with ByteStringText option",
|
||
hexDecode("4b68656c6c6f20776f726c64"),
|
||
`'hello world'`,
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"without ByteStringText option and with ByteStringHexWhitespace option",
|
||
hexDecode("4b68656c6c6f20776f726c64"),
|
||
`h'68 65 6c 6c 6f 20 77 6f 72 6c 64'`,
|
||
&DiagOptions{
|
||
ByteStringText: false,
|
||
ByteStringHexWhitespace: true,
|
||
},
|
||
},
|
||
{
|
||
"without ByteStringEmbeddedCBOR",
|
||
hexDecode("4101"),
|
||
`h'01'`,
|
||
&DiagOptions{
|
||
ByteStringEmbeddedCBOR: false,
|
||
},
|
||
},
|
||
{
|
||
"with ByteStringEmbeddedCBOR",
|
||
hexDecode("4101"),
|
||
`<<1>>`,
|
||
&DiagOptions{
|
||
ByteStringEmbeddedCBOR: true,
|
||
},
|
||
},
|
||
{
|
||
"multi CBOR items without ByteStringEmbeddedCBOR",
|
||
hexDecode("420102"),
|
||
`h'0102'`,
|
||
&DiagOptions{
|
||
ByteStringEmbeddedCBOR: false,
|
||
},
|
||
},
|
||
{
|
||
"multi CBOR items with ByteStringEmbeddedCBOR",
|
||
hexDecode("420102"),
|
||
`<<1, 2>>`,
|
||
&DiagOptions{
|
||
ByteStringEmbeddedCBOR: true,
|
||
},
|
||
},
|
||
{
|
||
"multi CBOR items with ByteStringEmbeddedCBOR",
|
||
hexDecode("4563666F6FF6"),
|
||
`h'63666f6ff6'`,
|
||
&DiagOptions{
|
||
ByteStringEmbeddedCBOR: false,
|
||
},
|
||
},
|
||
{
|
||
"multi CBOR items with ByteStringEmbeddedCBOR",
|
||
hexDecode("4563666F6FF6"),
|
||
`<<"foo", null>>`,
|
||
&DiagOptions{
|
||
ByteStringEmbeddedCBOR: true,
|
||
},
|
||
},
|
||
{
|
||
"indefinite length byte string with no chunks",
|
||
hexDecode("5fff"),
|
||
`''_`,
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"indefinite length byte string with a empty byte string",
|
||
hexDecode("5f40ff"),
|
||
`(_ h'')`, // RFC 8949, Section 8.1 says `(_ '')` but it looks wrong and conflicts with Appendix A.
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"indefinite length byte string with two empty byte string",
|
||
hexDecode("5f4040ff"),
|
||
`(_ h'', h'')`,
|
||
&DiagOptions{},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
str, err := dm.Diagnose(tc.cbor)
|
||
if err != nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
} else if str != tc.diag {
|
||
t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseTextString(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
diag string
|
||
opts *DiagOptions
|
||
}{
|
||
{
|
||
"\t",
|
||
hexDecode("6109"),
|
||
`"\t"`,
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"\r",
|
||
hexDecode("610d"),
|
||
`"\r"`,
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"other ascii",
|
||
hexDecode("611b"),
|
||
`"\u001b"`,
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"valid UTF-8 text in byte string",
|
||
hexDecode("4d68656c6c6f2c20e4bda0e5a5bd"),
|
||
`'hello, \u4f60\u597d'`,
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"valid UTF-8 text in text string",
|
||
hexDecode("6d68656c6c6f2c20e4bda0e5a5bd"),
|
||
`"hello, \u4f60\u597d"`, // "hello, 你好"
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"invalid UTF-8 text in byte string",
|
||
hexDecode("4d68656c6c6fffeee4bda0e5a5bd"),
|
||
`h'68656c6c6fffeee4bda0e5a5bd'`,
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"valid grapheme cluster text in byte string",
|
||
hexDecode("583448656c6c6f2c2027e29da4efb88fe2808df09f94a5270ae4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"),
|
||
`'Hello, \'\u2764\ufe0f\u200d\ud83d\udd25\'\n\u4f60\u597d\uff0c"\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1"'`,
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"valid grapheme cluster text in text string",
|
||
hexDecode("783448656c6c6f2c2027e29da4efb88fe2808df09f94a5270ae4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"),
|
||
`"Hello, '\u2764\ufe0f\u200d\ud83d\udd25'\n\u4f60\u597d\uff0c\"\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1\""`, // "Hello, '❤️🔥'\n你好,\"🧑🤝🧑\""
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"invalid grapheme cluster text in byte string",
|
||
hexDecode("583448656c6c6feeff27e29da4efb88fe2808df09f94a5270de4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"),
|
||
`h'48656c6c6feeff27e29da4efb88fe2808df09f94a5270de4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122'`,
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"indefinite length text string with no chunks",
|
||
hexDecode("7fff"),
|
||
`""_`,
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"indefinite length text string with a empty text string",
|
||
hexDecode("7f60ff"),
|
||
`(_ "")`,
|
||
&DiagOptions{},
|
||
},
|
||
{
|
||
"indefinite length text string with two empty text string",
|
||
hexDecode("7f6060ff"),
|
||
`(_ "", "")`,
|
||
&DiagOptions{},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
str, err := dm.Diagnose(tc.cbor)
|
||
if err != nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
} else if str != tc.diag {
|
||
t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseInvalidTextString(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
wantErrorMsg string
|
||
opts *DiagOptions
|
||
}{
|
||
{
|
||
"invalid UTF-8 text in text string",
|
||
hexDecode("6d68656c6c6fffeee4bda0e5a5bd"),
|
||
"invalid UTF-8 string",
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"invalid grapheme cluster text in text string",
|
||
hexDecode("783448656c6c6feeff27e29da4efb88fe2808df09f94a5270de4bda0e5a5bdefbc8c22f09fa791e2808df09fa49de2808df09fa79122"),
|
||
"invalid UTF-8 string",
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
{
|
||
"invalid indefinite length text string",
|
||
hexDecode("7f6040ff"),
|
||
`wrong element type`,
|
||
&DiagOptions{
|
||
ByteStringText: true,
|
||
},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
_, err = dm.Diagnose(tc.cbor)
|
||
if err == nil {
|
||
t.Errorf("Diagnose(0x%x) didn't return error", tc.cbor)
|
||
} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseFloatingPointNumber(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
diag string
|
||
opts *DiagOptions
|
||
}{
|
||
{
|
||
"float16 without FloatPrecisionIndicator option",
|
||
hexDecode("f93e00"),
|
||
`1.5`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: false,
|
||
},
|
||
},
|
||
{
|
||
"float16 with FloatPrecisionIndicator option",
|
||
hexDecode("f93e00"),
|
||
`1.5_1`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: true,
|
||
},
|
||
},
|
||
{
|
||
"float32 without FloatPrecisionIndicator option",
|
||
hexDecode("fa47c35000"),
|
||
`100000.0`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: false,
|
||
},
|
||
},
|
||
{
|
||
"float32 with FloatPrecisionIndicator option",
|
||
hexDecode("fa47c35000"),
|
||
`100000.0_2`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: true,
|
||
},
|
||
},
|
||
{
|
||
"float64 without FloatPrecisionIndicator option",
|
||
hexDecode("fbc010666666666666"),
|
||
`-4.1`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: false,
|
||
},
|
||
},
|
||
{
|
||
"float64 with FloatPrecisionIndicator option",
|
||
hexDecode("fbc010666666666666"),
|
||
`-4.1_3`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: true,
|
||
},
|
||
},
|
||
{
|
||
"with FloatPrecisionIndicator option",
|
||
hexDecode("c1fb41d452d9ec200000"),
|
||
`1(1363896240.5_3)`,
|
||
&DiagOptions{
|
||
FloatPrecisionIndicator: true,
|
||
},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
str, err := dm.Diagnose(tc.cbor)
|
||
if err != nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
} else if str != tc.diag {
|
||
t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseFirst(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
diag string
|
||
wantRest []byte
|
||
wantErrorMsg string
|
||
}{
|
||
{
|
||
"with no trailing data",
|
||
hexDecode("f93e00"),
|
||
`1.5`,
|
||
[]byte{},
|
||
"",
|
||
},
|
||
{
|
||
"with CBOR Sequences",
|
||
hexDecode("f93e0064494554464401020304"),
|
||
`1.5`,
|
||
hexDecode("64494554464401020304"),
|
||
"",
|
||
},
|
||
{
|
||
"with invalid CBOR trailing data",
|
||
hexDecode("f93e00ff494554464401020304"),
|
||
`1.5`,
|
||
hexDecode("ff494554464401020304"),
|
||
"",
|
||
},
|
||
{
|
||
"with invalid CBOR data",
|
||
hexDecode("f93e"),
|
||
``,
|
||
nil,
|
||
"unexpected EOF",
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
str, rest, err := DiagnoseFirst(tc.cbor)
|
||
if str != tc.diag {
|
||
t.Errorf("DiagnoseFirst(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag)
|
||
}
|
||
|
||
if bytes.Equal(rest, tc.wantRest) == false {
|
||
if str != tc.diag {
|
||
t.Errorf("DiagnoseFirst(0x%x) returned rest `%x`, want rest %x", tc.cbor, rest, tc.wantRest)
|
||
}
|
||
}
|
||
|
||
switch {
|
||
case tc.wantErrorMsg == "" && err != nil:
|
||
t.Errorf("DiagnoseFirst(0x%x) returned error %q", tc.cbor, err)
|
||
case tc.wantErrorMsg != "" && err == nil:
|
||
t.Errorf("DiagnoseFirst(0x%x) returned nil error, want error %q", tc.cbor, err)
|
||
case tc.wantErrorMsg != "" && !strings.Contains(err.Error(), tc.wantErrorMsg):
|
||
t.Errorf("DiagnoseFirst(0x%x) returned error %q, want error %q", tc.cbor, err, tc.wantErrorMsg)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseCBORSequences(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
diag string
|
||
opts *DiagOptions
|
||
returnError bool
|
||
}{
|
||
{
|
||
"CBOR Sequences without CBORSequence option",
|
||
hexDecode("f93e0064494554464401020304"),
|
||
``,
|
||
&DiagOptions{
|
||
CBORSequence: false,
|
||
},
|
||
true,
|
||
},
|
||
{
|
||
"CBOR Sequences with CBORSequence option",
|
||
hexDecode("f93e0064494554464401020304"),
|
||
`1.5, "IETF", h'01020304'`,
|
||
&DiagOptions{
|
||
CBORSequence: true,
|
||
},
|
||
false,
|
||
},
|
||
{
|
||
"CBOR Sequences with CBORSequence option",
|
||
hexDecode("0102"),
|
||
`1, 2`,
|
||
&DiagOptions{
|
||
CBORSequence: true,
|
||
},
|
||
false,
|
||
},
|
||
{
|
||
"CBOR Sequences with CBORSequence option",
|
||
hexDecode("63666F6FF6"),
|
||
`"foo", null`,
|
||
&DiagOptions{
|
||
CBORSequence: true,
|
||
},
|
||
false,
|
||
},
|
||
{
|
||
"partial/incomplete CBOR Sequences",
|
||
hexDecode("f93e00644945544644010203"),
|
||
`1.5, "IETF"`,
|
||
&DiagOptions{
|
||
CBORSequence: true,
|
||
},
|
||
true,
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
str, err := dm.Diagnose(tc.cbor)
|
||
if tc.returnError && err == nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
} else if !tc.returnError && err != nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
if str != tc.diag {
|
||
t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseTag(t *testing.T) {
|
||
testCases := []struct {
|
||
title string
|
||
cbor []byte
|
||
diag string
|
||
opts *DiagOptions
|
||
returnError bool
|
||
}{
|
||
{
|
||
"CBOR tag number 2 with not well-formed encoded CBOR data item",
|
||
hexDecode("c201"),
|
||
``,
|
||
&DiagOptions{},
|
||
true,
|
||
},
|
||
{
|
||
"CBOR tag number 3 with not well-formed encoded CBOR data item",
|
||
hexDecode("c301"),
|
||
``,
|
||
&DiagOptions{},
|
||
true,
|
||
},
|
||
{
|
||
"CBOR tag number 2 with well-formed encoded CBOR data item",
|
||
hexDecode("c240"),
|
||
`0`,
|
||
&DiagOptions{},
|
||
false,
|
||
},
|
||
{
|
||
"CBOR tag number 3 with well-formed encoded CBOR data item",
|
||
hexDecode("c340"),
|
||
`-1`, // -1 - n
|
||
&DiagOptions{},
|
||
false,
|
||
},
|
||
{
|
||
"CBOR tag number 2 with well-formed encoded CBOR data item",
|
||
hexDecode("c249010000000000000000"),
|
||
`18446744073709551616`,
|
||
&DiagOptions{},
|
||
false,
|
||
},
|
||
{
|
||
"CBOR tag number 3 with well-formed encoded CBOR data item",
|
||
hexDecode("c349010000000000000000"),
|
||
`-18446744073709551617`, // -1 - n
|
||
&DiagOptions{},
|
||
false,
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.title, func(t *testing.T) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() for 0x%x returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
str, err := dm.Diagnose(tc.cbor)
|
||
if tc.returnError && err == nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
} else if !tc.returnError && err != nil {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", tc.cbor, err)
|
||
}
|
||
|
||
if str != tc.diag {
|
||
t.Errorf("Diagnose(0x%x) returned `%s`, want %s", tc.cbor, str, tc.diag)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseOptions(t *testing.T) {
|
||
opts := DiagOptions{
|
||
ByteStringEncoding: ByteStringBase32Encoding,
|
||
ByteStringHexWhitespace: true,
|
||
ByteStringText: false,
|
||
ByteStringEmbeddedCBOR: true,
|
||
CBORSequence: false,
|
||
FloatPrecisionIndicator: true,
|
||
MaxNestedLevels: 100,
|
||
MaxArrayElements: 101,
|
||
MaxMapPairs: 102,
|
||
}
|
||
dm, err := opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() returned an error %v", err)
|
||
}
|
||
opts2 := dm.DiagOptions()
|
||
if !reflect.DeepEqual(opts, opts2) {
|
||
t.Errorf("DiagOptions() returned wrong options %v, want %v", opts2, opts)
|
||
}
|
||
|
||
opts = DiagOptions{
|
||
ByteStringEncoding: ByteStringBase64Encoding,
|
||
ByteStringHexWhitespace: false,
|
||
ByteStringText: true,
|
||
ByteStringEmbeddedCBOR: false,
|
||
CBORSequence: true,
|
||
FloatPrecisionIndicator: false,
|
||
MaxNestedLevels: 100,
|
||
MaxArrayElements: 101,
|
||
MaxMapPairs: 102,
|
||
}
|
||
dm, err = opts.DiagMode()
|
||
if err != nil {
|
||
t.Errorf("DiagMode() returned an error %v", err)
|
||
}
|
||
opts2 = dm.DiagOptions()
|
||
if !reflect.DeepEqual(opts, opts2) {
|
||
t.Errorf("DiagOptions() returned wrong options %v, want %v", opts2, opts)
|
||
}
|
||
}
|
||
|
||
func TestInvalidDiagnoseOptions(t *testing.T) {
|
||
opts := &DiagOptions{
|
||
ByteStringEncoding: ByteStringBase64Encoding + 1,
|
||
}
|
||
_, err := opts.DiagMode()
|
||
if err == nil {
|
||
t.Errorf("DiagMode() with invalid ByteStringEncoding option didn't return error")
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseExtraneousData(t *testing.T) {
|
||
data := hexDecode("63666F6FF6")
|
||
_, err := Diagnose(data)
|
||
if err == nil {
|
||
t.Errorf("Diagnose(0x%x) didn't return error", data)
|
||
} else if !strings.Contains(err.Error(), `extraneous data`) {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", data, err)
|
||
}
|
||
|
||
_, _, err = DiagnoseFirst(data)
|
||
if err != nil {
|
||
t.Errorf("DiagnoseFirst(0x%x) returned error %v", data, err)
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseNotwellformedData(t *testing.T) {
|
||
data := hexDecode("5f4060ff")
|
||
_, err := Diagnose(data)
|
||
if err == nil {
|
||
t.Errorf("Diagnose(0x%x) didn't return error", data)
|
||
} else if !strings.Contains(err.Error(), `wrong element type`) {
|
||
t.Errorf("Diagnose(0x%x) returned error %q", data, err)
|
||
}
|
||
}
|
||
|
||
func TestDiagnoseEmptyData(t *testing.T) {
|
||
var emptyData []byte
|
||
|
||
defaultMode, _ := DiagOptions{}.DiagMode()
|
||
sequenceMode, _ := DiagOptions{CBORSequence: true}.DiagMode()
|
||
|
||
testCases := []struct {
|
||
name string
|
||
dm DiagMode
|
||
}{
|
||
{"default", defaultMode},
|
||
{"sequence", sequenceMode},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
s, err := tc.dm.Diagnose(emptyData)
|
||
if s != "" {
|
||
t.Errorf("Diagnose() didn't return empty notation for empty data")
|
||
}
|
||
if err != io.EOF {
|
||
t.Errorf("Diagnose() didn't return io.EOF for empty data")
|
||
}
|
||
|
||
s, rest, err := tc.dm.DiagnoseFirst(emptyData)
|
||
if s != "" {
|
||
t.Errorf("DiagnoseFirst() didn't return empty notation for empty data")
|
||
}
|
||
if len(rest) != 0 {
|
||
t.Errorf("DiagnoseFirst() didn't return empty rest for empty data")
|
||
}
|
||
if err != io.EOF {
|
||
t.Errorf("DiagnoseFirst() didn't return io.EOF for empty data")
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func BenchmarkDiagnose(b *testing.B) {
|
||
for _, tc := range []struct {
|
||
name string
|
||
opts DiagOptions
|
||
input []byte
|
||
}{
|
||
{
|
||
name: "escaped character in text string",
|
||
opts: DiagOptions{},
|
||
input: hexDecode("62c3bc"), // "\u00fc"
|
||
},
|
||
{
|
||
name: "byte string base16 encoding",
|
||
opts: DiagOptions{ByteStringEncoding: ByteStringBase16Encoding},
|
||
input: []byte("\x45hello"),
|
||
},
|
||
{
|
||
name: "byte string base32 encoding",
|
||
opts: DiagOptions{ByteStringEncoding: ByteStringBase32Encoding},
|
||
input: []byte("\x45hello"),
|
||
},
|
||
{
|
||
name: "byte string base32hex encoding",
|
||
opts: DiagOptions{ByteStringEncoding: ByteStringBase32HexEncoding},
|
||
input: []byte("\x45hello"),
|
||
},
|
||
{
|
||
name: "byte string base64url encoding",
|
||
opts: DiagOptions{ByteStringEncoding: ByteStringBase64Encoding},
|
||
input: []byte("\x45hello"),
|
||
},
|
||
} {
|
||
b.Run(tc.name, func(b *testing.B) {
|
||
dm, err := tc.opts.DiagMode()
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for i := 0; i < b.N; i++ {
|
||
_, _ = dm.Diagnose(tc.input)
|
||
}
|
||
})
|
||
}
|
||
}
|