318 lines
7.3 KiB
Go
318 lines
7.3 KiB
Go
|
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)
|
||
|
}
|