I just learnt that uint8 and byte are the fucking same And that bytes.Buffer is the thing to use Idiot idiot idiot... Live and learn
564 lines
16 KiB
Go
564 lines
16 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))
|
|
err := p.Encode(buf)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if buf.Len() != 16 {
|
|
t.Errorf("Expected byte slice of length 16, got %d", buf.Len())
|
|
}
|
|
}
|
|
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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 TestEncodeIntoHandlesNilBuffer(t *testing.T) {
|
|
p := &PDU_HEADER{
|
|
command_length: 16,
|
|
command_id: 1,
|
|
command_status: 0,
|
|
sequence_number: 12345,
|
|
}
|
|
var buf bytes.Buffer
|
|
err := p.Encode(&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 := bytes.NewBuffer(make([]byte, 12)) // smaller buffer size
|
|
err := p.Encode(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 := bytes.NewBuffer(make([]byte, 20)) // larger buffer size
|
|
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))
|
|
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))
|
|
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))
|
|
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))
|
|
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 TestRealScenario(t *testing.T) {
|
|
// expected := []byte{0, 0, 0, 54, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 67, 77, 84, 0, 1, 1, 49, 50, 51, 52, 53, 0, 1, 1, 54, 55, 56, 57, 48, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 72, 101, 108, 108, 111, 44, 32, 83, 77, 80, 80, 33}
|
|
// p := &SUBMIT_SM{
|
|
// header: PDU_HEADER{
|
|
// command_length: 0,
|
|
// command_id: 4,
|
|
// command_status: 0,
|
|
// sequence_number: 1,
|
|
// },
|
|
// service_type: "CMT",
|
|
// source_addr_ton: 1,
|
|
// source_addr_npi: 1,
|
|
// source_addr: "12345",
|
|
// dest_addr_ton: 1,
|
|
// dest_addr_npi: 1,
|
|
// destination_addr: "67890",
|
|
// esm_class: 0,
|
|
// protocol_id: 0,
|
|
// priority_flag: 0,
|
|
// schedule_delivery_time: "",
|
|
// validity_period: "",
|
|
// registered_delivery: 1,
|
|
// data_coding: 0,
|
|
// sm_default_msg_id: 0,
|
|
// short_message: "Hello, SMPP!",
|
|
// }
|
|
// p.header.command_length = uint32(p.Size())
|
|
// p.sm_length = byte(len(p.short_message))
|
|
// buf := make([]byte, p.Size())
|
|
// err := p.EncodeInto(&buf)
|
|
// if err != nil {
|
|
// t.Errorf("Expected no error, got %v", err)
|
|
// }
|
|
// if len(buf) != len(expected) {
|
|
// t.Errorf("Expected byte slice of length %d, got %d", len(expected), len(buf))
|
|
// }
|
|
// for i, v := range buf {
|
|
// if v != expected[i] {
|
|
// t.Errorf("Expected byte slice with values %v, got %v", expected, buf)
|
|
// break
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// // 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(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(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(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 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 TestDecodeHandlesEmptyByteSliceGracefully(t *testing.T) {
|
|
// var p PDU_HEADER
|
|
// data := []byte{}
|
|
// err := p.Decode(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(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(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(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 BenchmarkEncode(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, _ := p.Encode()
|
|
// ByteBufferPool.Put(buf)
|
|
// }
|
|
// }
|
|
|
|
// // These two are effectively the same but there is difference in execution time
|
|
// // I wonder why...
|
|
// // I should stop writing benchmarks...
|
|
// 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(uint(p.Size()))
|
|
// p.EncodeInto(buf)
|
|
// ByteBufferPool.Put(buf)
|
|
// }
|
|
// }
|
|
|
|
// // Without buffer pool
|
|
// func BenchmarkEncodeInto(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 := make([]byte, 16)
|
|
// p.EncodeInto(&buf)
|
|
// }
|
|
// }
|
|
|
|
// func BenchmarkDecode(b *testing.B) {
|
|
// p := &PDU_HEADER{}
|
|
// data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
|
|
// b.ResetTimer()
|
|
// for i := 0; i < b.N; i++ {
|
|
// p.Decode(data)
|
|
// }
|
|
// }
|