summaryrefslogtreecommitdiff
path: root/testingTools_test.go
diff options
context:
space:
mode:
authorjEzEk <[email protected]>2018-10-30 18:05:48 +0100
committerjEzEk <[email protected]>2018-10-30 18:05:48 +0100
commitab6fdcc639689d45334bb9d3b80e8bf9160a0925 (patch)
treeb52e791e9b111437ac3ee8f8ec2547ed31dbeec0 /testingTools_test.go
parent75c4af6d19421197c0ed0486f41b9c4cf302ce9b (diff)
parent01e3ef92338ac79a57aa6542633770366db1ff52 (diff)
Merge branch 'tests'
Diffstat (limited to 'testingTools_test.go')
-rw-r--r--testingTools_test.go350
1 files changed, 350 insertions, 0 deletions
diff --git a/testingTools_test.go b/testingTools_test.go
new file mode 100644
index 0000000..518b326
--- /dev/null
+++ b/testingTools_test.go
@@ -0,0 +1,350 @@
+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
+ }
+ }
+ }
+}