351 lines
10 KiB
Go
351 lines
10 KiB
Go
package xgb
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLeaks(t *testing.T) {
|
|
lm := leaksMonitor("lm")
|
|
if lgrs := lm.leakingGoroutines(); len(lgrs) != 0 {
|
|
t.Errorf("leakingGoroutines returned %d leaking goroutines, want 0", len(lgrs))
|
|
}
|
|
|
|
done := make(chan struct{})
|
|
wg := &sync.WaitGroup{}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
<-done
|
|
wg.Done()
|
|
}()
|
|
|
|
if lgrs := lm.leakingGoroutines(); len(lgrs) != 1 {
|
|
t.Errorf("leakingGoroutines returned %d leaking goroutines, want 1", len(lgrs))
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
<-done
|
|
wg.Done()
|
|
}()
|
|
|
|
if lgrs := lm.leakingGoroutines(); len(lgrs) != 2 {
|
|
t.Errorf("leakingGoroutines returned %d leaking goroutines, want 2", len(lgrs))
|
|
}
|
|
|
|
close(done)
|
|
wg.Wait()
|
|
|
|
if lgrs := lm.leakingGoroutines(); len(lgrs) != 0 {
|
|
t.Errorf("leakingGoroutines returned %d leaking goroutines, want 0", len(lgrs))
|
|
}
|
|
|
|
lm.checkTesting(t)
|
|
//TODO multiple leak monitors with report ignore tests
|
|
}
|
|
|
|
func TestDummyNetConn(t *testing.T) {
|
|
ioStatesPairGenerator := func(writeStates, readStates []string) []func() (*dNC, error) {
|
|
writeSetters := map[string]func(*dNC) error{
|
|
"lock": (*dNC).WriteLock,
|
|
"error": (*dNC).WriteError,
|
|
"success": (*dNC).WriteSuccess,
|
|
}
|
|
readSetters := map[string]func(*dNC) error{
|
|
"lock": (*dNC).ReadLock,
|
|
"error": (*dNC).ReadError,
|
|
"success": (*dNC).ReadSuccess,
|
|
}
|
|
|
|
res := []func() (*dNC, error){}
|
|
for _, writeState := range writeStates {
|
|
writeState, writeSetter := writeState, writeSetters[writeState]
|
|
if writeSetter == nil {
|
|
panic("unknown write state: " + writeState)
|
|
continue
|
|
}
|
|
for _, readState := range readStates {
|
|
readState, readSetter := readState, readSetters[readState]
|
|
if readSetter == nil {
|
|
panic("unknown read state: " + readState)
|
|
continue
|
|
}
|
|
res = append(res, func() (*dNC, error) {
|
|
|
|
// loopback server
|
|
s := newDummyNetConn("w:"+writeState+";r:"+readState, func(b []byte) []byte { return b })
|
|
|
|
if err := readSetter(s); err != nil {
|
|
s.Close()
|
|
return nil, errors.New("set read " + readState + " error: " + err.Error())
|
|
}
|
|
|
|
if err := writeSetter(s); err != nil {
|
|
s.Close()
|
|
return nil, errors.New("set write " + writeState + " error: " + err.Error())
|
|
}
|
|
|
|
return s, nil
|
|
})
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
timeout := 10 * time.Millisecond
|
|
wantResponse := func(action func(*dNC) error, want, block error) func(*dNC) error {
|
|
return func(s *dNC) error {
|
|
actionResult := make(chan error)
|
|
timedOut := make(chan struct{})
|
|
go func() {
|
|
err := action(s)
|
|
select {
|
|
case <-timedOut:
|
|
if err != block {
|
|
t.Errorf("after unblocking, action result=%v, want %v", err, block)
|
|
}
|
|
case actionResult <- err:
|
|
}
|
|
}()
|
|
select {
|
|
case err := <-actionResult:
|
|
if err != want {
|
|
return errors.New(fmt.Sprintf("action result=%v, want %v", err, want))
|
|
}
|
|
case <-time.After(timeout):
|
|
close(timedOut)
|
|
return errors.New(fmt.Sprintf("action did not respond for %v, result want %v", timeout, want))
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
wantBlock := func(action func(*dNC) error, unblock error) func(*dNC) error {
|
|
return func(s *dNC) error {
|
|
actionResult := make(chan error)
|
|
timedOut := make(chan struct{})
|
|
go func() {
|
|
err := action(s)
|
|
select {
|
|
case <-timedOut:
|
|
if err != unblock {
|
|
t.Errorf("after unblocking, action result=%v, want %v", err, unblock)
|
|
}
|
|
case actionResult <- err:
|
|
}
|
|
}()
|
|
select {
|
|
case err := <-actionResult:
|
|
return errors.New(fmt.Sprintf("action result=%v, want to be blocked", err))
|
|
case <-time.After(timeout):
|
|
close(timedOut)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
write := func(b string) func(*dNC) error {
|
|
return func(s *dNC) error {
|
|
n, err := s.Write([]byte(b))
|
|
if err == nil && n != len(b) {
|
|
return errors.New("Write returned nil error, but not everything was written")
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
read := func(b string) func(*dNC) error {
|
|
return func(s *dNC) error {
|
|
r := make([]byte, len(b))
|
|
n, err := s.Read(r)
|
|
if err == nil {
|
|
if n != len(b) {
|
|
return errors.New("Read returned nil error, but not everything was read")
|
|
}
|
|
if !reflect.DeepEqual(r, []byte(b)) {
|
|
return errors.New("Read=\"" + string(r) + "\", want \"" + string(b) + "\"")
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
|
|
testCases := []struct {
|
|
description string
|
|
servers []func() (*dNC, error)
|
|
actions []func(*dNC) error // actions per server
|
|
}{
|
|
{"close,close",
|
|
ioStatesPairGenerator(
|
|
[]string{"lock", "error", "success"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse((*dNC).Close, nil, dNCErrClosed),
|
|
wantResponse((*dNC).Close, dNCErrClosed, dNCErrClosed),
|
|
},
|
|
},
|
|
{"write,close,write",
|
|
ioStatesPairGenerator(
|
|
[]string{"lock"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantBlock(write(""), dNCErrClosed),
|
|
wantResponse((*dNC).Close, nil, dNCErrClosed),
|
|
wantResponse(write(""), dNCErrClosed, dNCErrClosed),
|
|
},
|
|
},
|
|
{"write,close,write",
|
|
ioStatesPairGenerator(
|
|
[]string{"error"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse(write(""), dNCErrWrite, dNCErrClosed),
|
|
wantResponse((*dNC).Close, nil, dNCErrClosed),
|
|
wantResponse(write(""), dNCErrClosed, dNCErrClosed),
|
|
},
|
|
},
|
|
{"write,close,write",
|
|
ioStatesPairGenerator(
|
|
[]string{"success"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse(write(""), nil, dNCErrClosed),
|
|
wantResponse((*dNC).Close, nil, dNCErrClosed),
|
|
wantResponse(write(""), dNCErrClosed, dNCErrClosed),
|
|
},
|
|
},
|
|
{"read,close,read",
|
|
ioStatesPairGenerator(
|
|
[]string{"lock", "error", "success"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantBlock(read(""), io.EOF),
|
|
wantResponse((*dNC).Close, nil, dNCErrClosed),
|
|
wantResponse(read(""), io.EOF, io.EOF),
|
|
},
|
|
},
|
|
{"write,read",
|
|
ioStatesPairGenerator(
|
|
[]string{"lock"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantBlock(write("1"), dNCErrClosed),
|
|
wantBlock(read("1"), io.EOF),
|
|
},
|
|
},
|
|
{"write,read",
|
|
ioStatesPairGenerator(
|
|
[]string{"error"},
|
|
[]string{"lock", "error", "success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse(write("1"), dNCErrWrite, dNCErrClosed),
|
|
wantBlock(read("1"), io.EOF),
|
|
},
|
|
},
|
|
{"write,read",
|
|
ioStatesPairGenerator(
|
|
[]string{"success"},
|
|
[]string{"lock"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse(write("1"), nil, dNCErrClosed),
|
|
wantBlock(read("1"), io.EOF),
|
|
},
|
|
},
|
|
{"write,read",
|
|
ioStatesPairGenerator(
|
|
[]string{"success"},
|
|
[]string{"error"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse(write("1"), nil, dNCErrClosed),
|
|
wantResponse(read("1"), dNCErrRead, io.EOF),
|
|
},
|
|
},
|
|
{"write,read",
|
|
ioStatesPairGenerator(
|
|
[]string{"success"},
|
|
[]string{"success"},
|
|
),
|
|
[]func(*dNC) error{
|
|
wantResponse(write("1"), nil, dNCErrClosed),
|
|
wantResponse(read("1"), nil, io.EOF),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
defer leaksMonitor(tc.description).checkTesting(t)
|
|
|
|
for _, server := range tc.servers {
|
|
s, err := server()
|
|
if err != nil {
|
|
t.Error(err)
|
|
continue
|
|
}
|
|
if s == nil {
|
|
t.Error("nil server in testcase")
|
|
continue
|
|
}
|
|
|
|
t.Run(s.LocalAddr().String(), func(t *testing.T) {
|
|
defer leaksMonitor(s.LocalAddr().String()).checkTesting(t)
|
|
for _, action := range tc.actions {
|
|
if err := action(s); err != nil {
|
|
t.Error(err)
|
|
break
|
|
}
|
|
}
|
|
s.Close()
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDummyXServerReplier(t *testing.T) {
|
|
testCases := [][][2][]byte{
|
|
{
|
|
[2][]byte{[]byte("reply"), []byte{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("eply"), []byte{1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("ply"), []byte{1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("event"), []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("ly"), []byte{1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("y"), []byte{1, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte(""), []byte{1, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("event"), []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("reply"), []byte{1, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("error"), []byte{0, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("ply"), []byte{1, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("event"), []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("ly"), []byte{1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("noreply"), nil},
|
|
[2][]byte{[]byte("error"), []byte{0, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
[2][]byte{[]byte("noreply"), nil},
|
|
[2][]byte{[]byte(""), []byte{1, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
|
},
|
|
}
|
|
|
|
for tci, tc := range testCases {
|
|
replier := newDummyXServerReplier()
|
|
for ai, ioPair := range tc {
|
|
in, want := ioPair[0], ioPair[1]
|
|
if out := replier(in); !bytes.Equal(out, want) {
|
|
t.Errorf("testCase %d, action %d, replier(%s) = %v, want %v", tci, ai, string(in), out, want)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|