tailscale/util/eventbus/eventbustest/examples_test.go
Claus Lensbøl 005e264b54
util/eventbus/eventbustest: add support for synctest instead of timers (#17522)
Before synctest, timers was needed to allow the events to flow into the
test bus. There is still a timer, but this one is not derived from the
test deadline and it is mostly arbitrary as synctest will render it
practically non-existent.

With this approach, tests that do not need to test for the absence of
events do not rely on synctest.

Updates #15160

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
2025-10-10 15:33:30 -04:00

261 lines
6.9 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package eventbustest_test
import (
"testing"
"testing/synctest"
"time"
"tailscale.com/util/eventbus"
"tailscale.com/util/eventbus/eventbustest"
)
func TestExample_Expect(t *testing.T) {
type eventOfInterest struct{}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updater := eventbus.Publish[eventOfInterest](client)
updater.Publish(eventOfInterest{})
if err := eventbustest.Expect(tw, eventbustest.Type[eventOfInterest]()); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
// Output:
// OK
}
func TestExample_Expect_WithFunction(t *testing.T) {
type eventOfInterest struct {
value int
}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updater := eventbus.Publish[eventOfInterest](client)
updater.Publish(eventOfInterest{43})
updater.Publish(eventOfInterest{42})
// Look for an event of eventOfInterest with a specific value
if err := eventbustest.Expect(tw, func(event eventOfInterest) (bool, error) {
if event.value != 42 {
return false, nil // Look for another event with the expected value.
// You could alternatively return an error here to ensure that the
// first seen eventOfInterest matches the value:
// return false, fmt.Errorf("expected 42, got %d", event.value)
}
return true, nil
}); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
// Output:
// OK
}
func TestExample_Expect_MultipleEvents(t *testing.T) {
type eventOfInterest struct{}
type eventOfNoConcern struct{}
type eventOfCuriosity struct{}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updaterInterest := eventbus.Publish[eventOfInterest](client)
updaterConcern := eventbus.Publish[eventOfNoConcern](client)
updaterCuriosity := eventbus.Publish[eventOfCuriosity](client)
updaterInterest.Publish(eventOfInterest{})
updaterConcern.Publish(eventOfNoConcern{})
updaterCuriosity.Publish(eventOfCuriosity{})
// Even though three events was published, we just care about the two
if err := eventbustest.Expect(tw,
eventbustest.Type[eventOfInterest](),
eventbustest.Type[eventOfCuriosity]()); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
// Output:
// OK
}
func TestExample_ExpectExactly_MultipleEvents(t *testing.T) {
type eventOfInterest struct{}
type eventOfNoConcern struct{}
type eventOfCuriosity struct{}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updaterInterest := eventbus.Publish[eventOfInterest](client)
updaterConcern := eventbus.Publish[eventOfNoConcern](client)
updaterCuriosity := eventbus.Publish[eventOfCuriosity](client)
updaterInterest.Publish(eventOfInterest{})
updaterConcern.Publish(eventOfNoConcern{})
updaterCuriosity.Publish(eventOfCuriosity{})
// Will fail as more events than the two expected comes in
if err := eventbustest.ExpectExactly(tw,
eventbustest.Type[eventOfInterest](),
eventbustest.Type[eventOfCuriosity]()); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
}
func TestExample_Expect_WithMultipleFunctions(t *testing.T) {
type eventOfInterest struct {
value int
}
type eventOfNoConcern struct{}
type eventOfCuriosity struct {
value string
}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updaterInterest := eventbus.Publish[eventOfInterest](client)
updaterConcern := eventbus.Publish[eventOfNoConcern](client)
updaterCuriosity := eventbus.Publish[eventOfCuriosity](client)
updaterInterest.Publish(eventOfInterest{42})
updaterConcern.Publish(eventOfNoConcern{})
updaterCuriosity.Publish(eventOfCuriosity{"42"})
interest := func(event eventOfInterest) (bool, error) {
if event.value == 42 {
return true, nil
}
return false, nil
}
curiosity := func(event eventOfCuriosity) (bool, error) {
if event.value == "42" {
return true, nil
}
return false, nil
}
// Will fail as more events than the two expected comes in
if err := eventbustest.Expect(tw, interest, curiosity); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
// Output:
// OK
}
func TestExample_ExpectExactly_WithMultipleFunctions(t *testing.T) {
type eventOfInterest struct {
value int
}
type eventOfNoConcern struct{}
type eventOfCuriosity struct {
value string
}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updaterInterest := eventbus.Publish[eventOfInterest](client)
updaterConcern := eventbus.Publish[eventOfNoConcern](client)
updaterCuriosity := eventbus.Publish[eventOfCuriosity](client)
updaterInterest.Publish(eventOfInterest{42})
updaterConcern.Publish(eventOfNoConcern{})
updaterCuriosity.Publish(eventOfCuriosity{"42"})
interest := func(event eventOfInterest) (bool, error) {
if event.value == 42 {
return true, nil
}
return false, nil
}
curiosity := func(event eventOfCuriosity) (bool, error) {
if event.value == "42" {
return true, nil
}
return false, nil
}
// Will fail as more events than the two expected comes in
if err := eventbustest.ExpectExactly(tw, interest, curiosity); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
// Output:
// expected event type eventbustest.eventOfCuriosity, saw eventbustest.eventOfNoConcern, at index 1
}
func TestExample_ExpectExactly_NoEvents(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
go func() {
// Do some work that does not produce an event
time.Sleep(10 * time.Second)
t.Log("Not producing events")
}()
// Wait for all other routines to be stale before continuing to ensure that
// there is nothing running that would produce an event at a later time.
synctest.Wait()
if err := eventbustest.ExpectExactly(tw); err != nil {
t.Error(err.Error())
} else {
t.Log("OK")
}
// Output:
// OK
})
}
func TestExample_ExpectExactly_OneEventExpectingTwo(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
type eventOfInterest struct{}
bus := eventbustest.NewBus(t)
tw := eventbustest.NewWatcher(t, bus)
client := bus.Client("testClient")
updater := eventbus.Publish[eventOfInterest](client)
go func() {
// Do some work that does not produce an event
time.Sleep(10 * time.Second)
updater.Publish(eventOfInterest{})
}()
// Wait for all other routines to be stale before continuing to ensure that
// there is nothing running that would produce an event at a later time.
synctest.Wait()
if err := eventbustest.ExpectExactly(tw,
eventbustest.Type[eventOfInterest](),
eventbustest.Type[eventOfInterest](),
); err != nil {
t.Log(err.Error())
} else {
t.Log("OK")
}
// Output:
// timed out waiting for event, saw 1 events, 2 was expected
})
}