487 lines
14 KiB
Go
487 lines
14 KiB
Go
package pdu
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"math"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// region encode
|
|
func TestEncodeReturnsByteSliceOfLength16(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 1,
|
|
Command_id: 1,
|
|
Command_status: 1,
|
|
Sequence_number: 1,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if buf.Cap() != 16 {
|
|
t.Errorf("Expected byte slice of length 16, got %d", buf.Cap())
|
|
}
|
|
}
|
|
|
|
func TestEncodeHandlesZeroValues(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 0,
|
|
Command_id: 0,
|
|
Command_status: 0,
|
|
Sequence_number: 0,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
expected := make([]byte, 16)
|
|
for i, v := range buf.Bytes() {
|
|
if v != expected[i] {
|
|
t.Errorf("Expected byte slice with zero values, got %v", buf.Bytes())
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEncodeEncodesProperly(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 1,
|
|
Command_id: 2,
|
|
Command_status: 3,
|
|
Sequence_number: 4,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
expected := []byte{0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4}
|
|
for i, v := range buf.Bytes() {
|
|
if v != expected[i] {
|
|
t.Errorf("Expected byte slice with values %v, got %v", expected, buf.Bytes())
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEncodeEncodesProperlyComplex(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 13426724,
|
|
Command_id: 254352,
|
|
Command_status: 35634264,
|
|
Sequence_number: 476543523,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
expected := []byte{0, 204, 224, 36, 0, 3, 225, 144, 2, 31, 188, 88, 28, 103, 122, 35}
|
|
for i, v := range buf.Bytes() {
|
|
if v != expected[i] {
|
|
t.Errorf("Expected byte slice with values %v, got %v", expected, buf.Bytes())
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEncodeIntoCorrectlyEncodesFields(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 16,
|
|
Command_id: 1,
|
|
Command_status: 0,
|
|
Sequence_number: 12345,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
innerbuf := buf.Bytes()
|
|
if binary.BigEndian.Uint32(innerbuf[0:4]) != p.Command_length {
|
|
t.Errorf("Expected command_length %d, got %d", p.Command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[4:8]) != p.Command_id {
|
|
t.Errorf("Expected command_id %d, got %d", p.Command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[8:12]) != p.Command_status {
|
|
t.Errorf("Expected command_status %d, got %d", p.Command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[12:16]) != p.Sequence_number {
|
|
t.Errorf("Expected sequence_number %d, got %d", p.Sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
|
|
}
|
|
}
|
|
|
|
func TestEncodeIntoHandlesLargerBuffer(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 16,
|
|
Command_id: 1,
|
|
Command_status: 0,
|
|
Sequence_number: 12345,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 20)) // larger buffer size
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
innerbuf := buf.Bytes()
|
|
if binary.BigEndian.Uint32(innerbuf[0:4]) != p.Command_length {
|
|
t.Errorf("Expected command_length %d, got %d", p.Command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[4:8]) != p.Command_id {
|
|
t.Errorf("Expected command_id %d, got %d", p.Command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[8:12]) != p.Command_status {
|
|
t.Errorf("Expected command_status %d, got %d", p.Command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[12:16]) != p.Sequence_number {
|
|
t.Errorf("Expected sequence_number %d, got %d", p.Sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
|
|
}
|
|
}
|
|
|
|
func TestEncodeIntoUsesBigEndianEncoding(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 16,
|
|
Command_id: 1,
|
|
Command_status: 0,
|
|
Sequence_number: 12345,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
innerbuf := buf.Bytes()
|
|
if binary.BigEndian.Uint32(innerbuf[0:4]) != p.Command_length {
|
|
t.Errorf("Expected command_length %d, got %d", p.Command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[4:8]) != p.Command_id {
|
|
t.Errorf("Expected command_id %d, got %d", p.Command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[8:12]) != p.Command_status {
|
|
t.Errorf("Expected command_status %d, got %d", p.Command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[12:16]) != p.Sequence_number {
|
|
t.Errorf("Expected sequence_number %d, got %d", p.Sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
|
|
}
|
|
}
|
|
|
|
func TestEncodeIntoConcurrencySafety(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 16,
|
|
Command_id: 1,
|
|
Command_status: 0,
|
|
Sequence_number: 12345,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 1000; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
p.Encode(buf)
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
innerbuf := buf.Bytes()
|
|
if binary.BigEndian.Uint32(innerbuf[0:4]) != p.Command_length {
|
|
t.Errorf("Expected command_length %d, got %d", p.Command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[4:8]) != p.Command_id {
|
|
t.Errorf("Expected command_id %d, got %d", p.Command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[8:12]) != p.Command_status {
|
|
t.Errorf("Expected command_status %d, got %d", p.Command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[12:16]) != p.Sequence_number {
|
|
t.Errorf("Expected sequence_number %d, got %d", p.Sequence_number, binary.BigEndian.Uint32(innerbuf[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 := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
innerbuf := buf.Bytes()
|
|
if binary.BigEndian.Uint32(innerbuf[0:4]) != p.Command_length {
|
|
t.Errorf("Expected command_length %d, got %d", p.Command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[4:8]) != p.Command_id {
|
|
t.Errorf("Expected command_id %d, got %d", p.Command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[8:12]) != p.Command_status {
|
|
t.Errorf("Expected command_status %d, got %d", p.Command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[12:16]) != p.Sequence_number {
|
|
t.Errorf("Expected sequence_number %d, got %d", p.Sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
|
|
}
|
|
}
|
|
|
|
func TestEncodeIntoWithBoundaryValues(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 0,
|
|
Command_id: 0,
|
|
Command_status: 0,
|
|
Sequence_number: 0,
|
|
}
|
|
buf := bytes.NewBuffer(make([]byte, 16))
|
|
buf.Reset()
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
innerbuf := buf.Bytes()
|
|
if binary.BigEndian.Uint32(innerbuf[0:4]) != p.Command_length {
|
|
t.Errorf("Expected command_length %d, got %d", p.Command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[4:8]) != p.Command_id {
|
|
t.Errorf("Expected command_id %d, got %d", p.Command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[8:12]) != p.Command_status {
|
|
t.Errorf("Expected command_status %d, got %d", p.Command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
|
|
}
|
|
if binary.BigEndian.Uint32(innerbuf[12:16]) != p.Sequence_number {
|
|
t.Errorf("Expected sequence_number %d, got %d", p.Sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
|
|
}
|
|
}
|
|
|
|
// region decode
|
|
func TestDecodeHandlesShortByteSlice(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := []byte{0, 0, 0, 10}
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Errorf("Decode panicked with short byte slice")
|
|
}
|
|
}()
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDecodeParsesValidByteSlice(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if p.Command_length != 16 {
|
|
t.Errorf("Expected command_length to be 16, got %d", p.Command_length)
|
|
}
|
|
if p.Command_id != 1 {
|
|
t.Errorf("Expected command_id to be 1, got %d", p.Command_id)
|
|
}
|
|
if p.Command_status != 2 {
|
|
t.Errorf("Expected command_status to be 2, got %d", p.Command_status)
|
|
}
|
|
if p.Sequence_number != 3 {
|
|
t.Errorf("Expected sequence_number to be 3, got %d", p.Sequence_number)
|
|
}
|
|
}
|
|
|
|
func TestDecodeHandlesLongerByteSliceWithoutCrashing(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := make([]byte, 20)
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Errorf("Decode panicked with long byte slice")
|
|
}
|
|
}()
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDecodeHandlesNilDataInput(t *testing.T) {
|
|
var p PDU_HEADER
|
|
err := p.Decode(nil)
|
|
|
|
if err == nil {
|
|
t.Errorf("Expected error, got none")
|
|
}
|
|
|
|
if p.Command_length != 0 {
|
|
t.Errorf("Expected command_length to be 0, got %d", p.Command_length)
|
|
}
|
|
if p.Command_id != 0 {
|
|
t.Errorf("Expected command_id to be 0, got %d", p.Command_id)
|
|
}
|
|
if p.Command_status != 0 {
|
|
t.Errorf("Expected command_status to be 0, got %d", p.Command_status)
|
|
}
|
|
if p.Sequence_number != 0 {
|
|
t.Errorf("Expected sequence_number to be 0, got %d", p.Sequence_number)
|
|
}
|
|
}
|
|
|
|
func TestDecodeHandlesEmptyByteSliceGracefully(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := []byte{}
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if p.Command_length != 0 {
|
|
t.Errorf("Expected command_length to be 0, got %d", p.Command_length)
|
|
}
|
|
if p.Command_id != 0 {
|
|
t.Errorf("Expected command_id to be 0, got %d", p.Command_id)
|
|
}
|
|
if p.Command_status != 0 {
|
|
t.Errorf("Expected command_status to be 0, got %d", p.Command_status)
|
|
}
|
|
if p.Sequence_number != 0 {
|
|
t.Errorf("Expected sequence_number to be 0, got %d", p.Sequence_number)
|
|
}
|
|
}
|
|
|
|
func TestDecodeDoesNotModifyInputByteSlice(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
|
|
originalData := make([]byte, len(data))
|
|
copy(originalData, data)
|
|
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
for i := range data {
|
|
if data[i] != originalData[i] {
|
|
t.Errorf("Expected data at index %d to be %d, got %d", i, originalData[i], data[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeHandlesByteSlicesWithMaxUint32Values(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := []byte{255, 255, 255, 255, 255, 255, 255, 255}
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
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 != math.MaxUint32 {
|
|
t.Errorf("Expected command_id to be %d, got %d", math.MaxUint32, p.Command_id)
|
|
}
|
|
}
|
|
|
|
func TestDecodeHandlesByteSlicesWithMinimumUint32Values(t *testing.T) {
|
|
var p PDU_HEADER
|
|
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
|
err := p.Decode(bytes.NewBuffer(data))
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if p.Command_length != 0 {
|
|
t.Errorf("Expected command_length to be 0, got %d", p.Command_length)
|
|
}
|
|
if p.Command_id != 0 {
|
|
t.Errorf("Expected command_id to be 0, got %d", p.Command_id)
|
|
}
|
|
}
|
|
|
|
// region size
|
|
func TestSizeReturns16(t *testing.T) {
|
|
var p PDU_HEADER
|
|
if p.Size() != 16 {
|
|
t.Errorf("Expected size to be 16, got %d", p.Size())
|
|
}
|
|
}
|
|
|
|
// region benchmarks
|
|
// With buffer pool
|
|
func BenchmarkEncodeWithBufferPool(b *testing.B) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 16,
|
|
Command_id: 1,
|
|
Command_status: 0,
|
|
Sequence_number: 12345,
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
buf := ByteBufferPool.Get(p.Size())
|
|
p.Encode(buf)
|
|
ByteBufferPool.Put(buf)
|
|
}
|
|
}
|
|
|
|
// Without buffer pool
|
|
func BenchmarkEncodeWithoutBufferPool(b *testing.B) {
|
|
p := &PDU_HEADER{
|
|
Command_length: 16,
|
|
Command_id: 1,
|
|
Command_status: 0,
|
|
Sequence_number: 12345,
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
buf := bytes.Buffer{}
|
|
p.Encode(&buf)
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecodeBufferPool(b *testing.B) {
|
|
p := &PDU_HEADER{}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
|
|
buf := ByteBufferPool.Get(len(data))
|
|
buf.Write(data)
|
|
p.Decode(buf)
|
|
ByteBufferPool.Put(buf)
|
|
}
|
|
}
|