package tape import "bytes" import "testing" import "reflect" import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil" var samplePayloads = [][]byte { /* int8 */ []byte { byte(LSI.WithCN(0)), 0x45 }, /* int16 */ []byte { byte(LSI.WithCN(1)), 0x45, 0x67 }, /* int32 */ []byte { byte(LSI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB }, /* int64 */ []byte { byte(LSI.WithCN(7)), 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 }, /* uint5 */ []byte { byte(SI.WithCN(12)) }, /* uint8 */ []byte { byte(LI.WithCN(0)), 0x45 }, /* uint16 */ []byte { byte(LI.WithCN(1)), 0x45, 0x67 }, /* uint32 */ []byte { byte(LI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB }, /* uint64 */ []byte { byte(LI.WithCN(7)), 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 }, /* bool */ []byte { byte(SI.WithCN(0)) }, /* bool */ []byte { byte(SI.WithCN(1)) }, /* string */ []byte { byte(SBA.WithCN(7)), 'p', 'u', 'p', 'e', 'v', 'e', 'r' }, /* []byte */ []byte { byte(SBA.WithCN(5)), 'b', 'l', 'a', 'r', 'g' }, /* []string */ []byte { byte(OTA.WithCN(0)), 2, byte(LBA.WithCN(0)), 0x08, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x05, 0x11, 0x11, 0x11, 0x11, 0x11, }, /* map[uint16] any */ []byte { byte(KTV.WithCN(0)), 2, 0x02, 0x23, byte(LSI.WithCN(1)), 0x45, 0x67, 0x02, 0x24, byte(LI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB, }, /* map[uint16] any */ []byte { byte(KTV.WithCN(0)), 3, 0x00, 0x01, 0x63, 0x43, 0xF4, 0xC0, 0x00, 0x00, 0x02, 0x82, 'h', 'i', 0x00, 0x03, 0x21, 0x39, 0x92, }, } var sampleValues = []any { /* int8 */ int8(0x45), /* int16 */ int16(0x4567), /* int32 */ int32(0x456789AB), /* int64 */ int64(0x456789ABCDEF0123), /* uint5 */ uint8(12), /* uint8 */ uint8(0x45), /* uint16 */ uint16(0x4567), /* uint32 */ uint32(0x456789AB), /* uint64 */ uint64(0x456789ABCDEF0123), /* bool */ false, /* bool */ true, /* string */ "pupever", /* []byte */ "blarg", /* []string */ []string { "\x45\x67\x89\xAB\xCD\xEF\x01\x23", "\x11\x11\x11\x11\x11", }, /* map[uint16] any */ map[uint16] any { 0x0223: int16(0x4567), 0x0224: uint32(0x456789AB), }, /* map[uint16] any */ map[uint16] any { 0x0001: float32(489.5), 0x0002: "hi", 0x0003: uint16(0x3992), }, } type userDefinedInteger int16 func TestEncodeAnyInt(test *testing.T) { err := testEncodeAny(test, uint8(0xCA), LI.WithCN(0), tu.S(0xCA)) if err != nil { test.Fatal(err) } err = testEncodeAny(test, 400, LSI.WithCN(3), tu.S( 0, 0, 0x1, 0x90, )) if err != nil { test.Fatal(err) } } func TestEncodeAnyTable(test *testing.T) { err := testEncodeAny(test, map[uint16] any { 0xF3B9: 1, 0x0102: 2, 0x0000: "hi!", 0xFFFF: []uint16 { 0xBEE5, 0x7777 }, 0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} }, 0x2345: [][]int16 { []int16 { 0x5 }, []int16 { 0x17, -0xAAA } }, 0x3456: userDefinedInteger(0x3921), 0x1F1F: float32(67.26), 0x0F0F: float64(5.3), 0xAAAA: false, 0xBBBB: true, }, KTV.WithCN(0), tu.S(11).AddVar( []byte { 0xF3, 0xB9, byte(LSI.WithCN(3)), 0, 0, 0, 1, }, []byte { 0x01, 0x02, byte(LSI.WithCN(3)), 0, 0, 0, 2, }, []byte { 0, 0, byte(SBA.WithCN(3)), 'h', 'i', '!', }, []byte { 0xFF, 0xFF, byte(OTA.WithCN(0)), 2, byte(LI.WithCN(1)), 0xBE, 0xE5, 0x77, 0x77, }, []byte { 0x12, 0x34, byte(OTA.WithCN(0)), 2, byte(OTA.WithCN(0)), 1, byte(LI.WithCN(1)), 0, 0x5, 2, byte(LI.WithCN(1)), 0, 0x17, 0xAA, 0xAA, }, []byte { 0x23, 0x45, byte(OTA.WithCN(0)), 2, byte(OTA.WithCN(0)), 1, byte(LSI.WithCN(1)), 0, 0x5, 2, byte(LSI.WithCN(1)), 0, 0x17, 0xF5, 0x56, }, []byte { 0x34, 0x56, byte(LSI.WithCN(1)), 0x39, 0x21, }, []byte { 0x1F, 0x1F, byte(FP.WithCN(3)), 0x42, 0x86, 0x85, 0x1F, }, []byte { 0x0F, 0x0F, byte(FP.WithCN(7)), 0x40, 0x15, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, }, []byte { 0xAA, 0xAA, byte(SI.WithCN(0)), }, []byte { 0xBB, 0xBB, byte(SI.WithCN(1)), }, )) if err != nil { test.Fatal(err) } } func TestDecodeWrongType(test *testing.T) { for index, data := range samplePayloads { test.Logf("data %2d %v [%s]", index, Tag(data[0]), tu.HexBytes(data[1:])) // integers should only assign to other integers if index > 10 { cas := func(destination any) { n, err := DecodeAnyInto(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0])) if err != nil { test.Fatalf("error: %v | n: %d", err, n) } reflectValue := reflect.ValueOf(destination).Elem() if reflectValue.CanInt() { if reflectValue.Int() != 0 { test.Fatalf("destination not zero: %v", reflectValue.Elem().Interface()) } } else if reflectValue.Kind() != reflect.Bool { if reflectValue.Uint() != 0 { test.Fatalf("destination not zero: %v", reflectValue.Elem().Interface()) } } if n != len(data) - 1 { test.Fatalf("n not equal: %d != %d", n, len(data) - 1) } } test.Log("- int8") { var dest int8; cas(&dest) } test.Log("- int16") { var dest int16; cas(&dest) } test.Log("- int32") { var dest int32; cas(&dest) } test.Log("- int64") { var dest int64; cas(&dest) } test.Log("- uint8") { var dest uint8; cas(&dest) } test.Log("- uint16") { var dest uint16; cas(&dest) } test.Log("- uint32") { var dest uint32; cas(&dest) } test.Log("- uint64") { var dest uint64; cas(&dest) } test.Log("- bool") { var dest bool; cas(&dest) } } arrayCase := func(destination any) { n, err := DecodeAnyInto(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0])) if err != nil { test.Fatalf("error: %v | n: %d", err, n) } reflectDestination := reflect.ValueOf(destination) reflectValue := reflectDestination.Elem() if reflectValue.Len() != 0 { test.Fatalf("len(destination) not zero: %v", reflectValue.Interface()) } if n != len(data) - 1 { test.Fatalf("n not equal: %d != %d", n, len(data) - 1) } } // SBA/LBA types should only assign to other SBA/LBA types if index != 11 && index != 12 { test.Log("- string") { var dest string; arrayCase(&dest) } test.Log("- []byte") { var dest []byte; arrayCase(&dest) } } // arrays should only assign to other arrays if index != 13 { test.Log("- []string") { var dest []string; arrayCase(&dest) } } // tables should only assign to other tables if index != 14 && index != 15 { test.Log("- map[uint16] any") { var dest = map[uint16] any { }; arrayCase(&dest) } } } } func TestEncodeDecodeAnyTable(test *testing.T) { err := testEncodeDecodeAny(test, map[uint16] any { 0xF3B9: uint32(1), 0x0102: uint32(2), 0x0103: int64(23432), 0x0104: int64(-88777), 0x0000: "hi!", 0xFFFF: []uint16 { 0xBEE5, 0x7777 }, 0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} }, 0x1F1F: float32(67.26), 0x0F0F: float64(5.3), }, nil) if err != nil { test.Fatal(err) } } func TestEncodeDecodeAnyDestination(test *testing.T) { var destination any for index, data := range samplePayloads { if _, isBool := sampleValues[index].(bool); isBool { // test is invalid for bools because they are never // created as a skeleton value continue } tag := Tag(data[0]) payload := data[1:] test.Logf("data %2d %v [%s]", index, tag, tu.HexBytes(payload)) n, err := DecodeAnyInto(NewDecoder(bytes.NewBuffer(payload)), &destination, tag) if err != nil { test.Fatalf("error: %v | n: %d", err, n) } got := destination correct := sampleValues[index] test.Log("got: ", tu.Describe(got)) test.Log("correct:", tu.Describe(correct)) if !reflect.DeepEqual(got, correct) { test.Fatalf("values not equal") } if n != len(payload) { test.Fatalf("n not equal: %d != %d", n, len(payload)) } } } func TestPeekSlice(test *testing.T) { buffer := bytes.NewBuffer([]byte { 2, byte(OTA.WithCN(3)), 0, 0, 0, 1, byte(LI.WithCN(1)), 0, 0x5, 2, byte(LI.WithCN(1)), 0, 0x17, 0xAA, 0xAA, }) decoder := NewDecoder(buffer) elem, dimension, err := peekSlice(decoder, OTA.WithCN(0)) if err != nil { test.Fatal(err) } if elem != LI.WithCN(1) { test.Fatalf("wrong element tag: %v %02X", elem, byte(elem)) } if got, correct := dimension, 2; got != correct { test.Fatalf("wrong dimension: %d != %d", got, correct) } } func TestPeekSliceOnce(test *testing.T) { buffer := bytes.NewBuffer([]byte { 2, byte(OTA.WithCN(3)), 0, 0, 0, 1, byte(LI.WithCN(1)), 0, 0x5, 2, byte(LI.WithCN(1)), 0, 0x17, 0xAA, 0xAA, }) decoder := NewDecoder(buffer) test.Log("--- stage 1") elem, populated, n, err := peekSliceOnce(decoder, OTA.WithCN(0), 0) if err != nil { test.Fatal(err) } if elem != OTA.WithCN(3) { test.Fatal("wrong element tag:", elem) } if !populated { test.Fatal("wrong populated:", populated) } if got, correct := n, 2; got != correct { test.Fatalf("wrong n: %d != %d", got, correct) } test.Log("--- stage 2") elem, populated, n, err = peekSliceOnce(decoder, elem, n) if err != nil { test.Fatal(err) } if elem != LI.WithCN(1) { test.Fatal("wrong element tag:", elem) } if !populated { test.Fatal("wrong populated:", populated) } if got, correct := n, 7; got != correct { test.Fatalf("wrong n: %d != %d", got, correct) } } func TestTagAny(test *testing.T) { cases := [][2]any { [2]any { LSI.WithCN(3), int(9) }, [2]any { LSI.WithCN(0), int8(9) }, [2]any { LSI.WithCN(1), int16(9) }, [2]any { LSI.WithCN(3), int32(9) }, [2]any { LSI.WithCN(7), int64(9) }, [2]any { LI.WithCN(3), uint(9) }, [2]any { LI.WithCN(0), uint8(9) }, [2]any { LI.WithCN(1), uint16(9) }, [2]any { LI.WithCN(3), uint32(9) }, [2]any { LI.WithCN(7), uint64(9) }, [2]any { FP.WithCN(3), float32(9) }, [2]any { FP.WithCN(7), float64(9) }, [2]any { SBA.WithCN(12), "small string" }, [2]any { SBA.WithCN(12), []byte("small string") }, [2]any { LBA.WithCN(0), "this is a very long string that is long" }, [2]any { LBA.WithCN(0), []byte("this is a very long string that is long") }, [2]any { LBA.WithCN(1), lipsum }, [2]any { OTA.WithCN(0), []int { 1, 2, 3, 4, 5 } }, [2]any { OTA.WithCN(0), []string { "1, 2, 3, 4, 5" } }, [2]any { KTV.WithCN(0), map[uint16] any { 0: 1, 1: "wow", 2: 10.238, 45: -9, 9: map[uint16] any { }, }}, } for _, cas := range cases { test.Log(cas) got, err := TagAny(cas[1]) if err != nil { test.Fatal(err) } if correct := cas[0].(Tag); correct != got { test.Fatalf("wrong tag: %v != %v", got, correct) } } } func TestDecodeAny(test *testing.T) { for index, payload := range samplePayloads { if _, isBool := sampleValues[index].(bool); isBool { // test is invalid for bools because they are never // created as a skeleton value continue } correctValue := sampleValues[index] data := payload[1:] decoder := NewDecoder(bytes.NewBuffer(data)) tag := Tag(payload[0]) decoded, n, err := DecodeAny(decoder, tag) test.Log("n: ", n) test.Log("tag: ", tag) test.Log("got: ", tu.Describe(decoded)) test.Log("correct:", tu.Describe(correctValue)) if err != nil { test.Fatal(err) } if !reflect.DeepEqual(decoded, correctValue) { test.Fatal("values not equal") } if n != len(data) { test.Fatalf("n not equal: %d != %d", n, len(data)) } } }