mqtt/server/internal/circ/buffer_test.go

318 lines
7.3 KiB
Go
Raw Normal View History

2022-08-15 23:06:20 +03:00
package circ
import (
//"fmt"
"sync/atomic"
"testing"
"time"
"unsafe"
"github.com/stretchr/testify/require"
)
func TestNewBuffer(t *testing.T) {
var size int = 16
var block int = 4
buf := NewBuffer(size, block)
require.NotNil(t, buf.buf)
require.NotNil(t, buf.rcond)
require.NotNil(t, buf.wcond)
require.Equal(t, size, len(buf.buf))
require.Equal(t, size, buf.size)
require.Equal(t, block, buf.block)
}
func TestNewBuffer0Size(t *testing.T) {
buf := NewBuffer(0, 0)
require.NotNil(t, buf.buf)
require.Equal(t, DefaultBufferSize, buf.size)
require.Equal(t, DefaultBlockSize, buf.block)
}
func TestNewBufferUndersize(t *testing.T) {
buf := NewBuffer(DefaultBlockSize+10, DefaultBlockSize)
require.NotNil(t, buf.buf)
require.Equal(t, DefaultBlockSize*2, buf.size)
require.Equal(t, DefaultBlockSize, buf.block)
}
func TestNewBufferFromSlice(t *testing.T) {
b := NewBytesPool(256)
buf := NewBufferFromSlice(DefaultBlockSize, b.Get())
require.NotNil(t, buf.buf)
require.Equal(t, 256, cap(buf.buf))
}
func TestNewBufferFromSlice0Size(t *testing.T) {
b := NewBytesPool(256)
buf := NewBufferFromSlice(0, b.Get())
require.NotNil(t, buf.buf)
require.Equal(t, 256, cap(buf.buf))
}
func TestAtomicAlignment(t *testing.T) {
var b Buffer
offset := unsafe.Offsetof(b.head)
require.Equalf(t, uintptr(0), offset%8,
"head requires 64-bit alignment for atomic: offset %d", offset)
offset = unsafe.Offsetof(b.tail)
require.Equalf(t, uintptr(0), offset%8,
"tail requires 64-bit alignment for atomic: offset %d", offset)
}
func TestGetPos(t *testing.T) {
buf := NewBuffer(16, 4)
tail, head := buf.GetPos()
require.Equal(t, int64(0), tail)
require.Equal(t, int64(0), head)
atomic.StoreInt64(&buf.tail, 3)
atomic.StoreInt64(&buf.head, 11)
tail, head = buf.GetPos()
require.Equal(t, int64(3), tail)
require.Equal(t, int64(11), head)
}
func TestGet(t *testing.T) {
buf := NewBuffer(16, 4)
require.Equal(t, make([]byte, 16), buf.Get())
buf.buf[0] = 1
buf.buf[15] = 1
require.Equal(t, []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, buf.Get())
}
func TestSetPos(t *testing.T) {
buf := NewBuffer(16, 4)
require.Equal(t, int64(0), atomic.LoadInt64(&buf.tail))
require.Equal(t, int64(0), atomic.LoadInt64(&buf.head))
buf.SetPos(4, 8)
require.Equal(t, int64(4), atomic.LoadInt64(&buf.tail))
require.Equal(t, int64(8), atomic.LoadInt64(&buf.head))
}
func TestSet(t *testing.T) {
buf := NewBuffer(16, 4)
err := buf.Set([]byte{1, 1, 1, 1}, 17, 19)
require.Error(t, err)
err = buf.Set([]byte{1, 1, 1, 1}, 4, 8)
require.NoError(t, err)
require.Equal(t, []byte{0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, buf.buf)
}
func TestIndex(t *testing.T) {
buf := NewBuffer(1024, 4)
require.Equal(t, 512, buf.Index(512))
require.Equal(t, 0, buf.Index(1024))
require.Equal(t, 6, buf.Index(1030))
require.Equal(t, 6, buf.Index(61446))
}
func TestAwaitFilled(t *testing.T) {
tests := []struct {
tail int64
head int64
n int
await int
desc string
}{
{tail: 0, head: 4, n: 4, await: 1, desc: "OK 0, 4"},
{tail: 8, head: 11, n: 4, await: 1, desc: "OK 8, 11"},
{tail: 102, head: 103, n: 4, await: 3, desc: "OK 102, 103"},
}
for i, tt := range tests {
//fmt.Println(i)
buf := NewBuffer(16, 4)
buf.SetPos(tt.tail, tt.head)
o := make(chan error)
go func() {
o <- buf.awaitFilled(4)
}()
time.Sleep(time.Millisecond)
atomic.AddInt64(&buf.head, int64(tt.await))
buf.wcond.L.Lock()
buf.wcond.Broadcast()
buf.wcond.L.Unlock()
require.NoError(t, <-o, "Unexpected Error [i:%d] %s", i, tt.desc)
}
}
func TestAwaitFilledEnded(t *testing.T) {
buf := NewBuffer(16, 4)
o := make(chan error)
go func() {
o <- buf.awaitFilled(4)
}()
time.Sleep(time.Millisecond)
atomic.StoreUint32(&buf.done, 1)
buf.wcond.L.Lock()
buf.wcond.Broadcast()
buf.wcond.L.Unlock()
require.Error(t, <-o)
}
func TestAwaitEmptyOK(t *testing.T) {
tests := []struct {
tail int64
head int64
await int
desc string
}{
{tail: 0, head: 0, await: 0, desc: "OK 0, 0"},
{tail: 0, head: 5, await: 0, desc: "OK 0, 5"},
{tail: 0, head: 14, await: 3, desc: "OK wrap 0, 14 "},
{tail: 22, head: 35, await: 2, desc: "OK wrap 0, 14 "},
{tail: 15, head: 17, await: 7, desc: "OK 15,2"},
{tail: 0, head: 10, await: 2, desc: "OK 0, 10"},
{tail: 1, head: 15, await: 4, desc: "OK 2, 14"},
}
for i, tt := range tests {
buf := NewBuffer(16, 4)
buf.SetPos(tt.tail, tt.head)
o := make(chan error)
go func() {
o <- buf.awaitEmpty(4)
}()
time.Sleep(time.Millisecond)
atomic.AddInt64(&buf.tail, int64(tt.await))
buf.rcond.L.Lock()
buf.rcond.Broadcast()
buf.rcond.L.Unlock()
require.NoError(t, <-o, "Unexpected Error [i:%d] %s", i, tt.desc)
}
}
func TestAwaitEmptyEnded(t *testing.T) {
buf := NewBuffer(16, 4)
buf.SetPos(1, 15)
o := make(chan error)
go func() {
o <- buf.awaitEmpty(4)
}()
time.Sleep(time.Millisecond)
atomic.StoreUint32(&buf.done, 1)
buf.rcond.L.Lock()
buf.rcond.Broadcast()
buf.rcond.L.Unlock()
require.Error(t, <-o)
}
func TestCheckEmpty(t *testing.T) {
buf := NewBuffer(16, 4)
tests := []struct {
head int64
tail int64
want bool
desc string
}{
{tail: 0, head: 0, want: true, desc: "0, 0 true"},
{tail: 3, head: 4, want: true, desc: "4, 3 true"},
{tail: 15, head: 17, want: true, desc: "15, 17(1) true"},
{tail: 1, head: 30, want: false, desc: "1, 30(14) false"},
{tail: 15, head: 30, want: false, desc: "15, 30(14) false; head has caught up to tail"},
}
for i, tt := range tests {
buf.SetPos(tt.tail, tt.head)
require.Equal(t, tt.want, buf.checkEmpty(4), "Mismatched bool wanted [i:%d] %s", i, tt.desc)
}
}
func TestCheckFilled(t *testing.T) {
buf := NewBuffer(16, 4)
tests := []struct {
head int64
tail int64
want bool
desc string
}{
{tail: 0, head: 0, want: false, desc: "0, 0 false"},
{tail: 0, head: 4, want: true, desc: "0, 4 true"},
{tail: 14, head: 16, want: false, desc: "14,16 false"},
{tail: 14, head: 18, want: true, desc: "14,16 true"},
}
for i, tt := range tests {
buf.SetPos(tt.tail, tt.head)
require.Equal(t, tt.want, buf.checkFilled(4), "Mismatched bool wanted [i:%d] %s", i, tt.desc)
}
}
func TestCommitTail(t *testing.T) {
tests := []struct {
tail int64
head int64
n int
next int64
await int
desc string
}{
{tail: 0, head: 5, n: 4, next: 4, await: 0, desc: "OK 0, 4"},
{tail: 0, head: 5, n: 6, next: 6, await: 1, desc: "OK 0, 5"},
}
for i, tt := range tests {
buf := NewBuffer(16, 4)
buf.SetPos(tt.tail, tt.head)
go func() {
buf.CommitTail(tt.n)
}()
time.Sleep(time.Millisecond)
for j := 0; j < tt.await; j++ {
atomic.AddInt64(&buf.head, 1)
buf.wcond.L.Lock()
buf.wcond.Broadcast()
buf.wcond.L.Unlock()
}
require.Equal(t, tt.next, atomic.LoadInt64(&buf.tail), "Next tail mismatch [i:%d] %s", i, tt.desc)
}
}
/*
func TestCommitTailEnded(t *testing.T) {
buf := NewBuffer(16, 4)
o := make(chan error)
go func() {
o <- buf.CommitTail(5)
}()
time.Sleep(time.Millisecond)
atomic.StoreUint32(&buf.done, 1)
buf.wcond.L.Lock()
buf.wcond.Broadcast()
buf.wcond.L.Unlock()
require.Error(t, <-o)
}
*/
func TestCapDelta(t *testing.T) {
buf := NewBuffer(16, 4)
require.Equal(t, 0, buf.CapDelta())
buf.SetPos(10, 15)
require.Equal(t, 5, buf.CapDelta())
}
func TestStop(t *testing.T) {
buf := NewBuffer(16, 4)
buf.Stop()
require.Equal(t, uint32(1), buf.done)
}