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 }