...

Source file src/github.com/jezek/xgb/testingTools_test.go

Documentation: github.com/jezek/xgb

     1  package xgb
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  )
    13  
    14  func TestLeaks(t *testing.T) {
    15  	lm := leaksMonitor("lm")
    16  	if lgrs := lm.leakingGoroutines(); len(lgrs) != 0 {
    17  		t.Errorf("leakingGoroutines returned %d leaking goroutines, want 0", len(lgrs))
    18  	}
    19  
    20  	done := make(chan struct{})
    21  	wg := &sync.WaitGroup{}
    22  
    23  	wg.Add(1)
    24  	go func() {
    25  		<-done
    26  		wg.Done()
    27  	}()
    28  
    29  	if lgrs := lm.leakingGoroutines(); len(lgrs) != 1 {
    30  		t.Errorf("leakingGoroutines returned %d leaking goroutines, want 1", len(lgrs))
    31  	}
    32  
    33  	wg.Add(1)
    34  	go func() {
    35  		<-done
    36  		wg.Done()
    37  	}()
    38  
    39  	if lgrs := lm.leakingGoroutines(); len(lgrs) != 2 {
    40  		t.Errorf("leakingGoroutines returned %d leaking goroutines, want 2", len(lgrs))
    41  	}
    42  
    43  	close(done)
    44  	wg.Wait()
    45  
    46  	if lgrs := lm.leakingGoroutines(); len(lgrs) != 0 {
    47  		t.Errorf("leakingGoroutines returned %d leaking goroutines, want 0", len(lgrs))
    48  	}
    49  
    50  	lm.checkTesting(t)
    51  	//TODO multiple leak monitors with report ignore tests
    52  }
    53  
    54  func TestDummyNetConn(t *testing.T) {
    55  	ioStatesPairGenerator := func(writeStates, readStates []string) []func() (*dNC, error) {
    56  		writeSetters := map[string]func(*dNC) error{
    57  			"lock":    (*dNC).WriteLock,
    58  			"error":   (*dNC).WriteError,
    59  			"success": (*dNC).WriteSuccess,
    60  		}
    61  		readSetters := map[string]func(*dNC) error{
    62  			"lock":    (*dNC).ReadLock,
    63  			"error":   (*dNC).ReadError,
    64  			"success": (*dNC).ReadSuccess,
    65  		}
    66  
    67  		res := []func() (*dNC, error){}
    68  		for _, writeState := range writeStates {
    69  			writeState, writeSetter := writeState, writeSetters[writeState]
    70  			if writeSetter == nil {
    71  				panic("unknown write state: " + writeState)
    72  				continue
    73  			}
    74  			for _, readState := range readStates {
    75  				readState, readSetter := readState, readSetters[readState]
    76  				if readSetter == nil {
    77  					panic("unknown read state: " + readState)
    78  					continue
    79  				}
    80  				res = append(res, func() (*dNC, error) {
    81  
    82  					// loopback server
    83  					s := newDummyNetConn("w:"+writeState+";r:"+readState, func(b []byte) []byte { return b })
    84  
    85  					if err := readSetter(s); err != nil {
    86  						s.Close()
    87  						return nil, errors.New("set read " + readState + " error: " + err.Error())
    88  					}
    89  
    90  					if err := writeSetter(s); err != nil {
    91  						s.Close()
    92  						return nil, errors.New("set write " + writeState + " error: " + err.Error())
    93  					}
    94  
    95  					return s, nil
    96  				})
    97  			}
    98  		}
    99  		return res
   100  	}
   101  
   102  	timeout := 10 * time.Millisecond
   103  	wantResponse := func(action func(*dNC) error, want, block error) func(*dNC) error {
   104  		return func(s *dNC) error {
   105  			actionResult := make(chan error)
   106  			timedOut := make(chan struct{})
   107  			go func() {
   108  				err := action(s)
   109  				select {
   110  				case <-timedOut:
   111  					if err != block {
   112  						t.Errorf("after unblocking, action result=%v, want %v", err, block)
   113  					}
   114  				case actionResult <- err:
   115  				}
   116  			}()
   117  			select {
   118  			case err := <-actionResult:
   119  				if err != want {
   120  					return errors.New(fmt.Sprintf("action result=%v, want %v", err, want))
   121  				}
   122  			case <-time.After(timeout):
   123  				close(timedOut)
   124  				return errors.New(fmt.Sprintf("action did not respond for %v, result want %v", timeout, want))
   125  			}
   126  			return nil
   127  		}
   128  	}
   129  	wantBlock := func(action func(*dNC) error, unblock error) func(*dNC) error {
   130  		return func(s *dNC) error {
   131  			actionResult := make(chan error)
   132  			timedOut := make(chan struct{})
   133  			go func() {
   134  				err := action(s)
   135  				select {
   136  				case <-timedOut:
   137  					if err != unblock {
   138  						t.Errorf("after unblocking, action result=%v, want %v", err, unblock)
   139  					}
   140  				case actionResult <- err:
   141  				}
   142  			}()
   143  			select {
   144  			case err := <-actionResult:
   145  				return errors.New(fmt.Sprintf("action result=%v, want to be blocked", err))
   146  			case <-time.After(timeout):
   147  				close(timedOut)
   148  			}
   149  			return nil
   150  		}
   151  	}
   152  	write := func(b string) func(*dNC) error {
   153  		return func(s *dNC) error {
   154  			n, err := s.Write([]byte(b))
   155  			if err == nil && n != len(b) {
   156  				return errors.New("Write returned nil error, but not everything was written")
   157  			}
   158  			return err
   159  		}
   160  	}
   161  	read := func(b string) func(*dNC) error {
   162  		return func(s *dNC) error {
   163  			r := make([]byte, len(b))
   164  			n, err := s.Read(r)
   165  			if err == nil {
   166  				if n != len(b) {
   167  					return errors.New("Read returned nil error, but not everything was read")
   168  				}
   169  				if !reflect.DeepEqual(r, []byte(b)) {
   170  					return errors.New("Read=\"" + string(r) + "\", want \"" + string(b) + "\"")
   171  				}
   172  			}
   173  			return err
   174  		}
   175  	}
   176  
   177  	testCases := []struct {
   178  		description string
   179  		servers     []func() (*dNC, error)
   180  		actions     []func(*dNC) error // actions per server
   181  	}{
   182  		{"close,close",
   183  			ioStatesPairGenerator(
   184  				[]string{"lock", "error", "success"},
   185  				[]string{"lock", "error", "success"},
   186  			),
   187  			[]func(*dNC) error{
   188  				wantResponse((*dNC).Close, nil, dNCErrClosed),
   189  				wantResponse((*dNC).Close, dNCErrClosed, dNCErrClosed),
   190  			},
   191  		},
   192  		{"write,close,write",
   193  			ioStatesPairGenerator(
   194  				[]string{"lock"},
   195  				[]string{"lock", "error", "success"},
   196  			),
   197  			[]func(*dNC) error{
   198  				wantBlock(write(""), dNCErrClosed),
   199  				wantResponse((*dNC).Close, nil, dNCErrClosed),
   200  				wantResponse(write(""), dNCErrClosed, dNCErrClosed),
   201  			},
   202  		},
   203  		{"write,close,write",
   204  			ioStatesPairGenerator(
   205  				[]string{"error"},
   206  				[]string{"lock", "error", "success"},
   207  			),
   208  			[]func(*dNC) error{
   209  				wantResponse(write(""), dNCErrWrite, dNCErrClosed),
   210  				wantResponse((*dNC).Close, nil, dNCErrClosed),
   211  				wantResponse(write(""), dNCErrClosed, dNCErrClosed),
   212  			},
   213  		},
   214  		{"write,close,write",
   215  			ioStatesPairGenerator(
   216  				[]string{"success"},
   217  				[]string{"lock", "error", "success"},
   218  			),
   219  			[]func(*dNC) error{
   220  				wantResponse(write(""), nil, dNCErrClosed),
   221  				wantResponse((*dNC).Close, nil, dNCErrClosed),
   222  				wantResponse(write(""), dNCErrClosed, dNCErrClosed),
   223  			},
   224  		},
   225  		{"read,close,read",
   226  			ioStatesPairGenerator(
   227  				[]string{"lock", "error", "success"},
   228  				[]string{"lock", "error", "success"},
   229  			),
   230  			[]func(*dNC) error{
   231  				wantBlock(read(""), io.EOF),
   232  				wantResponse((*dNC).Close, nil, dNCErrClosed),
   233  				wantResponse(read(""), io.EOF, io.EOF),
   234  			},
   235  		},
   236  		{"write,read",
   237  			ioStatesPairGenerator(
   238  				[]string{"lock"},
   239  				[]string{"lock", "error", "success"},
   240  			),
   241  			[]func(*dNC) error{
   242  				wantBlock(write("1"), dNCErrClosed),
   243  				wantBlock(read("1"), io.EOF),
   244  			},
   245  		},
   246  		{"write,read",
   247  			ioStatesPairGenerator(
   248  				[]string{"error"},
   249  				[]string{"lock", "error", "success"},
   250  			),
   251  			[]func(*dNC) error{
   252  				wantResponse(write("1"), dNCErrWrite, dNCErrClosed),
   253  				wantBlock(read("1"), io.EOF),
   254  			},
   255  		},
   256  		{"write,read",
   257  			ioStatesPairGenerator(
   258  				[]string{"success"},
   259  				[]string{"lock"},
   260  			),
   261  			[]func(*dNC) error{
   262  				wantResponse(write("1"), nil, dNCErrClosed),
   263  				wantBlock(read("1"), io.EOF),
   264  			},
   265  		},
   266  		{"write,read",
   267  			ioStatesPairGenerator(
   268  				[]string{"success"},
   269  				[]string{"error"},
   270  			),
   271  			[]func(*dNC) error{
   272  				wantResponse(write("1"), nil, dNCErrClosed),
   273  				wantResponse(read("1"), dNCErrRead, io.EOF),
   274  			},
   275  		},
   276  		{"write,read",
   277  			ioStatesPairGenerator(
   278  				[]string{"success"},
   279  				[]string{"success"},
   280  			),
   281  			[]func(*dNC) error{
   282  				wantResponse(write("1"), nil, dNCErrClosed),
   283  				wantResponse(read("1"), nil, io.EOF),
   284  			},
   285  		},
   286  	}
   287  	for _, tc := range testCases {
   288  		t.Run(tc.description, func(t *testing.T) {
   289  			defer leaksMonitor(tc.description).checkTesting(t)
   290  
   291  			for _, server := range tc.servers {
   292  				s, err := server()
   293  				if err != nil {
   294  					t.Error(err)
   295  					continue
   296  				}
   297  				if s == nil {
   298  					t.Error("nil server in testcase")
   299  					continue
   300  				}
   301  
   302  				t.Run(s.LocalAddr().String(), func(t *testing.T) {
   303  					defer leaksMonitor(s.LocalAddr().String()).checkTesting(t)
   304  					for _, action := range tc.actions {
   305  						if err := action(s); err != nil {
   306  							t.Error(err)
   307  							break
   308  						}
   309  					}
   310  					s.Close()
   311  				})
   312  			}
   313  		})
   314  	}
   315  }
   316  
   317  func TestDummyXServerReplier(t *testing.T) {
   318  	testCases := [][][2][]byte{
   319  		{
   320  			[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}},
   321  			[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}},
   322  			[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}},
   323  			[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}},
   324  			[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}},
   325  			[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}},
   326  			[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}},
   327  			[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}},
   328  			[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}},
   329  			[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}},
   330  			[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}},
   331  			[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}},
   332  			[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}},
   333  			[2][]byte{[]byte("noreply"), nil},
   334  			[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}},
   335  			[2][]byte{[]byte("noreply"), nil},
   336  			[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}},
   337  		},
   338  	}
   339  
   340  	for tci, tc := range testCases {
   341  		replier := newDummyXServerReplier()
   342  		for ai, ioPair := range tc {
   343  			in, want := ioPair[0], ioPair[1]
   344  			if out := replier(in); !bytes.Equal(out, want) {
   345  				t.Errorf("testCase %d, action %d, replier(%s) = %v, want %v", tci, ai, string(in), out, want)
   346  				break
   347  			}
   348  		}
   349  	}
   350  }
   351  

View as plain text