...

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

Documentation: github.com/jezek/xgb

     1  package xgb
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"net"
     8  	"regexp"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  // Leaks monitor
    17  
    18  type goroutine struct {
    19  	id    int
    20  	name  string
    21  	stack []byte
    22  }
    23  
    24  type leaks struct {
    25  	name       string
    26  	goroutines map[int]goroutine
    27  	report     []*leaks
    28  }
    29  
    30  func leaksMonitor(name string, monitors ...*leaks) *leaks {
    31  	return &leaks{
    32  		name,
    33  		leaks{}.collectGoroutines(),
    34  		monitors,
    35  	}
    36  }
    37  
    38  // ispired by https://golang.org/src/runtime/debug/stack.go?s=587:606#L21
    39  // stack returns a formatted stack trace of all goroutines.
    40  // It calls runtime.Stack with a large enough buffer to capture the entire trace.
    41  func (_ leaks) stack() []byte {
    42  	buf := make([]byte, 1024)
    43  	for {
    44  		n := runtime.Stack(buf, true)
    45  		if n < len(buf) {
    46  			return buf[:n]
    47  		}
    48  		buf = make([]byte, 2*len(buf))
    49  	}
    50  }
    51  
    52  func (l leaks) collectGoroutines() map[int]goroutine {
    53  	res := make(map[int]goroutine)
    54  	stacks := bytes.Split(l.stack(), []byte{'\n', '\n'})
    55  
    56  	regexpId := regexp.MustCompile(`^\s*goroutine\s*(\d+)`)
    57  	for _, st := range stacks {
    58  		lines := bytes.Split(st, []byte{'\n'})
    59  		if len(lines) < 2 {
    60  			panic("routine stach has less tnan two lines: " + string(st))
    61  		}
    62  
    63  		idMatches := regexpId.FindSubmatch(lines[0])
    64  		if len(idMatches) < 2 {
    65  			panic("no id found in goroutine stack's first line: " + string(lines[0]))
    66  		}
    67  		id, err := strconv.Atoi(string(idMatches[1]))
    68  		if err != nil {
    69  			panic("converting goroutine id to number error: " + err.Error())
    70  		}
    71  		if _, ok := res[id]; ok {
    72  			panic("2 goroutines with same id: " + strconv.Itoa(id))
    73  		}
    74  		name := strings.TrimSpace(string(lines[1]))
    75  
    76  		//filter out our stack routine
    77  		if strings.Contains(name, "xgb.leaks.stack") {
    78  			continue
    79  		}
    80  
    81  		res[id] = goroutine{id, name, st}
    82  	}
    83  	return res
    84  }
    85  
    86  func (l leaks) leakingGoroutines() []goroutine {
    87  	goroutines := l.collectGoroutines()
    88  	res := []goroutine{}
    89  	for id, gr := range goroutines {
    90  		if _, ok := l.goroutines[id]; ok {
    91  			continue
    92  		}
    93  		res = append(res, gr)
    94  	}
    95  	return res
    96  }
    97  func (l leaks) checkTesting(t *testing.T) {
    98  	if len(l.leakingGoroutines()) == 0 {
    99  		return
   100  	}
   101  	leakTimeout := 10 * time.Millisecond
   102  	time.Sleep(leakTimeout)
   103  	//t.Logf("possible goroutine leakage, waiting %v", leakTimeout)
   104  	grs := l.leakingGoroutines()
   105  	for _, gr := range grs {
   106  		t.Errorf("%s: %s is leaking", l.name, gr.name)
   107  		//t.Errorf("%s: %s is leaking\n%v", l.name, gr.name, string(gr.stack))
   108  	}
   109  	for _, rl := range l.report {
   110  		rl.ignoreLeak(grs...)
   111  	}
   112  }
   113  func (l *leaks) ignoreLeak(grs ...goroutine) {
   114  	for _, gr := range grs {
   115  		l.goroutines[gr.id] = gr
   116  	}
   117  }
   118  
   119  // dummy net.Conn
   120  
   121  type dAddr struct {
   122  	s string
   123  }
   124  
   125  func (_ dAddr) Network() string { return "dummy" }
   126  func (a dAddr) String() string  { return a.s }
   127  
   128  var (
   129  	dNCErrNotImplemented = errors.New("command not implemented")
   130  	dNCErrClosed         = errors.New("server closed")
   131  	dNCErrWrite          = errors.New("server write failed")
   132  	dNCErrRead           = errors.New("server read failed")
   133  	dNCErrResponse       = errors.New("server response error")
   134  )
   135  
   136  type dNCIoResult struct {
   137  	n   int
   138  	err error
   139  }
   140  type dNCIo struct {
   141  	b      []byte
   142  	result chan dNCIoResult
   143  }
   144  
   145  type dNCCWriteLock struct{}
   146  type dNCCWriteUnlock struct{}
   147  type dNCCWriteError struct{}
   148  type dNCCWriteSuccess struct{}
   149  type dNCCReadLock struct{}
   150  type dNCCReadUnlock struct{}
   151  type dNCCReadError struct{}
   152  type dNCCReadSuccess struct{}
   153  
   154  // dummy net.Conn interface. Needs to be constructed via newDummyNetConn([...]) function.
   155  type dNC struct {
   156  	reply   func([]byte) []byte
   157  	addr    dAddr
   158  	in, out chan dNCIo
   159  	control chan interface{}
   160  	done    chan struct{}
   161  }
   162  
   163  // Results running dummy server, satisfying net.Conn interface for test purposes.
   164  // 'name' parameter will be returned via (*dNC).Local/RemoteAddr().String()
   165  // 'reply' parameter function will be runned only on successful (*dNC).Write(b) with 'b' as parameter to 'reply'. The result will be stored in internal buffer and can be retrieved later via (*dNC).Read([...]) method.
   166  // It is users responsibility to stop and clean up resources with (*dNC).Close, if not needed anymore.
   167  // By default, the (*dNC).Write([...]) and (*dNC).Read([...]) methods are unlocked and will not result in error.
   168  //TODO make (*dNC).SetDeadline, (*dNC).SetReadDeadline, (*dNC).SetWriteDeadline work proprely.
   169  func newDummyNetConn(name string, reply func([]byte) []byte) *dNC {
   170  
   171  	s := &dNC{
   172  		reply,
   173  		dAddr{name},
   174  		make(chan dNCIo), make(chan dNCIo),
   175  		make(chan interface{}),
   176  		make(chan struct{}),
   177  	}
   178  
   179  	in, out := s.in, chan dNCIo(nil)
   180  	buf := &bytes.Buffer{}
   181  	errorRead, errorWrite := false, false
   182  	lockRead := false
   183  
   184  	go func() {
   185  		defer close(s.done)
   186  		for {
   187  			select {
   188  			case dxsio := <-in:
   189  				if errorWrite {
   190  					dxsio.result <- dNCIoResult{0, dNCErrWrite}
   191  					break
   192  				}
   193  
   194  				response := s.reply(dxsio.b)
   195  
   196  				buf.Write(response)
   197  				dxsio.result <- dNCIoResult{len(dxsio.b), nil}
   198  
   199  				if !lockRead && buf.Len() > 0 && out == nil {
   200  					out = s.out
   201  				}
   202  			case dxsio := <-out:
   203  				if errorRead {
   204  					dxsio.result <- dNCIoResult{0, dNCErrRead}
   205  					break
   206  				}
   207  
   208  				n, err := buf.Read(dxsio.b)
   209  				dxsio.result <- dNCIoResult{n, err}
   210  
   211  				if buf.Len() == 0 {
   212  					out = nil
   213  				}
   214  			case ci := <-s.control:
   215  				if ci == nil {
   216  					return
   217  				}
   218  				switch ci.(type) {
   219  				case dNCCWriteLock:
   220  					in = nil
   221  				case dNCCWriteUnlock:
   222  					in = s.in
   223  				case dNCCWriteError:
   224  					errorWrite = true
   225  				case dNCCWriteSuccess:
   226  					errorWrite = false
   227  				case dNCCReadLock:
   228  					out = nil
   229  					lockRead = true
   230  				case dNCCReadUnlock:
   231  					lockRead = false
   232  					if buf.Len() > 0 && out == nil {
   233  						out = s.out
   234  					}
   235  				case dNCCReadError:
   236  					errorRead = true
   237  				case dNCCReadSuccess:
   238  					errorRead = false
   239  				default:
   240  				}
   241  			}
   242  		}
   243  	}()
   244  	return s
   245  }
   246  
   247  // Shuts down dummy net.Conn server. Every blocking or future method calls will do nothing and result in error.
   248  // Result will be dNCErrClosed if server was allready closed.
   249  // Server can not be unclosed.
   250  func (s *dNC) Close() error {
   251  	select {
   252  	case s.control <- nil:
   253  		<-s.done
   254  		return nil
   255  	case <-s.done:
   256  	}
   257  	return dNCErrClosed
   258  }
   259  
   260  // Performs a write action to server.
   261  // If not locked by (*dNC).WriteLock, it results in error or success. If locked, this method will block until unlocked, or closed.
   262  //
   263  // This method can be set to result in error or success, via (*dNC).WriteError() or (*dNC).WriteSuccess() methods.
   264  //
   265  // If setted to result in error, the 'reply' function will NOT be called and internal buffer will NOT increasethe.
   266  // Result will be (0, dNCErrWrite).
   267  //
   268  // If setted to result in success, the 'reply' function will be called and its result will be writen to internal buffer.
   269  // If there is something in the internal buffer, the (*dNC).Read([...]) will be unblocked (if not previously locked with (*dNC).ReadLock).
   270  // Result will be (len(b), nil)
   271  //
   272  // If server was closed previously, result will be (0, dNCErrClosed).
   273  func (s *dNC) Write(b []byte) (int, error) {
   274  	resChan := make(chan dNCIoResult)
   275  	select {
   276  	case s.in <- dNCIo{b, resChan}:
   277  		res := <-resChan
   278  		return res.n, res.err
   279  	case <-s.done:
   280  	}
   281  	return 0, dNCErrClosed
   282  }
   283  
   284  // Performs a read action from server.
   285  // If locked by (*dNC).ReadLock(), this method will block until unlocked with (*dNC).ReadUnlock(), or server closes.
   286  //
   287  // If not locked, this method can be setted to result imidiatly in error, will block if internal buffer is empty or will perform an read operation from internal buffer.
   288  //
   289  // If setted to result in error via (*dNC).ReadError(), the result will be (0, dNCErrWrite).
   290  //
   291  // If not locked and not setted to result in error via (*dNC).ReadSuccess(), this method will block until internall buffer is not empty, than it returns the result of the buffer read operation via (*bytes.Buffer).Read([...]).
   292  // If the internal buffer is empty after this method, all follwing (*dNC).Read([...]), requests will block until internall buffer is filled after successful write requests.
   293  //
   294  // If server was closed previously, result will be (0, io.EOF).
   295  func (s *dNC) Read(b []byte) (int, error) {
   296  	resChan := make(chan dNCIoResult)
   297  	select {
   298  	case s.out <- dNCIo{b, resChan}:
   299  		res := <-resChan
   300  		return res.n, res.err
   301  	case <-s.done:
   302  	}
   303  	return 0, io.EOF
   304  }
   305  func (s *dNC) LocalAddr() net.Addr                { return s.addr }
   306  func (s *dNC) RemoteAddr() net.Addr               { return s.addr }
   307  func (s *dNC) SetDeadline(t time.Time) error      { return dNCErrNotImplemented }
   308  func (s *dNC) SetReadDeadline(t time.Time) error  { return dNCErrNotImplemented }
   309  func (s *dNC) SetWriteDeadline(t time.Time) error { return dNCErrNotImplemented }
   310  
   311  func (s *dNC) Control(i interface{}) error {
   312  	select {
   313  	case s.control <- i:
   314  		return nil
   315  	case <-s.done:
   316  	}
   317  	return dNCErrClosed
   318  }
   319  
   320  // Locks writing. All write requests will be blocked until write is unlocked with (*dNC).WriteUnlock, or server closes.
   321  func (s *dNC) WriteLock() error {
   322  	return s.Control(dNCCWriteLock{})
   323  }
   324  
   325  // Unlocks writing. All blocked write requests until now will be accepted.
   326  func (s *dNC) WriteUnlock() error {
   327  	return s.Control(dNCCWriteUnlock{})
   328  }
   329  
   330  // Unlocks writing and makes (*dNC).Write to result (0, dNCErrWrite).
   331  func (s *dNC) WriteError() error {
   332  	if err := s.WriteUnlock(); err != nil {
   333  		return err
   334  	}
   335  	return s.Control(dNCCWriteError{})
   336  }
   337  
   338  // Unlocks writing and makes (*dNC).Write([...]) not result in error. See (*dNC).Write for details.
   339  func (s *dNC) WriteSuccess() error {
   340  	if err := s.WriteUnlock(); err != nil {
   341  		return err
   342  	}
   343  	return s.Control(dNCCWriteSuccess{})
   344  }
   345  
   346  // Locks reading. All read requests will be blocked until read is unlocked with (*dNC).ReadUnlock, or server closes.
   347  // (*dNC).Read([...]) wil block even after successful write.
   348  func (s *dNC) ReadLock() error {
   349  	return s.Control(dNCCReadLock{})
   350  }
   351  
   352  // Unlocks reading. If the internall buffer is not empty, next read will not block.
   353  func (s *dNC) ReadUnlock() error {
   354  	return s.Control(dNCCReadUnlock{})
   355  }
   356  
   357  // Unlocks read and makes every blocked and following (*dNC).Read([...]) imidiatly result in error. See (*dNC).Read for details.
   358  func (s *dNC) ReadError() error {
   359  	if err := s.ReadUnlock(); err != nil {
   360  		return err
   361  	}
   362  	return s.Control(dNCCReadError{})
   363  }
   364  
   365  // Unlocks read and makes every blocked and following (*dNC).Read([...]) requests be handled, if according to internal buffer. See (*dNC).Read for details.
   366  func (s *dNC) ReadSuccess() error {
   367  	if err := s.ReadUnlock(); err != nil {
   368  		return err
   369  	}
   370  	return s.Control(dNCCReadSuccess{})
   371  }
   372  
   373  // dummy X server replier for dummy net.Conn
   374  
   375  type dXSEvent struct{}
   376  
   377  func (_ dXSEvent) Bytes() []byte  { return nil }
   378  func (_ dXSEvent) String() string { return "dummy X server event" }
   379  
   380  type dXSError struct {
   381  	seqId uint16
   382  }
   383  
   384  func (e dXSError) SequenceId() uint16 { return e.seqId }
   385  func (_ dXSError) BadId() uint32      { return 0 }
   386  func (_ dXSError) Error() string      { return "dummy X server error reply" }
   387  
   388  func newDummyXServerReplier() func([]byte) []byte {
   389  	// register xgb error & event replies
   390  	NewErrorFuncs[255] = func(buf []byte) Error {
   391  		return dXSError{Get16(buf[2:])}
   392  	}
   393  	NewEventFuncs[128&127] = func(buf []byte) Event {
   394  		return dXSEvent{}
   395  	}
   396  
   397  	// sequence number generator
   398  	seqId := uint16(1)
   399  	incrementSequenceId := func() {
   400  		// this has to be the same algorithm as in (*Conn).generateSeqIds
   401  		if seqId == uint16((1<<16)-1) {
   402  			seqId = 0
   403  		} else {
   404  			seqId++
   405  		}
   406  	}
   407  	return func(request []byte) []byte {
   408  		res := make([]byte, 32)
   409  		switch string(request) {
   410  		case "event":
   411  			res[0] = 128
   412  			return res
   413  		case "error":
   414  			res[0] = 0   // error
   415  			res[1] = 255 // error function
   416  		default:
   417  			res[0] = 1 // reply
   418  		}
   419  		Put16(res[2:], seqId) // sequence number
   420  		incrementSequenceId()
   421  		if string(request) == "noreply" {
   422  			return nil
   423  		}
   424  		return res
   425  	}
   426  }
   427  

View as plain text