273 lines
4.5 KiB
Go
273 lines
4.5 KiB
Go
package wal
|
|
|
|
import (
|
|
"log"
|
|
"math/rand"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.crumpington.com/public/jldb/lib/errs"
|
|
"git.crumpington.com/public/jldb/lib/testutil"
|
|
)
|
|
|
|
func TestSendRecvHarness(t *testing.T) {
|
|
t.Parallel()
|
|
(&SendRecvTestHarness{}).Run(t)
|
|
}
|
|
|
|
type SendRecvTestHarness struct{}
|
|
|
|
func (h *SendRecvTestHarness) Run(t *testing.T) {
|
|
val := reflect.ValueOf(h)
|
|
typ := val.Type()
|
|
for i := 0; i < typ.NumMethod(); i++ {
|
|
method := typ.Method(i)
|
|
if !strings.HasPrefix(method.Name, "Test") {
|
|
continue
|
|
}
|
|
|
|
t.Run(method.Name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
pDir := t.TempDir()
|
|
sDir := t.TempDir()
|
|
|
|
config := Config{
|
|
SegMinCount: 8,
|
|
SegMaxAgeSec: 1,
|
|
}
|
|
|
|
pWAL, err := Create(pDir, 1, config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer pWAL.Close()
|
|
|
|
sWAL, err := Create(sDir, 1, config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer sWAL.Close()
|
|
|
|
nw := testutil.NewNetwork()
|
|
defer func() {
|
|
nw.CloseServer()
|
|
nw.CloseClient()
|
|
}()
|
|
|
|
val.MethodByName(method.Name).Call([]reflect.Value{
|
|
reflect.ValueOf(t),
|
|
reflect.ValueOf(pWAL),
|
|
reflect.ValueOf(sWAL),
|
|
reflect.ValueOf(nw),
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func (h *SendRecvTestHarness) TestSimple(
|
|
t *testing.T,
|
|
pWAL *WAL,
|
|
sWAL *WAL,
|
|
nw *testutil.Network,
|
|
) {
|
|
wg := sync.WaitGroup{}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := writeRandomWithEOF(pWAL, 5*time.Second); err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
// Send in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
conn := nw.Accept()
|
|
if err := pWAL.Send(conn, 8*time.Second); err != nil {
|
|
log.Printf("Send error: %v", err)
|
|
}
|
|
}()
|
|
|
|
// Recv in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
conn := nw.Dial()
|
|
if err := sWAL.Recv(conn, 8*time.Second); err != nil {
|
|
log.Printf("Recv error: %v", err)
|
|
}
|
|
}()
|
|
|
|
waitForEOF(t, sWAL)
|
|
|
|
nw.CloseServer()
|
|
nw.CloseClient()
|
|
wg.Wait()
|
|
|
|
checkWALsEqual(t, pWAL, sWAL)
|
|
}
|
|
|
|
func (h *SendRecvTestHarness) TestWriteThenRead(
|
|
t *testing.T,
|
|
pWAL *WAL,
|
|
sWAL *WAL,
|
|
nw *testutil.Network,
|
|
) {
|
|
wg := sync.WaitGroup{}
|
|
|
|
if err := writeRandomWithEOF(pWAL, 2*time.Second); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Send in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
conn := nw.Accept()
|
|
if err := pWAL.Send(conn, 8*time.Second); err != nil {
|
|
log.Printf("Send error: %v", err)
|
|
}
|
|
}()
|
|
|
|
// Recv in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
conn := nw.Dial()
|
|
if err := sWAL.Recv(conn, 8*time.Second); err != nil {
|
|
log.Printf("Recv error: %v", err)
|
|
}
|
|
}()
|
|
|
|
waitForEOF(t, sWAL)
|
|
|
|
nw.CloseServer()
|
|
nw.CloseClient()
|
|
wg.Wait()
|
|
|
|
checkWALsEqual(t, pWAL, sWAL)
|
|
}
|
|
|
|
func (h *SendRecvTestHarness) TestNetworkFailures(
|
|
t *testing.T,
|
|
pWAL *WAL,
|
|
sWAL *WAL,
|
|
nw *testutil.Network,
|
|
) {
|
|
recvDone := &atomic.Bool{}
|
|
wg := sync.WaitGroup{}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
writeRandomWithEOF(pWAL, 10*time.Second)
|
|
}()
|
|
|
|
// Send in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
for {
|
|
if recvDone.Load() {
|
|
return
|
|
}
|
|
if conn := nw.Accept(); conn != nil {
|
|
pWAL.Send(conn, 8*time.Second)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Recv in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for !recvDone.Load() {
|
|
if conn := nw.Dial(); conn != nil {
|
|
sWAL.Recv(conn, 8*time.Second)
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Add(1)
|
|
failureCount := 0
|
|
go func() {
|
|
defer wg.Done()
|
|
for {
|
|
if recvDone.Load() {
|
|
return
|
|
}
|
|
time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
|
|
failureCount++
|
|
if rand.Float64() < 0.5 {
|
|
nw.CloseClient()
|
|
} else {
|
|
nw.CloseServer()
|
|
}
|
|
}
|
|
}()
|
|
|
|
waitForEOF(t, sWAL)
|
|
recvDone.Store(true)
|
|
wg.Wait()
|
|
|
|
log.Printf("%d network failures.", failureCount)
|
|
|
|
if failureCount < 10 {
|
|
t.Fatal("Expected more failures.")
|
|
}
|
|
|
|
checkWALsEqual(t, pWAL, sWAL)
|
|
}
|
|
|
|
func (h *SendRecvTestHarness) TestSenderClose(
|
|
t *testing.T,
|
|
pWAL *WAL,
|
|
sWAL *WAL,
|
|
nw *testutil.Network,
|
|
) {
|
|
wg := sync.WaitGroup{}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := writeRandomWithEOF(pWAL, 5*time.Second); !errs.Closed.Is(err) {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
// Close primary after some time.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
time.Sleep(time.Second)
|
|
pWAL.Close()
|
|
}()
|
|
|
|
// Send in the background.
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
conn := nw.Accept()
|
|
if err := pWAL.Send(conn, 8*time.Second); err != nil {
|
|
log.Printf("Send error: %v", err)
|
|
}
|
|
}()
|
|
|
|
conn := nw.Dial()
|
|
if err := sWAL.Recv(conn, 8*time.Second); !errs.Closed.Is(err) {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
nw.CloseServer()
|
|
nw.CloseClient()
|
|
wg.Wait()
|
|
}
|