nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/test/common_test.go (about)

     1  // Copyright 2018 The Mangos Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain a copy of the license at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package test contains a framework for testing.
    16  package test
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/binary"
    21  	"fmt"
    22  	"strings"
    23  	"sync"
    24  	"sync/atomic"
    25  	"testing"
    26  	"time"
    27  
    28  	"nanomsg.org/go/mangos/v2"
    29  	"nanomsg.org/go/mangos/v2/transport/all"
    30  )
    31  
    32  var cliCfg, _ = NewTLSConfig(false)
    33  var srvCfg, _ = NewTLSConfig(true)
    34  
    35  // T is a structure that sub-tests can inherit from.
    36  type T struct {
    37  	t       *testing.T
    38  	debug   bool
    39  	ID      int
    40  	addr    string
    41  	MsgSize int // size of messages
    42  	txdelay time.Duration
    43  	WantTx  int32
    44  	WantRx  int32
    45  	Synch   bool
    46  	NReply  int
    47  	numtx   int32
    48  	numrx   int32
    49  	timeout time.Duration
    50  	Sock    mangos.Socket
    51  	rdone   bool
    52  	rdoneq  chan struct{}
    53  	sdone   bool
    54  	sdoneq  chan struct{}
    55  	readyq  chan struct{}
    56  	Server  bool
    57  	sync.Mutex
    58  }
    59  
    60  // TestCase represents a single test case.
    61  type TestCase interface {
    62  	Init(t *testing.T, addr string) bool
    63  	NewMessage() *mangos.Message
    64  	SendMsg(*mangos.Message) error
    65  	RecvMsg() (*mangos.Message, error)
    66  	SendHook(*mangos.Message) bool
    67  	RecvHook(*mangos.Message) bool
    68  	IsServer() bool
    69  	Logf(string, ...interface{})
    70  	Errorf(string, ...interface{})
    71  	Debugf(string, ...interface{})
    72  	WantSend() int32
    73  	BumpSend()
    74  	GetSend() int32
    75  	WantRecv() int32
    76  	BumpRecv()
    77  	GetRecv() int32
    78  	GetID() int
    79  	Dial() bool
    80  	Listen() bool
    81  	WaitRecv() bool
    82  	RecvDone()
    83  	WaitSend() bool
    84  	SendDone()
    85  	Close()
    86  	SendDelay() time.Duration
    87  	Ready() bool
    88  	SendStart() bool
    89  	RecvStart() bool
    90  	WaitReady() bool
    91  	WantSendStart() bool
    92  	WantRecvStart() bool
    93  	SetReady()
    94  	IsSynch() bool
    95  	NumReply() int
    96  }
    97  
    98  func (c *T) Init(t *testing.T, addr string) bool {
    99  	time.Sleep(100 * time.Millisecond)
   100  	// Initial defaults
   101  	c.Lock()
   102  	defer c.Unlock()
   103  	c.t = t
   104  	c.addr = addr
   105  	c.numrx = 0 // Reset
   106  	c.numtx = 0 // Reset
   107  	c.sdoneq = make(chan struct{})
   108  	c.rdoneq = make(chan struct{})
   109  	c.readyq = make(chan struct{})
   110  	c.timeout = time.Second * 30
   111  	c.txdelay = time.Millisecond * 7
   112  
   113  	all.AddTransports(c.Sock)
   114  	return true
   115  }
   116  
   117  func (c *T) NewMessage() *mangos.Message {
   118  	return mangos.NewMessage(c.MsgSize)
   119  }
   120  
   121  func (c *T) SendHook(*mangos.Message) bool {
   122  	c.BumpSend()
   123  	return true
   124  }
   125  
   126  func (c *T) RecvHook(*mangos.Message) bool {
   127  	c.BumpRecv()
   128  	return true
   129  }
   130  
   131  func (c *T) SendMsg(m *mangos.Message) error {
   132  	// We sleep a tiny bit, to avoid cramming too many messages on
   133  	// busses, etc. all at once.  (The test requires no dropped messages.)
   134  	time.Sleep(c.SendDelay())
   135  	_ = c.Sock.SetOption(mangos.OptionSendDeadline, time.Second*5)
   136  	return c.Sock.SendMsg(m)
   137  }
   138  
   139  func (c *T) RecvMsg() (*mangos.Message, error) {
   140  	_ = c.Sock.SetOption(mangos.OptionRecvDeadline, time.Second*5)
   141  	return c.Sock.RecvMsg()
   142  }
   143  
   144  func (c *T) Debugf(f string, v ...interface{}) {
   145  	if !c.debug {
   146  		return
   147  	}
   148  	now := time.Now().Format(time.StampMilli)
   149  	c.t.Logf("%s: Id %d: %s", now, c.ID, fmt.Sprintf(f, v...))
   150  }
   151  
   152  func (c *T) Logf(f string, v ...interface{}) {
   153  	now := time.Now().Format(time.StampMilli)
   154  	c.t.Logf("%s: Id %d: %s", now, c.ID, fmt.Sprintf(f, v...))
   155  }
   156  
   157  func (c *T) Errorf(f string, v ...interface{}) {
   158  	now := time.Now().Format(time.StampMilli)
   159  	c.t.Errorf("%s: Id %d: %s", now, c.ID, fmt.Sprintf(f, v...))
   160  }
   161  
   162  func (c *T) WantSend() int32 {
   163  	return c.WantTx
   164  }
   165  
   166  func (c *T) BumpSend() {
   167  	atomic.AddInt32(&c.numtx, 1)
   168  }
   169  
   170  func (c *T) GetSend() int32 {
   171  	return atomic.AddInt32(&c.numtx, 0)
   172  }
   173  
   174  func (c *T) WantRecv() int32 {
   175  	return c.WantRx
   176  }
   177  
   178  func (c *T) BumpRecv() {
   179  	atomic.AddInt32(&c.numrx, 1)
   180  }
   181  
   182  func (c *T) GetRecv() int32 {
   183  	return atomic.AddInt32(&c.numrx, 0)
   184  }
   185  
   186  func (c *T) GetID() int {
   187  	return c.ID
   188  }
   189  
   190  func (c *T) SendDone() {
   191  	c.Lock()
   192  	if !c.sdone {
   193  		c.sdone = true
   194  		close(c.sdoneq)
   195  	}
   196  	c.Unlock()
   197  }
   198  
   199  func (c *T) RecvDone() {
   200  	c.Lock()
   201  	if !c.rdone {
   202  		c.rdone = true
   203  		close(c.rdoneq)
   204  	}
   205  	c.Unlock()
   206  }
   207  
   208  func (c *T) WaitSend() bool {
   209  	select {
   210  	case <-c.sdoneq:
   211  		return true
   212  	case <-time.After(c.timeout):
   213  		return false
   214  	}
   215  }
   216  
   217  func (c *T) WaitRecv() bool {
   218  	select {
   219  	case <-c.rdoneq:
   220  		return true
   221  	case <-time.After(c.timeout):
   222  		return false
   223  	}
   224  }
   225  
   226  func (c *T) Dial() bool {
   227  	options := make(map[string]interface{})
   228  	switch {
   229  	case strings.HasPrefix(c.addr, "tls+tcp://"):
   230  		fallthrough
   231  	case strings.HasPrefix(c.addr, "wss://"):
   232  		options[mangos.OptionTLSConfig] = cliCfg
   233  	}
   234  
   235  	options[mangos.OptionDialAsynch] = true
   236  	err := c.Sock.DialOptions(c.addr, options)
   237  	if err != nil {
   238  		c.Errorf("Dial (%s) failed: %v", c.addr, err)
   239  		return false
   240  	}
   241  	// Allow time for transports to establish connection
   242  	time.Sleep(time.Millisecond * 500)
   243  	return true
   244  }
   245  
   246  func (c *T) Listen() bool {
   247  	options := make(map[string]interface{})
   248  	switch {
   249  	case strings.HasPrefix(c.addr, "tls+tcp://"):
   250  		fallthrough
   251  	case strings.HasPrefix(c.addr, "wss://"):
   252  		options[mangos.OptionTLSConfig] = srvCfg
   253  	}
   254  	err := c.Sock.ListenOptions(c.addr, options)
   255  	if err != nil {
   256  		c.Errorf("Listen (%s) failed: %v", c.addr, err)
   257  		return false
   258  	}
   259  	// Allow time for transports to establish connection
   260  	time.Sleep(time.Millisecond * 500)
   261  	return true
   262  }
   263  
   264  func (c *T) Close() {
   265  	_ = c.Sock.Close()
   266  }
   267  
   268  func (c *T) SendDelay() time.Duration {
   269  	return c.txdelay
   270  }
   271  
   272  func (c *T) IsServer() bool {
   273  	return c.Server
   274  }
   275  
   276  func (c *T) Ready() bool {
   277  	select {
   278  	case <-c.readyq:
   279  		return true
   280  	default:
   281  		return false
   282  	}
   283  }
   284  
   285  func (c *T) SetReady() {
   286  	close(c.readyq)
   287  }
   288  
   289  func (c *T) WaitReady() bool {
   290  	select {
   291  	case <-c.readyq:
   292  		return true
   293  	case <-time.After(c.timeout):
   294  		return false
   295  	}
   296  }
   297  
   298  func (c *T) SendStart() bool {
   299  
   300  	c.Debugf("Sending START")
   301  	m := MakeStart(uint32(c.GetID()))
   302  	if err := c.SendMsg(m); err != nil {
   303  		c.Errorf("SendStart failed: %v", err)
   304  		return false
   305  	}
   306  	return true
   307  }
   308  
   309  func (c *T) RecvStart() bool {
   310  	m, err := c.RecvMsg()
   311  	if err != nil {
   312  		c.Errorf("RecvMsg failed: %v", err)
   313  		return false
   314  	}
   315  	defer m.Free()
   316  	if addr := m.Pipe.Address(); addr != c.addr {
   317  		c.Errorf("Got unexpected message pipe address: %s", addr)
   318  		return false
   319  	}
   320  	if c.IsServer() && m.Pipe.Listener() == nil {
   321  		c.Errorf("Expected message pipe listener")
   322  		return false
   323  	}
   324  	if !c.IsServer() && m.Pipe.Dialer() == nil {
   325  		c.Errorf("Expected message pipe dialer")
   326  		return false
   327  	}
   328  
   329  	if v, ok := ParseStart(m); ok {
   330  		c.Debugf("Got START from %d", v)
   331  		return true
   332  	}
   333  	c.Debugf("Got unexpected message: %v", m.Body)
   334  	return false
   335  }
   336  
   337  func (c *T) WantSendStart() bool {
   338  	return c.WantTx > 0
   339  }
   340  
   341  func (c *T) WantRecvStart() bool {
   342  	return c.WantRx > 0
   343  }
   344  
   345  func (c *T) IsSynch() bool {
   346  	return c.Synch
   347  }
   348  
   349  func (c *T) NumReply() int {
   350  	return c.NReply
   351  }
   352  
   353  // MakeStart makes a start message, storing a 32-bit ID in the body.
   354  func MakeStart(v uint32) *mangos.Message {
   355  	m := mangos.NewMessage(10)
   356  	m.Body = append(m.Body, []byte("START")...)
   357  	m.Body = append(m.Body, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
   358  	return m
   359  }
   360  
   361  // ParseStart parses a start message, returning the ID stored therein.
   362  func ParseStart(m *mangos.Message) (uint32, bool) {
   363  	if bytes.HasPrefix(m.Body, []byte("START")) && len(m.Body) >= 9 {
   364  		v := binary.BigEndian.Uint32(m.Body[5:])
   365  		return v, true
   366  	}
   367  	return 0, false
   368  }
   369  
   370  func sendTester(c TestCase) bool {
   371  
   372  	time.Sleep(c.SendDelay())
   373  	defer c.SendDone()
   374  	for c.GetSend() < c.WantSend() {
   375  		msg := c.NewMessage()
   376  		if !c.SendHook(msg) {
   377  			c.Errorf("SendHook failed")
   378  			return false
   379  		}
   380  		if err := c.SendMsg(msg); err != nil {
   381  			c.Errorf("SendMsg failed: %v", err)
   382  			return false
   383  		}
   384  		c.Debugf("Good send (%d/%d)", c.GetSend(), c.WantSend())
   385  	}
   386  	c.Logf("Sent all %d messages", c.GetSend())
   387  	return true
   388  }
   389  
   390  func recvTester(c TestCase) bool {
   391  	defer c.RecvDone()
   392  	for c.GetRecv() < c.WantRecv() {
   393  		msg, err := c.RecvMsg()
   394  		if err != nil {
   395  			c.Errorf("RecvMsg failed: %v", err)
   396  			return false
   397  		}
   398  		if bytes.HasPrefix(msg.Body, []byte("START")) {
   399  			c.Debugf("Extra start message")
   400  			// left over slow start message, toss it
   401  			msg.Free()
   402  			continue
   403  		}
   404  		if !c.RecvHook(msg) {
   405  			c.Errorf("RecvHook failed")
   406  			return false
   407  		}
   408  		c.Debugf("Good recv (%d/%d)", c.GetRecv(), c.WantRecv())
   409  		msg.Free()
   410  	}
   411  	c.Logf("Got all %d messages", c.GetRecv())
   412  	return true
   413  }
   414  
   415  func sendRecvTester(c TestCase) bool {
   416  
   417  	time.Sleep(c.SendDelay())
   418  	defer c.SendDone()
   419  	defer c.RecvDone()
   420  	for c.GetSend() < c.WantSend() {
   421  		msg := c.NewMessage()
   422  		if !c.SendHook(msg) {
   423  			c.Errorf("SendHook failed")
   424  			return false
   425  		}
   426  		if err := c.SendMsg(msg); err != nil {
   427  			c.Errorf("SendMsg failed: %v", err)
   428  			return false
   429  		}
   430  		c.Debugf("Good send (%d/%d)", c.GetSend(), c.WantSend())
   431  
   432  		for i := 0; i < c.NumReply(); i++ {
   433  			msg, err := c.RecvMsg()
   434  			if err != nil {
   435  				c.Errorf("RecvMsg (reply) failed: %v", err)
   436  				return false
   437  			}
   438  			if bytes.HasPrefix(msg.Body, []byte("START")) {
   439  				c.Debugf("Extra start message")
   440  				// left over slow start message, toss it
   441  				msg.Free()
   442  				continue
   443  			}
   444  			if !c.RecvHook(msg) {
   445  				c.Errorf("RecvHook failed")
   446  				return false
   447  			}
   448  			c.Debugf("Good recv (%d/%d)", c.GetRecv(), c.WantRecv())
   449  			msg.Free()
   450  		}
   451  	}
   452  	c.Logf("Sent all %d messages", c.GetSend())
   453  	return true
   454  }
   455  
   456  func waitTest(c TestCase) {
   457  	if !c.WaitSend() {
   458  		c.Errorf("Timeout waiting for send")
   459  		return
   460  	}
   461  	if !c.WaitRecv() {
   462  		c.Errorf("Timeout waiting for recv")
   463  		return
   464  	}
   465  	c.Logf("Testing complete")
   466  }
   467  
   468  func startDialTest(c TestCase) {
   469  	go func() {
   470  		if !c.Dial() {
   471  			c.SendDone()
   472  			c.RecvDone()
   473  			return
   474  		}
   475  	}()
   476  }
   477  
   478  func startListenTest(c TestCase) {
   479  	go func() {
   480  
   481  		if !c.Listen() {
   482  			c.SendDone()
   483  			c.RecvDone()
   484  			return
   485  		}
   486  	}()
   487  }
   488  
   489  func startSendRecv(c TestCase) {
   490  	if c.IsSynch() {
   491  		go sendRecvTester(c)
   492  	} else {
   493  		go recvTester(c)
   494  		go sendTester(c)
   495  	}
   496  }
   497  
   498  func slowStartSender(c TestCase, exitq chan bool) {
   499  	if !c.WantSendStart() {
   500  		return
   501  	}
   502  	for {
   503  		select {
   504  		case <-exitq:
   505  			return
   506  		case <-time.After(time.Millisecond * 100):
   507  			if !c.SendStart() {
   508  				return
   509  			}
   510  		}
   511  	}
   512  }
   513  
   514  func slowStartReceiver(c TestCase, wakeq chan bool, exitq chan bool) {
   515  	defer func() {
   516  		wakeq <- true
   517  	}()
   518  	if !c.WantRecvStart() {
   519  		c.SetReady()
   520  		return
   521  	}
   522  	for {
   523  		if c.RecvStart() {
   524  			c.SetReady()
   525  			return
   526  		}
   527  		select {
   528  		case <-exitq:
   529  			return
   530  		default:
   531  		}
   532  	}
   533  }
   534  
   535  func slowStart(_ *testing.T, cases []TestCase) bool {
   536  	exitq := make(chan bool)
   537  	wakeq := make(chan bool)
   538  	needrdy := len(cases)
   539  	numexit := 0
   540  	numrdy := 0
   541  	exitqclosed := false
   542  
   543  	// Windows can take a while to complete TCP connections.
   544  	// I don't know why Windows in particular is so bad here.
   545  	time.Sleep(time.Millisecond * 250)
   546  	for i := range cases {
   547  		go slowStartSender(cases[i], exitq)
   548  		go slowStartReceiver(cases[i], wakeq, exitq)
   549  	}
   550  
   551  	timeout := time.After(time.Second * 5)
   552  	for numexit < needrdy {
   553  		select {
   554  		case <-timeout:
   555  			if !exitqclosed {
   556  				close(exitq)
   557  				exitqclosed = true
   558  			}
   559  			break
   560  		case <-wakeq:
   561  			numexit++
   562  		}
   563  	}
   564  
   565  	if !exitqclosed {
   566  		close(exitq)
   567  	}
   568  
   569  	for i := range cases {
   570  		if cases[i].Ready() {
   571  			numrdy++
   572  		} else {
   573  			cases[i].Errorf("Timed out waiting to become ready")
   574  		}
   575  	}
   576  	return numrdy == needrdy
   577  }
   578  
   579  // RunTests runs tests.
   580  func RunTests(t *testing.T, addr string, cases []TestCase) {
   581  
   582  	// We need to inject a slight bit of sleep to allow any sessions to
   583  	// drain before we close connections.
   584  	defer time.Sleep(50 * time.Millisecond)
   585  
   586  	t.Logf("Address %s, %d Cases", addr, len(cases))
   587  	for i := range cases {
   588  		if !cases[i].Init(t, addr) {
   589  			return
   590  		}
   591  	}
   592  
   593  	for i := range cases {
   594  		if cases[i].IsServer() {
   595  			startListenTest(cases[i])
   596  		} else {
   597  			startDialTest(cases[i])
   598  		}
   599  	}
   600  
   601  	if !slowStart(t, cases) {
   602  		return
   603  	}
   604  
   605  	for i := range cases {
   606  		startSendRecv(cases[i])
   607  	}
   608  	for i := range cases {
   609  		waitTest(cases[i])
   610  	}
   611  
   612  	for i := range cases {
   613  		cases[i].Close()
   614  	}
   615  }
   616  
   617  // We have to expose these, so that device tests can use them.
   618  
   619  var currPort uint16
   620  var currLock sync.Mutex
   621  
   622  func init() {
   623  	currPort = uint16(time.Now().UnixNano()%20000 + 20000)
   624  }
   625  
   626  func NextPort() uint16 {
   627  	currLock.Lock()
   628  	defer currLock.Unlock()
   629  	p := currPort
   630  	currPort++
   631  	return p
   632  }
   633  
   634  func AddrTestIPC() string {
   635  	return fmt.Sprintf("ipc://mangostest%d", NextPort())
   636  }
   637  
   638  func AddrTestWSS() string {
   639  	return fmt.Sprintf("wss://127.0.0.1:%d/", NextPort())
   640  }
   641  
   642  func AddrTestWS() string {
   643  	return fmt.Sprintf("ws://127.0.0.1:%d/", NextPort())
   644  }
   645  func AddrTestTCP() string {
   646  	return fmt.Sprintf("tcp://127.0.0.1:%d", NextPort())
   647  }
   648  
   649  func AddrTestTLS() string {
   650  	return fmt.Sprintf("tls+tcp://127.0.0.1:%d", NextPort())
   651  }
   652  
   653  func AddrTestInp() string {
   654  	return fmt.Sprintf("inproc://test_%d", NextPort())
   655  }