cbor/diagnose_test.go
Ben Luddy 5029ed5882
Add benchmarks for Diagnose.
Signed-off-by: Ben Luddy <bluddy@redhat.com>
2024-05-08 13:08:16 -04:00

1151 lines
24 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}
})
}
}