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