Files
mochi-mqtt/circ/buffer_test.go
2019-10-27 11:00:50 +00:00

179 lines
4.0 KiB
Go

package circ
import (
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestNewBuffer(t *testing.T) {
var size int64 = 16
var block int64 = 4
buf := newBuffer(size, block)
require.NotNil(t, buf.buf)
require.NotNil(t, buf.rcond)
require.NotNil(t, buf.wcond)
require.Equal(t, size, int64(len(buf.buf)))
require.Equal(t, size, buf.size)
require.Equal(t, block, buf.block)
}
func TestSet(t *testing.T) {
buf := newBuffer(8, 4)
require.Equal(t, make([]byte, 4), buf.buf[0:4])
p := []byte{'1', '2', '3', '4'}
buf.Set(p, 0, 4)
require.Equal(t, p, buf.buf[0:4])
buf.Set(p, 2, 6)
require.Equal(t, p, buf.buf[2:6])
}
func TestGetPos(t *testing.T) {
buf := newBuffer(8, 4)
buf.tail, buf.head = 1, 3
tail, head := buf.GetPos()
require.Equal(t, int64(1), tail)
require.Equal(t, int64(3), head)
}
func TestSetPos(t *testing.T) {
buf := newBuffer(8, 4)
buf.SetPos(1, 3)
require.Equal(t, int64(3), buf.head)
require.Equal(t, int64(1), buf.tail)
}
func TestCommitHead(t *testing.T) {
//buf := newBuffer(16, 4)
}
func TestAwaitCapacity(t *testing.T) {
tests := []struct {
tail int64
head int64
await int
desc string
}{
{0, 0, 0, "OK 4, 0"},
{6, 6, 0, "OK 6, 10"},
{12, 16, 0, "OK 16, 4"},
{16, 12, 0, "OK 16 16"},
{3, 6, 0, "OK 3, 10"},
{12, 0, 0, "OK 16, 0"},
{1, 16, 4, "next is more than tail, wait for tail incr"},
{7, 5, 2, "tail is great than head, wrapped and caught up with tail, wait for tail incr"},
}
buf := newBuffer(16, 4)
for i, tt := range tests {
buf.tail, buf.head = tt.tail, tt.head
o := make(chan []interface{})
var start int64 = -1
var err error
go func() {
start, err = buf.awaitCapacity(4)
o <- []interface{}{start, err}
}()
time.Sleep(time.Millisecond)
for j := 0; j < tt.await; j++ {
atomic.AddInt64(&buf.tail, 1)
buf.rcond.L.Lock()
buf.rcond.Broadcast()
buf.rcond.L.Unlock()
}
time.Sleep(time.Millisecond) // wait for await capacity to actually exit
if start == -1 {
atomic.StoreInt64(&buf.done, 1)
buf.rcond.L.Lock()
buf.rcond.Broadcast()
buf.rcond.L.Unlock()
}
done := <-o
require.Equal(t, tt.head, done[0].(int64), "Head-Start mismatch [i:%d] %s", i, tt.desc)
require.Nil(t, done[1], "Unexpected Error [i:%d] %s", i, tt.desc)
}
}
func TestAwaitFilled(t *testing.T) {
tests := []struct {
tail int64
head int64
next int64
await int
desc string
}{
{0, 4, 4, 0, "OK 0, 4"},
{6, 10, 10, 0, "OK 6, 10"},
{14, 2, 2, 0, "OK 14, 2 wrapped"},
{6, 8, 10, 2, "Wait 6, 8"},
{14, 1, 4, 3, "Wait 14, 1 wrapped"},
}
buf := newBuffer(16, 4)
for i, tt := range tests {
buf.tail, buf.head = tt.tail, tt.head
o := make(chan []interface{})
var start int64 = -1
var err error
go func() {
start, err = buf.awaitFilled(4)
o <- []interface{}{start, err}
}()
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()
}
done := <-o
require.Equal(t, tt.tail, done[0].(int64), "tail start [i:%d] %s", i, tt.desc)
require.Equal(t, tt.next, buf.head, "Head-next mismatch [i:%d] %s", i, tt.desc)
require.Nil(t, done[1], "Unexpected Error [i:%d] %s", i, tt.desc)
}
}
func TestCommitTail(t *testing.T) {
tests := []struct {
tail int64
head int64
next int64
await int
desc string
}{
{0, 5, 4, 0, "OK 0, 4"},
{6, 10, 10, 0, "OK 6, 10"},
{14, 2, 2, 0, "OK 14, 2 wrapped"},
{6, 8, 10, 2, "Wait 6, 8"},
{14, 1, 2, 2, "Wait 14, 1 wrapped"},
}
buf := newBuffer(16, 4)
for i, tt := range tests {
buf.tail, buf.head = tt.tail, tt.head
o := make(chan error)
go func() {
o <- buf.CommitTail(4)
}()
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.NoError(t, <-o, "Unexpected Error [i:%d] %s", i, tt.desc)
require.Equal(t, tt.next, buf.tail, "Tail-next mismatch [i:%d] %s", i, tt.desc)
}
}