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 }