package byteframe import ( "bytes" "encoding/binary" "io" "math" "testing" ) func TestNewByteFrame(t *testing.T) { bf := NewByteFrame() if bf == nil { t.Fatal("NewByteFrame() returned nil") } if bf.index != 0 { t.Errorf("index = %d, want 0", bf.index) } if bf.usedSize != 0 { t.Errorf("usedSize = %d, want 0", bf.usedSize) } if len(bf.buf) != 4 { t.Errorf("buf length = %d, want 4", len(bf.buf)) } if bf.byteOrder != binary.BigEndian { t.Error("byteOrder should be BigEndian by default") } } func TestNewByteFrameFromBytes(t *testing.T) { input := []byte{0x01, 0x02, 0x03, 0x04} bf := NewByteFrameFromBytes(input) if bf == nil { t.Fatal("NewByteFrameFromBytes() returned nil") } if bf.index != 0 { t.Errorf("index = %d, want 0", bf.index) } if bf.usedSize != uint(len(input)) { t.Errorf("usedSize = %d, want %d", bf.usedSize, len(input)) } if !bytes.Equal(bf.buf, input) { t.Errorf("buf = %v, want %v", bf.buf, input) } // Verify it's a copy, not the same slice input[0] = 0xFF if bf.buf[0] == 0xFF { t.Error("NewByteFrameFromBytes should make a copy, not use the same slice") } } func TestByteFrame_WriteAndReadUint8(t *testing.T) { bf := NewByteFrame() values := []uint8{0, 1, 127, 128, 255} for _, v := range values { bf.WriteUint8(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadUint8() if got != expected { t.Errorf("ReadUint8()[%d] = %d, want %d", i, got, expected) } } } func TestByteFrame_WriteAndReadUint16(t *testing.T) { tests := []struct { name string value uint16 }{ {"zero", 0}, {"one", 1}, {"max_int8", 127}, {"max_uint8", 255}, {"max_int16", 32767}, {"max_uint16", 65535}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { bf := NewByteFrame() bf.WriteUint16(tt.value) bf.Seek(0, io.SeekStart) got := bf.ReadUint16() if got != tt.value { t.Errorf("ReadUint16() = %d, want %d", got, tt.value) } }) } } func TestByteFrame_WriteAndReadUint32(t *testing.T) { tests := []struct { name string value uint32 }{ {"zero", 0}, {"one", 1}, {"max_uint16", 65535}, {"max_uint32", 4294967295}, {"arbitrary", 0x12345678}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { bf := NewByteFrame() bf.WriteUint32(tt.value) bf.Seek(0, io.SeekStart) got := bf.ReadUint32() if got != tt.value { t.Errorf("ReadUint32() = %d, want %d", got, tt.value) } }) } } func TestByteFrame_WriteAndReadUint64(t *testing.T) { tests := []struct { name string value uint64 }{ {"zero", 0}, {"one", 1}, {"max_uint32", 4294967295}, {"max_uint64", 18446744073709551615}, {"arbitrary", 0x123456789ABCDEF0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { bf := NewByteFrame() bf.WriteUint64(tt.value) bf.Seek(0, io.SeekStart) got := bf.ReadUint64() if got != tt.value { t.Errorf("ReadUint64() = %d, want %d", got, tt.value) } }) } } func TestByteFrame_WriteAndReadInt8(t *testing.T) { values := []int8{-128, -1, 0, 1, 127} bf := NewByteFrame() for _, v := range values { bf.WriteInt8(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadInt8() if got != expected { t.Errorf("ReadInt8()[%d] = %d, want %d", i, got, expected) } } } func TestByteFrame_WriteAndReadInt16(t *testing.T) { values := []int16{-32768, -1, 0, 1, 32767} bf := NewByteFrame() for _, v := range values { bf.WriteInt16(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadInt16() if got != expected { t.Errorf("ReadInt16()[%d] = %d, want %d", i, got, expected) } } } func TestByteFrame_WriteAndReadInt32(t *testing.T) { values := []int32{-2147483648, -1, 0, 1, 2147483647} bf := NewByteFrame() for _, v := range values { bf.WriteInt32(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadInt32() if got != expected { t.Errorf("ReadInt32()[%d] = %d, want %d", i, got, expected) } } } func TestByteFrame_WriteAndReadInt64(t *testing.T) { values := []int64{-9223372036854775808, -1, 0, 1, 9223372036854775807} bf := NewByteFrame() for _, v := range values { bf.WriteInt64(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadInt64() if got != expected { t.Errorf("ReadInt64()[%d] = %d, want %d", i, got, expected) } } } func TestByteFrame_WriteAndReadFloat32(t *testing.T) { values := []float32{0.0, -1.5, 1.5, 3.14159, math.MaxFloat32, -math.MaxFloat32} bf := NewByteFrame() for _, v := range values { bf.WriteFloat32(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadFloat32() if got != expected { t.Errorf("ReadFloat32()[%d] = %f, want %f", i, got, expected) } } } func TestByteFrame_WriteAndReadFloat64(t *testing.T) { values := []float64{0.0, -1.5, 1.5, 3.14159265358979, math.MaxFloat64, -math.MaxFloat64} bf := NewByteFrame() for _, v := range values { bf.WriteFloat64(v) } bf.Seek(0, io.SeekStart) for i, expected := range values { got := bf.ReadFloat64() if got != expected { t.Errorf("ReadFloat64()[%d] = %f, want %f", i, got, expected) } } } func TestByteFrame_WriteAndReadBool(t *testing.T) { bf := NewByteFrame() bf.WriteBool(true) bf.WriteBool(false) bf.WriteBool(true) bf.Seek(0, io.SeekStart) if got := bf.ReadBool(); got != true { t.Errorf("ReadBool()[0] = %v, want true", got) } if got := bf.ReadBool(); got != false { t.Errorf("ReadBool()[1] = %v, want false", got) } if got := bf.ReadBool(); got != true { t.Errorf("ReadBool()[2] = %v, want true", got) } } func TestByteFrame_WriteAndReadBytes(t *testing.T) { bf := NewByteFrame() input := []byte{0x01, 0x02, 0x03, 0x04, 0x05} bf.WriteBytes(input) bf.Seek(0, io.SeekStart) got := bf.ReadBytes(uint(len(input))) if !bytes.Equal(got, input) { t.Errorf("ReadBytes() = %v, want %v", got, input) } } func TestByteFrame_WriteAndReadNullTerminatedBytes(t *testing.T) { bf := NewByteFrame() input := []byte("Hello, World!") bf.WriteNullTerminatedBytes(input) bf.Seek(0, io.SeekStart) got := bf.ReadNullTerminatedBytes() if !bytes.Equal(got, input) { t.Errorf("ReadNullTerminatedBytes() = %v, want %v", got, input) } } func TestByteFrame_ReadNullTerminatedBytes_NoNull(t *testing.T) { bf := NewByteFrame() input := []byte("Hello") bf.WriteBytes(input) bf.Seek(0, io.SeekStart) got := bf.ReadNullTerminatedBytes() // When there's no null terminator, it should return empty slice if len(got) != 0 { t.Errorf("ReadNullTerminatedBytes() = %v, want empty slice", got) } } func TestByteFrame_Endianness(t *testing.T) { // Test BigEndian (default) bfBE := NewByteFrame() bfBE.WriteUint16(0x1234) dataBE := bfBE.Data() if dataBE[0] != 0x12 || dataBE[1] != 0x34 { t.Errorf("BigEndian: got %X %X, want 12 34", dataBE[0], dataBE[1]) } // Test LittleEndian bfLE := NewByteFrame() bfLE.SetLE() bfLE.WriteUint16(0x1234) dataLE := bfLE.Data() if dataLE[0] != 0x34 || dataLE[1] != 0x12 { t.Errorf("LittleEndian: got %X %X, want 34 12", dataLE[0], dataLE[1]) } } func TestByteFrame_Seek(t *testing.T) { bf := NewByteFrame() bf.WriteBytes([]byte{0x01, 0x02, 0x03, 0x04, 0x05}) tests := []struct { name string offset int64 whence int wantIndex uint wantErr bool }{ {"seek_start_0", 0, io.SeekStart, 0, false}, {"seek_start_2", 2, io.SeekStart, 2, false}, {"seek_start_5", 5, io.SeekStart, 5, false}, {"seek_start_beyond", 6, io.SeekStart, 5, true}, {"seek_current_forward", 2, io.SeekCurrent, 5, true}, // Will go beyond max {"seek_current_backward", -3, io.SeekCurrent, 2, false}, {"seek_current_before_start", -10, io.SeekCurrent, 2, true}, {"seek_end_0", 0, io.SeekEnd, 5, false}, {"seek_end_negative", -2, io.SeekEnd, 3, false}, {"seek_end_beyond", 1, io.SeekEnd, 3, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset to known position for each test bf.Seek(5, io.SeekStart) pos, err := bf.Seek(tt.offset, tt.whence) if tt.wantErr { if err == nil { t.Errorf("Seek() expected error, got nil") } } else { if err != nil { t.Errorf("Seek() unexpected error: %v", err) } if bf.index != tt.wantIndex { t.Errorf("index = %d, want %d", bf.index, tt.wantIndex) } if uint(pos) != tt.wantIndex { t.Errorf("returned position = %d, want %d", pos, tt.wantIndex) } } }) } } func TestByteFrame_Data(t *testing.T) { bf := NewByteFrame() input := []byte{0x01, 0x02, 0x03, 0x04, 0x05} bf.WriteBytes(input) data := bf.Data() if !bytes.Equal(data, input) { t.Errorf("Data() = %v, want %v", data, input) } } func TestByteFrame_DataFromCurrent(t *testing.T) { bf := NewByteFrame() bf.WriteBytes([]byte{0x01, 0x02, 0x03, 0x04, 0x05}) bf.Seek(2, io.SeekStart) data := bf.DataFromCurrent() expected := []byte{0x03, 0x04, 0x05} if !bytes.Equal(data, expected) { t.Errorf("DataFromCurrent() = %v, want %v", data, expected) } } func TestByteFrame_Index(t *testing.T) { bf := NewByteFrame() if bf.Index() != 0 { t.Errorf("Index() = %d, want 0", bf.Index()) } bf.WriteUint8(0x01) if bf.Index() != 1 { t.Errorf("Index() = %d, want 1", bf.Index()) } bf.WriteUint16(0x0102) if bf.Index() != 3 { t.Errorf("Index() = %d, want 3", bf.Index()) } } func TestByteFrame_BufferGrowth(t *testing.T) { bf := NewByteFrame() initialCap := len(bf.buf) // Write enough data to force growth for i := 0; i < 100; i++ { bf.WriteUint32(uint32(i)) } if len(bf.buf) <= initialCap { t.Errorf("Buffer should have grown, initial cap: %d, current: %d", initialCap, len(bf.buf)) } // Verify all data is still accessible bf.Seek(0, io.SeekStart) for i := 0; i < 100; i++ { got := bf.ReadUint32() if got != uint32(i) { t.Errorf("After growth, ReadUint32()[%d] = %d, want %d", i, got, i) break } } } func TestByteFrame_ReadPanic(t *testing.T) { defer func() { if r := recover(); r == nil { t.Error("Reading beyond buffer should panic") } }() bf := NewByteFrame() bf.WriteUint8(0x01) bf.Seek(0, io.SeekStart) bf.ReadUint8() bf.ReadUint16() // Should panic - trying to read 2 bytes when only 1 was written } func TestByteFrame_SequentialWrites(t *testing.T) { bf := NewByteFrame() bf.WriteUint8(0x01) bf.WriteUint16(0x0203) bf.WriteUint32(0x04050607) bf.WriteUint64(0x08090A0B0C0D0E0F) expected := []byte{ 0x01, // uint8 0x02, 0x03, // uint16 0x04, 0x05, 0x06, 0x07, // uint32 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // uint64 } data := bf.Data() if !bytes.Equal(data, expected) { t.Errorf("Sequential writes: got %X, want %X", data, expected) } } func BenchmarkByteFrame_WriteUint8(b *testing.B) { bf := NewByteFrame() b.ResetTimer() for i := 0; i < b.N; i++ { bf.WriteUint8(0x42) } } func BenchmarkByteFrame_WriteUint32(b *testing.B) { bf := NewByteFrame() b.ResetTimer() for i := 0; i < b.N; i++ { bf.WriteUint32(0x12345678) } } func BenchmarkByteFrame_ReadUint32(b *testing.B) { bf := NewByteFrame() for i := 0; i < 1000; i++ { bf.WriteUint32(0x12345678) } b.ResetTimer() for i := 0; i < b.N; i++ { bf.Seek(0, io.SeekStart) bf.ReadUint32() } } func BenchmarkByteFrame_WriteBytes(b *testing.B) { bf := NewByteFrame() data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} b.ResetTimer() for i := 0; i < b.N; i++ { bf.WriteBytes(data) } }