|
9 | 9 | "fmt" |
10 | 10 | "log" |
11 | 11 | "regexp" |
| 12 | + "sync" |
12 | 13 | "testing" |
13 | 14 | "testing/synctest" |
14 | 15 | "time" |
@@ -546,6 +547,105 @@ func TestRegression(t *testing.T) { |
546 | 547 | }) |
547 | 548 | } |
548 | 549 |
|
| 550 | +func TestPublishWithMutex(t *testing.T) { |
| 551 | + testPublishWithMutex(t, 1024) // arbitrary large number of events |
| 552 | +} |
| 553 | + |
| 554 | +// testPublishWithMutex publishes the specified number of events, |
| 555 | +// acquiring and releasing a mutex around each publish and each |
| 556 | +// subscriber event receive. |
| 557 | +// |
| 558 | +// The test fails if it loses any events or times out due to a deadlock. |
| 559 | +// Unfortunately, a goroutine waiting on a mutex held by a durably blocked |
| 560 | +// goroutine is not itself considered durably blocked, so [synctest] cannot |
| 561 | +// detect this deadlock on its own. |
| 562 | +func testPublishWithMutex(t *testing.T, n int) { |
| 563 | + synctest.Test(t, func(t *testing.T) { |
| 564 | + b := eventbus.New() |
| 565 | + defer b.Close() |
| 566 | + |
| 567 | + c := b.Client("TestClient") |
| 568 | + |
| 569 | + evts := make([]any, n) |
| 570 | + for i := range evts { |
| 571 | + evts[i] = EventA{Counter: i} |
| 572 | + } |
| 573 | + exp := expectEvents(t, evts...) |
| 574 | + |
| 575 | + var mu sync.Mutex |
| 576 | + eventbus.SubscribeFunc[EventA](c, func(e EventA) { |
| 577 | + // Acquire the same mutex as the publisher. |
| 578 | + mu.Lock() |
| 579 | + mu.Unlock() |
| 580 | + |
| 581 | + // Mark event as received, so we can check for lost events. |
| 582 | + exp.Got(e) |
| 583 | + }) |
| 584 | + |
| 585 | + p := eventbus.Publish[EventA](c) |
| 586 | + go func() { |
| 587 | + // Publish events, acquiring the mutex around each publish. |
| 588 | + for i := range n { |
| 589 | + mu.Lock() |
| 590 | + p.Publish(EventA{Counter: i}) |
| 591 | + mu.Unlock() |
| 592 | + } |
| 593 | + }() |
| 594 | + |
| 595 | + synctest.Wait() |
| 596 | + |
| 597 | + if !exp.Empty() { |
| 598 | + t.Errorf("unexpected extra events: %+v", exp.want) |
| 599 | + } |
| 600 | + }) |
| 601 | +} |
| 602 | + |
| 603 | +func TestPublishFromSubscriber(t *testing.T) { |
| 604 | + testPublishFromSubscriber(t, 1024) // arbitrary large number of events |
| 605 | +} |
| 606 | + |
| 607 | +// testPublishFromSubscriber publishes the specified number of EventA events. |
| 608 | +// Each EventA causes the subscriber to publish an EventB. |
| 609 | +// The test fails if it loses any events or if a deadlock occurs. |
| 610 | +func testPublishFromSubscriber(t *testing.T, n int) { |
| 611 | + synctest.Test(t, func(t *testing.T) { |
| 612 | + b := eventbus.New() |
| 613 | + defer b.Close() |
| 614 | + |
| 615 | + c := b.Client("TestClient") |
| 616 | + |
| 617 | + // Ultimately we expect to receive n EventB events |
| 618 | + // published as a result of receiving n EventA events. |
| 619 | + evts := make([]any, n) |
| 620 | + for i := range evts { |
| 621 | + evts[i] = EventB{Counter: i} |
| 622 | + } |
| 623 | + exp := expectEvents(t, evts...) |
| 624 | + |
| 625 | + pubA := eventbus.Publish[EventA](c) |
| 626 | + pubB := eventbus.Publish[EventB](c) |
| 627 | + |
| 628 | + eventbus.SubscribeFunc[EventA](c, func(e EventA) { |
| 629 | + // Upon receiving EventA, publish EventB. |
| 630 | + pubB.Publish(EventB{Counter: e.Counter}) |
| 631 | + }) |
| 632 | + eventbus.SubscribeFunc[EventB](c, func(e EventB) { |
| 633 | + // Mark EventB as received. |
| 634 | + exp.Got(e) |
| 635 | + }) |
| 636 | + |
| 637 | + for i := range n { |
| 638 | + pubA.Publish(EventA{Counter: i}) |
| 639 | + } |
| 640 | + |
| 641 | + synctest.Wait() |
| 642 | + |
| 643 | + if !exp.Empty() { |
| 644 | + t.Errorf("unexpected extra events: %+v", exp.want) |
| 645 | + } |
| 646 | + }) |
| 647 | +} |
| 648 | + |
549 | 649 | type queueChecker struct { |
550 | 650 | t *testing.T |
551 | 651 | want []any |
|
0 commit comments