From 4a819f15633952c2bb6316126d7ce79b38c9874b Mon Sep 17 00:00:00 2001 From: PhatPhuckDave <> Date: Mon, 22 Jul 2024 22:27:22 +0200 Subject: [PATCH] Add more tests for encoding --- pdu/pdu.go | 18 +++- pdu/pdu_test.go | 239 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 245 insertions(+), 12 deletions(-) diff --git a/pdu/pdu.go b/pdu/pdu.go index 9a4602d..4a0b437 100644 --- a/pdu/pdu.go +++ b/pdu/pdu.go @@ -1,6 +1,9 @@ package pdu -import "encoding/binary" +import ( + "encoding/binary" + "fmt" +) type ( PDU interface { @@ -22,20 +25,27 @@ type ( } ) -func (p *PDU_HEADER) Encode() []uint8 { +func (p *PDU_HEADER) Encode() ([]uint8, error) { buf := make([]uint8, 16) binary.BigEndian.PutUint32(buf[0:4], p.command_length) binary.BigEndian.PutUint32(buf[4:8], p.command_id) binary.BigEndian.PutUint32(buf[8:12], p.command_status) binary.BigEndian.PutUint32(buf[12:16], p.sequence_number) - return buf + return buf, nil } -func (p *PDU_HEADER) EncodeInto(buf *[]uint8) { +func (p *PDU_HEADER) EncodeInto(buf *[]uint8) error { + if buf == nil { + return fmt.Errorf("cannot encode PDU_HEADER, buffer is nil") + } + if len(*buf) < 16 { + return fmt.Errorf("cannot encode PDU_HEADER, buffer too small (%d, required 16)", len(*buf)) + } bufVal := *buf binary.BigEndian.PutUint32(bufVal[0:4], p.command_length) binary.BigEndian.PutUint32(bufVal[4:8], p.command_id) binary.BigEndian.PutUint32(bufVal[8:12], p.command_status) binary.BigEndian.PutUint32(bufVal[12:16], p.sequence_number) + return nil } func (p *PDU_HEADER) Decode(data []uint8) { if len(data) >= 4 { diff --git a/pdu/pdu_test.go b/pdu/pdu_test.go index 6f83a75..8570f5d 100644 --- a/pdu/pdu_test.go +++ b/pdu/pdu_test.go @@ -1,6 +1,9 @@ package pdu import ( + "encoding/binary" + "math" + "sync" "testing" ) @@ -12,7 +15,11 @@ func TestEncodeReturnsByteSliceOfLength16(t *testing.T) { command_status: 1, sequence_number: 1, } - result := p.Encode() + result, err := p.Encode() + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } if len(result) != 16 { t.Errorf("Expected byte slice of length 16, got %d", len(result)) } @@ -25,7 +32,11 @@ func TestEncodeHandlesZeroValues(t *testing.T) { command_status: 0, sequence_number: 0, } - result := p.Encode() + result, err := p.Encode() + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } expected := make([]uint8, 16) for i, v := range result { if v != expected[i] { @@ -42,7 +53,12 @@ func TestEncodeEncodesProperly(t *testing.T) { command_status: 3, sequence_number: 4, } - result := p.Encode() + result, err := p.Encode() + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + expected := []uint8{0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4} for i, v := range result { if v != expected[i] { @@ -59,7 +75,12 @@ func TestEncodeEncodesProperlyComplex(t *testing.T) { command_status: 35634264, sequence_number: 476543523, } - result := p.Encode() + result, err := p.Encode() + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + expected := []uint8{0, 204, 224, 36, 0, 3, 225, 144, 2, 31, 188, 88, 28, 103, 122, 35} for i, v := range result { if v != expected[i] { @@ -69,6 +90,208 @@ func TestEncodeEncodesProperlyComplex(t *testing.T) { } } +func TestEncodeIntoCorrectlyEncodesFields(t *testing.T) { + p := &PDU_HEADER{ + command_length: 16, + command_id: 1, + command_status: 0, + sequence_number: 12345, + } + buf := make([]uint8, 16) + err := p.EncodeInto(&buf) + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if binary.BigEndian.Uint32(buf[0:4]) != p.command_length { + t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4])) + } + if binary.BigEndian.Uint32(buf[4:8]) != p.command_id { + t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8])) + } + if binary.BigEndian.Uint32(buf[8:12]) != p.command_status { + t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12])) + } + if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number { + t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16])) + } +} + +func TestEncodeIntoHandlesNilBuffer(t *testing.T) { + p := &PDU_HEADER{ + command_length: 16, + command_id: 1, + command_status: 0, + sequence_number: 12345, + } + var buf *[]uint8 = nil + + err := p.EncodeInto(buf) + if err == nil { + t.Errorf("Expected error when buffer is nil") + } +} + +func TestEncodeIntoHandlesSmallerBuffer(t *testing.T) { + p := &PDU_HEADER{ + command_length: 16, + command_id: 1, + command_status: 0, + sequence_number: 12345, + } + buf := make([]uint8, 12) // smaller buffer size + + err := p.EncodeInto(&buf) + if err == nil { + t.Errorf("Expected error when buffer is too small") + } +} + +func TestEncodeIntoHandlesLargerBuffer(t *testing.T) { + p := &PDU_HEADER{ + command_length: 16, + command_id: 1, + command_status: 0, + sequence_number: 12345, + } + buf := make([]uint8, 20) + err := p.EncodeInto(&buf) + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if binary.BigEndian.Uint32(buf[0:4]) != p.command_length { + t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4])) + } + if binary.BigEndian.Uint32(buf[4:8]) != p.command_id { + t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8])) + } + if binary.BigEndian.Uint32(buf[8:12]) != p.command_status { + t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12])) + } + if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number { + t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16])) + } +} + +func TestEncodeIntoUsesBigEndianEncoding(t *testing.T) { + p := &PDU_HEADER{ + command_length: 16, + command_id: 1, + command_status: 0, + sequence_number: 12345, + } + buf := make([]uint8, 16) + err := p.EncodeInto(&buf) + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if binary.BigEndian.Uint32(buf[0:4]) != p.command_length { + t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4])) + } + if binary.BigEndian.Uint32(buf[4:8]) != p.command_id { + t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8])) + } + if binary.BigEndian.Uint32(buf[8:12]) != p.command_status { + t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12])) + } + if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number { + t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16])) + } +} + +func TestEncodeIntoConcurrencySafety(t *testing.T) { + p := &PDU_HEADER{ + command_length: 16, + command_id: 1, + command_status: 0, + sequence_number: 12345, + } + buf := make([]uint8, 16) + var wg sync.WaitGroup + for i := 0; i < 1000; i++ { + wg.Add(1) + go func() { + defer wg.Done() + p.EncodeInto(&buf) + }() + } + wg.Wait() + + if binary.BigEndian.Uint32(buf[0:4]) != p.command_length { + t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4])) + } + if binary.BigEndian.Uint32(buf[4:8]) != p.command_id { + t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8])) + } + if binary.BigEndian.Uint32(buf[8:12]) != p.command_status { + t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12])) + } + if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number { + t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16])) + } +} + +func TestEncodeIntoWithMaximumValues(t *testing.T) { + p := &PDU_HEADER{ + command_length: math.MaxUint32, + command_id: math.MaxUint32, + command_status: math.MaxUint32, + sequence_number: math.MaxUint32, + } + buf := make([]uint8, 16) + err := p.EncodeInto(&buf) + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if binary.BigEndian.Uint32(buf[0:4]) != p.command_length { + t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4])) + } + if binary.BigEndian.Uint32(buf[4:8]) != p.command_id { + t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8])) + } + if binary.BigEndian.Uint32(buf[8:12]) != p.command_status { + t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12])) + } + if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number { + t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16])) + } +} + +func TestEncodeIntoWithBoundaryValues(t *testing.T) { + p := &PDU_HEADER{ + command_length: 0, + command_id: 0, + command_status: 0, + sequence_number: 0, + } + buf := make([]uint8, 16) + err := p.EncodeInto(&buf) + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if binary.BigEndian.Uint32(buf[0:4]) != p.command_length { + t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4])) + } + if binary.BigEndian.Uint32(buf[4:8]) != p.command_id { + t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8])) + } + if binary.BigEndian.Uint32(buf[8:12]) != p.command_status { + t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12])) + } + if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number { + t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16])) + } +} + // region decode func TestDecodeHandlesShortByteSlice(t *testing.T) { var p PDU_HEADER @@ -164,11 +387,11 @@ func TestDecodeHandlesByteSlicesWithMaxUint32Values(t *testing.T) { var p PDU_HEADER data := []uint8{255, 255, 255, 255, 255, 255, 255, 255} p.Decode(data) - if p.command_length != 4294967295 { - t.Errorf("Expected command_length to be 4294967295, got %d", p.command_length) + if p.command_length != math.MaxUint32 { + t.Errorf("Expected command_length to be %d, got %d", math.MaxUint32, p.command_length) } - if p.command_id != 4294967295 { - t.Errorf("Expected command_id to be 4294967295, got %d", p.command_id) + if p.command_id != math.MaxUint32 { + t.Errorf("Expected command_id to be %d, got %d", math.MaxUint32, p.command_id) } }