diff options
| author | Laytan Laats <laytanlaats@hotmail.com> | 2023-11-22 16:12:37 +0100 |
|---|---|---|
| committer | Laytan Laats <laytanlaats@hotmail.com> | 2024-03-04 17:25:34 +0100 |
| commit | 5533a327eb0f526cbebbe71124620fcbb0bc0649 (patch) | |
| tree | 6aadf7d275703ef574533ac1e1d348374a411cde /tests/core/encoding | |
| parent | 4c35633e0147b481dd7b2352d6bdb603f78c6dc7 (diff) | |
encoding/cbor: initial package implementation
Diffstat (limited to 'tests/core/encoding')
| -rw-r--r-- | tests/core/encoding/cbor/test_core_cbor.odin | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin new file mode 100644 index 000000000..22359d830 --- /dev/null +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -0,0 +1,719 @@ +package test_encoding_cbor + +import "core:bytes" +import "core:encoding/cbor" +import "core:fmt" +import "core:intrinsics" +import "core:math/big" +import "core:mem" +import "core:reflect" +import "core:testing" +import "core:time" + +Foo :: struct { + str: string, + cstr: cstring, + value: cbor.Value, + neg: cbor.Negative_U16, + pos: u16, + iamint: int, + base64: string `cbor_tag:"base64"`, + renamed: f32 `cbor:"renamed :)"`, + now: time.Time `cbor_tag:"1"`, + nowie: time.Time, + child: struct{ + dyn: [dynamic]string, + mappy: map[string]int, + my_integers: [10]int, + }, + my_bytes: []byte, + ennie: FooBar, + ennieb: FooBars, + quat: quaternion64, + comp: complex128, + important: rune, + no: cbor.Nil, + nos: cbor.Undefined, + yes: b32, + biggie: u64, + smallie: cbor.Negative_U64, + onetwenty: i128, + small_onetwenty: i128, + biggest: big.Int, + smallest: big.Int, +} + +FooBar :: enum { + EFoo, + EBar, +} + +FooBars :: bit_set[FooBar; u16] + +@(test) +test_marshalling :: proc(t: ^testing.T) { + tracker: mem.Tracking_Allocator + mem.tracking_allocator_init(&tracker, context.allocator) + context.allocator = mem.tracking_allocator(&tracker) + context.temp_allocator = context.allocator + defer mem.tracking_allocator_destroy(&tracker) + + ev :: testing.expect_value + + { + nice := "16 is a nice number" + now := time.Time{_nsec = 1701117968 * 1e9} + f: Foo = { + str = "Hellope", + cstr = "Hellnope", + value = &cbor.Map{{u8(16), &nice}, {u8(32), u8(69)}}, + neg = 68, + pos = 1212, + iamint = -256, + base64 = nice, + renamed = 123123.125, + + now = now, + nowie = now, + + child = { + dyn = [dynamic]string{"one", "two", "three", "four"}, + mappy = map[string]int{"one" = 1, "two" = 2, "three" = 3, "four" = 4}, + my_integers = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }, + + my_bytes = []byte{}, + + ennie = .EFoo, + ennieb = {.EBar}, + + quat = quaternion(16, 17, 18, 19), + comp = complex(32, 33), + + important = '!', + + no = cbor.Nil(uintptr(3)), + + yes = true, + + biggie = max(u64), + smallie = cbor.Negative_U64(max(u64)), + onetwenty = i128(12345), + small_onetwenty = -i128(max(u64)), + } + + big.atoi(&f.biggest, "1234567891011121314151617181920") + big.atoi(&f.smallest, "-1234567891011121314151617181920") + + defer { + delete(f.child.dyn) + delete(f.child.mappy) + big.destroy(&f.biggest) + big.destroy(&f.smallest) + } + + data, err := cbor.marshal(f, cbor.ENCODE_FULLY_DETERMINISTIC) + ev(t, err, nil) + defer delete(data) + + decoded, derr := cbor.decode_string(string(data)) + ev(t, derr, nil) + defer cbor.destroy(decoded) + + diagnosis, eerr := cbor.diagnose(decoded) + ev(t, eerr, nil) + defer delete(diagnosis) + + ev(t, diagnosis, `{ + "base64": 34("MTYgaXMgYSBuaWNlIG51bWJlcg=="), + "biggest": 2(h'f951a9fd3c158afdff08ab8e0'), + "biggie": 18446744073709551615, + "child": { + "dyn": [ + "one", + "two", + "three", + "four" + ], + "mappy": { + "one": 1, + "two": 2, + "four": 4, + "three": 3 + }, + "my_integers": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ] + }, + "comp": [ + 32.0000, + 33.0000 + ], + "cstr": "Hellnope", + "ennie": 0, + "ennieb": 2, + "iamint": -256, + "important": "!", + "my_bytes": h'', + "neg": -69, + "no": nil, + "nos": undefined, + "now": 1(1701117968), + "nowie": { + "_nsec": 1701117968000000000 + }, + "onetwenty": 12345, + "pos": 1212, + "quat": [ + 17.0000, + 18.0000, + 19.0000, + 16.0000 + ], + "renamed :)": 123123.12500000, + "small_onetwenty": -18446744073709551615, + "smallest": 3(h'f951a9fd3c158afdff08ab8e0'), + "smallie": -18446744073709551616, + "str": "Hellope", + "value": { + 16: "16 is a nice number", + 32: 69 + }, + "yes": true +}`) + + backf: Foo + uerr := cbor.unmarshal(string(data), &backf) + ev(t, uerr, nil) + defer { + delete(backf.str) + delete(backf.cstr) + cbor.destroy(backf.value) + delete(backf.base64) + + for e in backf.child.dyn { delete(e) } + delete(backf.child.dyn) + + for k in backf.child.mappy { delete(k) } + delete(backf.child.mappy) + + delete(backf.my_bytes) + + big.destroy(&backf.biggest) + big.destroy(&backf.smallest) + } + + ev(t, backf.str, f.str) + ev(t, backf.cstr, f.cstr) + + #partial switch v in backf.value { + case ^cbor.Map: + for entry, i in v { + fm := f.value.(^cbor.Map) + ev(t, entry.key, fm[i].key) + + if str, is_str := entry.value.(^cbor.Text); is_str { + ev(t, str^, fm[i].value.(^cbor.Text)^) + } else { + ev(t, entry.value, fm[i].value) + } + } + + case: testing.error(t, v) + } + + ev(t, backf.neg, f.neg) + ev(t, backf.iamint, f.iamint) + ev(t, backf.base64, f.base64) + ev(t, backf.renamed, f.renamed) + ev(t, backf.now, f.now) + ev(t, backf.nowie, f.nowie) + for e, i in f.child.dyn { ev(t, backf.child.dyn[i], e) } + for key, value in f.child.mappy { ev(t, backf.child.mappy[key], value) } + ev(t, backf.child.my_integers, f.child.my_integers) + ev(t, len(backf.my_bytes), 0) + ev(t, len(backf.my_bytes), len(f.my_bytes)) + ev(t, backf.ennie, f.ennie) + ev(t, backf.ennieb, f.ennieb) + ev(t, backf.quat, f.quat) + ev(t, backf.comp, f.comp) + ev(t, backf.important, f.important) + ev(t, backf.no, nil) + ev(t, backf.nos, nil) + ev(t, backf.yes, f.yes) + ev(t, backf.biggie, f.biggie) + ev(t, backf.smallie, f.smallie) + ev(t, backf.onetwenty, f.onetwenty) + ev(t, backf.small_onetwenty, f.small_onetwenty) + + s_equals, s_err := big.equals(&backf.smallest, &f.smallest) + ev(t, s_err, nil) + if !s_equals { + testing.errorf(t, "smallest: %v does not equal %v", big.itoa(&backf.smallest), big.itoa(&f.smallest)) + } + + b_equals, b_err := big.equals(&backf.biggest, &f.biggest) + ev(t, b_err, nil) + if !b_equals { + testing.errorf(t, "biggest: %v does not equal %v", big.itoa(&backf.biggest), big.itoa(&f.biggest)) + } + } + + for _, leak in tracker.allocation_map { + testing.errorf(t, "%v leaked %m\n", leak.location, leak.size) + } + + for bad_free in tracker.bad_free_array { + testing.errorf(t, "%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) + } +} + +@(test) +test_decode_unsigned :: proc(t: ^testing.T) { + expect_decoding(t, "\x00", "0", u8) + expect_decoding(t, "\x01", "1", u8) + expect_decoding(t, "\x0a", "10", u8) + expect_decoding(t, "\x17", "23", u8) + expect_decoding(t, "\x18\x18", "24", u8) + expect_decoding(t, "\x18\x19", "25", u8) + expect_decoding(t, "\x18\x64", "100", u8) + expect_decoding(t, "\x19\x03\xe8", "1000", u16) + expect_decoding(t, "\x1a\x00\x0f\x42\x40", "1000000", u32) // Million. + expect_decoding(t, "\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00", "1000000000000", u64) // Trillion. + expect_decoding(t, "\x1b\xff\xff\xff\xff\xff\xff\xff\xff", "18446744073709551615", u64) // max(u64). +} + +@(test) +test_encode_unsigned :: proc(t: ^testing.T) { + expect_encoding(t, u8(0), "\x00") + expect_encoding(t, u8(1), "\x01") + expect_encoding(t, u8(10), "\x0a") + expect_encoding(t, u8(23), "\x17") + expect_encoding(t, u8(24), "\x18\x18") + expect_encoding(t, u8(25), "\x18\x19") + expect_encoding(t, u8(100), "\x18\x64") + expect_encoding(t, u16(1000), "\x19\x03\xe8") + expect_encoding(t, u32(1000000), "\x1a\x00\x0f\x42\x40") // Million. + expect_encoding(t, u64(1000000000000), "\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00") // Trillion. + expect_encoding(t, u64(18446744073709551615), "\x1b\xff\xff\xff\xff\xff\xff\xff\xff") // max(u64). +} + +@(test) +test_decode_negative :: proc(t: ^testing.T) { + expect_decoding(t, "\x20", "-1", cbor.Negative_U8) + expect_decoding(t, "\x29", "-10", cbor.Negative_U8) + expect_decoding(t, "\x38\x63", "-100", cbor.Negative_U8) + expect_decoding(t, "\x39\x03\xe7", "-1000", cbor.Negative_U16) + + // Negative max(u64). + expect_decoding(t, "\x3b\xff\xff\xff\xff\xff\xff\xff\xff", "-18446744073709551616", cbor.Negative_U64) +} + +@(test) +test_encode_negative :: proc(t: ^testing.T) { + expect_encoding(t, cbor.Negative_U8(0), "\x20") + expect_encoding(t, cbor.Negative_U8(9), "\x29") + expect_encoding(t, cbor.Negative_U8(99), "\x38\x63") + expect_encoding(t, cbor.Negative_U16(999), "\x39\x03\xe7") + + // Negative max(u64). + expect_encoding(t, cbor.Negative_U64(18446744073709551615), "\x3b\xff\xff\xff\xff\xff\xff\xff\xff") +} + +@(test) +test_decode_simples :: proc(t: ^testing.T) { + expect_decoding(t, "\xf4", "false", bool) + expect_decoding(t, "\xf5", "true", bool) + expect_decoding(t, "\xf6", "nil", cbor.Nil) + expect_decoding(t, "\xf7", "undefined", cbor.Undefined) + + expect_decoding(t, "\xf0", "simple(16)", cbor.Simple) + expect_decoding(t, "\xf8\xff", "simple(255)", cbor.Atom) +} + +@(test) +test_encode_simples :: proc(t: ^testing.T) { + expect_encoding(t, bool(false), "\xf4") + expect_encoding(t, bool(true), "\xf5") + expect_encoding(t, cbor.Nil{}, "\xf6") // default value for a distinct rawptr, in this case Nil. + expect_encoding(t, cbor.Undefined{}, "\xf7") // default value for a distinct rawptr, in this case Undefined. + + expect_encoding(t, cbor.Simple(16), "\xf0") // simple(16) + expect_encoding(t, cbor.Simple(255), "\xf8\xff") // simple(255) +} + +@(test) +test_decode_floats :: proc(t: ^testing.T) { + expect_float(t, "\xf9\x00\x00", f16(0.0)) + expect_float(t, "\xf9\x80\x00", f16(-0.0)) + expect_float(t, "\xf9\x3c\x00", f16(1.0)) + expect_float(t, "\xfb\x3f\xf1\x99\x99\x99\x99\x99\x9a", f64(1.1)) + expect_float(t, "\xf9\x3e\x00", f16(1.5)) + expect_float(t, "\xf9\x7b\xff", f16(65504.0)) + expect_float(t, "\xfa\x47\xc3\x50\x00", f32(100000.0)) + expect_float(t, "\xfa\x7f\x7f\xff\xff", f32(3.4028234663852886e+38)) + expect_float(t, "\xfb\x7e\x37\xe4\x3c\x88\x00\x75\x9c", f64(1.0e+300)) + expect_float(t, "\xf9\x00\x01", f16(5.960464477539063e-8)) + expect_float(t, "\xf9\x04\x00", f16(0.00006103515625)) + expect_float(t, "\xf9\xc4\x00", f16(-4.0)) + expect_float(t, "\xfb\xc0\x10\x66\x66\x66\x66\x66\x66", f64(-4.1)) + expect_decoding(t, "\xf9\x7c\x00", "+Inf", f16) + expect_decoding(t, "\xf9\x7e\x00", "NaN", f16) + expect_decoding(t, "\xf9\xfc\x00", "-Inf", f16) + expect_decoding(t, "\xfa\x7f\x80\x00\x00", "+Inf", f32) + expect_decoding(t, "\xfa\x7f\xc0\x00\x00", "NaN", f32) + expect_decoding(t, "\xfa\xff\x80\x00\x00", "-Inf", f32) + expect_decoding(t, "\xfb\x7f\xf0\x00\x00\x00\x00\x00\x00", "+Inf", f64) + expect_decoding(t, "\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00", "NaN", f64) + expect_decoding(t, "\xfb\xff\xf0\x00\x00\x00\x00\x00\x00", "-Inf", f64) +} + +@(test) +test_encode_floats :: proc(t: ^testing.T) { + expect_encoding(t, f16(0.0), "\xf9\x00\x00") + expect_encoding(t, f16(-0.0), "\xf9\x80\x00") + expect_encoding(t, f16(1.0), "\xf9\x3c\x00") + expect_encoding(t, f64(1.1), "\xfb\x3f\xf1\x99\x99\x99\x99\x99\x9a") + expect_encoding(t, f16(1.5), "\xf9\x3e\x00") + expect_encoding(t, f16(65504.0), "\xf9\x7b\xff") + expect_encoding(t, f32(100000.0), "\xfa\x47\xc3\x50\x00") + expect_encoding(t, f32(3.4028234663852886e+38), "\xfa\x7f\x7f\xff\xff") + expect_encoding(t, f64(1.0e+300), "\xfb\x7e\x37\xe4\x3c\x88\x00\x75\x9c") + expect_encoding(t, f16(5.960464477539063e-8), "\xf9\x00\x01") + expect_encoding(t, f16(0.00006103515625), "\xf9\x04\x00") + expect_encoding(t, f16(-4.0), "\xf9\xc4\x00") + expect_encoding(t, f64(-4.1), "\xfb\xc0\x10\x66\x66\x66\x66\x66\x66") +} + +@(test) +test_decode_bytes :: proc(t: ^testing.T) { + expect_decoding(t, "\x40", "h''", ^cbor.Bytes) + expect_decoding(t, "\x44\x01\x02\x03\x04", "h'1234'", ^cbor.Bytes) + + // Indefinite lengths + + expect_decoding(t, "\x5f\x42\x01\x02\x43\x03\x04\x05\xff", "h'12345'", ^cbor.Bytes) +} + +@(test) +test_encode_bytes :: proc(t: ^testing.T) { + expect_encoding(t, &cbor.Bytes{}, "\x40") + expect_encoding(t, &cbor.Bytes{1, 2, 3, 4}, "\x44\x01\x02\x03\x04") + + // Indefinite lengths + + expect_streamed_encoding(t, "\x5f\x42\x01\x02\x43\x03\x04\x05\xff", &cbor.Bytes{1, 2}, &cbor.Bytes{3, 4, 5}) +} + +@(test) +test_decode_strings :: proc(t: ^testing.T) { + expect_decoding(t, "\x60", `""`, ^cbor.Text) + expect_decoding(t, "\x61\x61", `"a"`, ^cbor.Text) + expect_decoding(t, "\x64\x49\x45\x54\x46", `"IETF"`, ^cbor.Text) + expect_decoding(t, "\x62\x22\x5c", `""\"`, ^cbor.Text) + expect_decoding(t, "\x62\xc3\xbc", `"ü"`, ^cbor.Text) + expect_decoding(t, "\x63\xe6\xb0\xb4", `"水"`, ^cbor.Text) + expect_decoding(t, "\x64\xf0\x90\x85\x91", `"𐅑"`, ^cbor.Text) + + // Indefinite lengths + + expect_decoding(t, "\x7f\x65\x73\x74\x72\x65\x61\x64\x6d\x69\x6e\x67\xff", `"streaming"`, ^cbor.Text) +} + +@(test) +test_encode_strings :: proc(t: ^testing.T) { + expect_encoding(t, &cbor.Text{}, "\x60") + + a := "a" + expect_encoding(t, &a, "\x61\x61") + + b := "IETF" + expect_encoding(t, &b, "\x64\x49\x45\x54\x46") + + c := "\"\\" + expect_encoding(t, &c, "\x62\x22\x5c") + + d := "ü" + expect_encoding(t, &d, "\x62\xc3\xbc") + + e := "水" + expect_encoding(t, &e, "\x63\xe6\xb0\xb4") + + f := "𐅑" + expect_encoding(t, &f, "\x64\xf0\x90\x85\x91") + + // Indefinite lengths + + sa := "strea" + sb := "ming" + expect_streamed_encoding(t, "\x7f\x65\x73\x74\x72\x65\x61\x64\x6d\x69\x6e\x67\xff", &sa, &sb) +} + +@(test) +test_decode_lists :: proc(t: ^testing.T) { + expect_decoding(t, "\x80", "[]", ^cbor.Array) + expect_decoding(t, "\x83\x01\x02\x03", "[1, 2, 3]", ^cbor.Array) + expect_decoding(t, "\x83\x01\x82\x02\x03\x82\x04\x05", "[1, [2, 3], [4, 5]]", ^cbor.Array) + expect_decoding(t, "\x98\x19\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x18\x18\x19", "[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]", ^cbor.Array) + expect_decoding(t, "\x82\x61\x61\xa1\x61\x62\x61\x63", `["a", {"b": "c"}]`, ^cbor.Array) + + // Indefinite lengths + + expect_decoding(t, "\x9f\xff", "[]", ^cbor.Array) + expect_decoding(t, "\x9f\x01\x82\x02\x03\x9f\x04\x05\xff\xff", "[1, [2, 3], [4, 5]]", ^cbor.Array) + expect_decoding(t, "\x9f\x01\x82\x02\x03\x82\x04\x05\xff", "[1, [2, 3], [4, 5]]", ^cbor.Array) + expect_decoding(t, "\x83\x01\x82\x02\x03\x9f\x04\x05\xff", "[1, [2, 3], [4, 5]]", ^cbor.Array) + expect_decoding(t, "\x83\x01\x9f\x02\x03\xff\x82\x04\x05", "[1, [2, 3], [4, 5]]", ^cbor.Array) + expect_decoding(t, "\x9f\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x18\x18\x19\xff", "[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]", ^cbor.Array) + expect_decoding(t, "\x82\x61\x61\xbf\x61\x62\x61\x63\xff", `["a", {"b": "c"}]`, ^cbor.Array) +} + +@(test) +test_encode_lists :: proc(t: ^testing.T) { + expect_encoding(t, &cbor.Array{}, "\x80") + expect_encoding(t, &cbor.Array{u8(1), u8(2), u8(3)}, "\x83\x01\x02\x03") + expect_encoding(t, &cbor.Array{u8(1), &cbor.Array{u8(2), u8(3)}, &cbor.Array{u8(4), u8(5)}}, "\x83\x01\x82\x02\x03\x82\x04\x05") + expect_encoding(t, &cbor.Array{u8(1), u8(2), u8(3), u8(4), u8(5), u8(6), u8(7), u8(8), u8(9), u8(10), u8(11), u8(12), u8(13), u8(14), u8(15), u8(16), u8(17), u8(18), u8(19), u8(20), u8(21), u8(22), u8(23), u8(24), u8(25)}, "\x98\x19\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x18\x18\x19") + + { + a := "a" + b := "b" + c := "c" + expect_encoding(t, &cbor.Array{&a, &cbor.Map{{&b, &c}}}, "\x82\x61\x61\xa1\x61\x62\x61\x63") + } + + // Indefinite lengths + + expect_streamed_encoding(t, "\x9f\xff", &cbor.Array{}) + + { + bytes.buffer_reset(&buf) + + err: cbor.Encode_Error + err = cbor.encode_stream_begin(stream, .Array) + testing.expect_value(t, err, nil) + + { + err = cbor.encode_stream_array_item(encoder, u8(1)) + testing.expect_value(t, err, nil) + + err = cbor.encode_stream_array_item(encoder, &cbor.Array{u8(2), u8(3)}) + testing.expect_value(t, err, nil) + + err = cbor.encode_stream_begin(stream, .Array) + testing.expect_value(t, err, nil) + + { + err = cbor.encode_stream_array_item(encoder, u8(4)) + testing.expect_value(t, err, nil) + + err = cbor.encode_stream_array_item(encoder, u8(5)) + testing.expect_value(t, err, nil) + } + + err = cbor.encode_stream_end(stream) + testing.expect_value(t, err, nil) + } + + err = cbor.encode_stream_end(stream) + testing.expect_value(t, err, nil) + + testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)string("\x9f\x01\x82\x02\x03\x9f\x04\x05\xff\xff"))) + } + + { + bytes.buffer_reset(&buf) + + err: cbor.Encode_Error + err = cbor._encode_u8(stream, 2, .Array) + testing.expect_value(t, err, nil) + + a := "a" + err = cbor.encode(encoder, &a) + testing.expect_value(t, err, nil) + + { + err = cbor.encode_stream_begin(stream, .Map) + testing.expect_value(t, err, nil) + + b := "b" + c := "c" + err = cbor.encode_stream_map_entry(encoder, &b, &c) + testing.expect_value(t, err, nil) + + err = cbor.encode_stream_end(stream) + testing.expect_value(t, err, nil) + } + + testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)string("\x82\x61\x61\xbf\x61\x62\x61\x63\xff"))) + } +} + +@(test) +test_decode_maps :: proc(t: ^testing.T) { + expect_decoding(t, "\xa0", "{}", ^cbor.Map) + expect_decoding(t, "\xa2\x01\x02\x03\x04", "{1: 2, 3: 4}", ^cbor.Map) + expect_decoding(t, "\xa2\x61\x61\x01\x61\x62\x82\x02\x03", `{"a": 1, "b": [2, 3]}`, ^cbor.Map) + expect_decoding(t, "\xa5\x61\x61\x61\x41\x61\x62\x61\x42\x61\x63\x61\x43\x61\x64\x61\x44\x61\x65\x61\x45", `{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}`, ^cbor.Map) + + // Indefinite lengths + + expect_decoding(t, "\xbf\x61\x61\x01\x61\x62\x9f\x02\x03\xff\xff", `{"a": 1, "b": [2, 3]}`, ^cbor.Map) + expect_decoding(t, "\xbf\x63\x46\x75\x6e\xf5\x63\x41\x6d\x74\x21\xff", `{"Fun": true, "Amt": -2}`, ^cbor.Map) +} + +@(test) +test_encode_maps :: proc(t: ^testing.T) { + expect_encoding(t, &cbor.Map{}, "\xa0") + expect_encoding(t, &cbor.Map{{u8(1), u8(2)}, {u8(3), u8(4)}}, "\xa2\x01\x02\x03\x04") + + a := "a" + b := "b" + // NOTE: also tests the deterministic nature because it has to swap/sort the entries. + expect_encoding(t, &cbor.Map{{&b, &cbor.Array{u8(2), u8(3)}}, {&a, u8(1)}}, "\xa2\x61\x61\x01\x61\x62\x82\x02\x03") + + fun := "Fun" + amt := "Amt" + expect_streamed_encoding(t, "\xbf\x63\x46\x75\x6e\xf5\x63\x41\x6d\x74\x21\xff", &cbor.Map{{&fun, true}, {&amt, cbor.Negative_U8(1)}}) +} + +@(test) +test_decode_tags :: proc(t: ^testing.T) { + // Tag number 2 (unsigned bignumber), value bytes, max(u64) + 1. + expect_tag(t, "\xc2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", cbor.TAG_UNSIGNED_BIG_NR, "2(h'100000000')") + + // Tag number 3 (negative bignumber), value bytes, negative max(u64) - 1. + expect_tag(t, "\xc3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", cbor.TAG_NEGATIVE_BIG_NR, "3(h'100000000')") + + expect_tag(t, "\xc1\x1a\x51\x4b\x67\xb0", cbor.TAG_EPOCH_TIME_NR, "1(1363896240)") + expect_tag(t, "\xc1\xfb\x41\xd4\x52\xd9\xec\x20\x00\x00", cbor.TAG_EPOCH_TIME_NR, "1(1363896240.5000000000000000)") + expect_tag(t, "\xd8\x18\x45\x64\x49\x45\x54\x46", cbor.TAG_CBOR_NR, "24(h'6449455446')") +} + +@(test) +test_encode_tags :: proc(t: ^testing.T) { + expect_encoding(t, &cbor.Tag{cbor.TAG_UNSIGNED_BIG_NR, &cbor.Bytes{1, 0, 0, 0, 0, 0, 0, 0, 0}}, "\xc2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00") + expect_encoding(t, &cbor.Tag{cbor.TAG_EPOCH_TIME_NR, u32(1363896240)}, "\xc1\x1a\x51\x4b\x67\xb0") + expect_encoding(t, &cbor.Tag{cbor.TAG_EPOCH_TIME_NR, f64(1363896240.500)}, "\xc1\xfb\x41\xd4\x52\xd9\xec\x20\x00\x00") +} + +// Helpers + +buf: bytes.Buffer +stream := bytes.buffer_to_stream(&buf) +encoder := cbor.Encoder{cbor.ENCODE_FULLY_DETERMINISTIC, stream} + +expect_decoding :: proc(t: ^testing.T, encoded: string, decoded: string, type: typeid, loc := #caller_location) { + bytes.buffer_reset(&buf) + bytes.buffer_write_string(&buf, encoded) + + res, err := cbor.decode(stream) + defer cbor.destroy(res) + + testing.expect_value(t, reflect.union_variant_typeid(res), type, loc) + testing.expect_value(t, err, nil, loc) + + str := cbor.diagnose(res, padding=-1) + defer delete(str) + + testing.expect_value(t, str, decoded, loc) +} + +expect_tag :: proc(t: ^testing.T, encoded: string, nr: cbor.Tag_Number, value_decoded: string, loc := #caller_location) { + bytes.buffer_reset(&buf) + bytes.buffer_write_string(&buf, encoded) + + res, err := cbor.decode(stream) + defer cbor.destroy(res) + + testing.expect_value(t, err, nil, loc) + + if tag, is_tag := res.(^cbor.Tag); is_tag { + testing.expect_value(t, tag.number, nr, loc) + + str := cbor.diagnose(tag, padding=-1) + defer delete(str) + + testing.expect_value(t, str, value_decoded, loc) + } else { + testing.errorf(t, "Value %#v is not a tag", res, loc) + } +} + +expect_float :: proc(t: ^testing.T, encoded: string, expected: $T, loc := #caller_location) where intrinsics.type_is_float(T) { + bytes.buffer_reset(&buf) + bytes.buffer_write_string(&buf, encoded) + + res, err := cbor.decode(stream) + defer cbor.destroy(res) + + testing.expect_value(t, reflect.union_variant_typeid(res), typeid_of(T), loc) + testing.expect_value(t, err, nil, loc) + + #partial switch r in res { + case f16: + when T == f16 { testing.expect_value(t, res, expected, loc) } else { unreachable() } + case f32: + when T == f32 { testing.expect_value(t, res, expected, loc) } else { unreachable() } + case f64: + when T == f64 { testing.expect_value(t, res, expected, loc) } else { unreachable() } + case: + unreachable() + } +} + +expect_encoding :: proc(t: ^testing.T, val: cbor.Value, encoded: string, loc := #caller_location) { + bytes.buffer_reset(&buf) + + err := cbor.encode(encoder, val) + testing.expect_value(t, err, nil, loc) + testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc) +} + +expect_streamed_encoding :: proc(t: ^testing.T, encoded: string, values: ..cbor.Value, loc := #caller_location) { + bytes.buffer_reset(&buf) + + for value, i in values { + err: cbor.Encode_Error + err2: cbor.Encode_Error + #partial switch v in value { + case ^cbor.Bytes: + if i == 0 { err = cbor.encode_stream_begin(stream, .Bytes) } + err2 = cbor._encode_bytes(encoder, v^) + case ^cbor.Text: + if i == 0 { err = cbor.encode_stream_begin(stream, .Text) } + err2 = cbor._encode_text(encoder, v^) + case ^cbor.Array: + if i == 0 { err = cbor.encode_stream_begin(stream, .Array) } + for item in v { + err2 = cbor.encode_stream_array_item(encoder, item) + if err2 != nil { break } + } + case ^cbor.Map: + err = cbor.encode_stream_begin(stream, .Map) + for item in v { + err2 = cbor.encode_stream_map_entry(encoder, item.key, item.value) + if err2 != nil { break } + } + case: + testing.errorf(t, "%v does not support streamed encoding", reflect.union_variant_typeid(value)) + } + + testing.expect_value(t, err, nil, loc) + testing.expect_value(t, err2, nil, loc) + } + + err := cbor.encode_stream_end(stream) + testing.expect_value(t, err, nil, loc) + + testing.expect_value(t, fmt.tprint(bytes.buffer_to_bytes(&buf)), fmt.tprint(transmute([]byte)encoded), loc) +} |