diff --git a/common/testutils/test_utils.go b/common/testutils/test_utils.go new file mode 100644 index 0000000000..f6cbbd2dcd --- /dev/null +++ b/common/testutils/test_utils.go @@ -0,0 +1,85 @@ +package testutils + +import ( + "context" + "fmt" + "github.com/stretchr/testify/assert" + "golang.org/x/exp/rand" + "testing" + "time" +) + +// InitializeRandom initializes the random number generator. If no arguments are provided, then the seed is randomly +// generated. If a single argument is provided, then the seed is fixed to that value. +func InitializeRandom(fixedSeed ...uint64) { + + var seed uint64 + if len(fixedSeed) == 0 { + rand.Seed(uint64(time.Now().UnixNano())) + seed = rand.Uint64() + } else if len(fixedSeed) == 1 { + seed = fixedSeed[0] + } else { + panic("too many arguments, expected exactly one seed") + } + + fmt.Printf("Random seed: %d\n", seed) + rand.Seed(seed) +} + +// AssertEventuallyTrue asserts that a condition is true within a given duration. Repeatably checks the condition. +func AssertEventuallyTrue(t *testing.T, condition func() bool, duration time.Duration, debugInfo ...any) { + if len(debugInfo) == 0 { + debugInfo = []any{"Condition did not become true within the given duration"} + } + + ticker := time.NewTicker(1 * time.Millisecond) + select { + case <-ticker.C: + if condition() { + return + } + case <-time.After(duration): + assert.True(t, condition(), debugInfo...) + } +} + +// AssertEventuallyEquals asserts that a function returns a specific value within a given duration. +func AssertEventuallyEquals(t *testing.T, expected any, actual func() any, duration time.Duration, debugInfo ...any) { + if len(debugInfo) == 0 { + debugInfo = []any{ + "Expected value did not match actual value within the given duration. Expected: %v, Actual: %v", + expected, + actual(), + } + } + + condition := func() bool { + return expected == actual() + } + + AssertEventuallyTrue(t, condition, duration, debugInfo...) +} + +// ExecuteWithTimeout executes a function with a timeout. +// Panics if the function does not complete within the given duration. +func ExecuteWithTimeout(f func(), duration time.Duration, debugInfo ...any) { + if len(debugInfo) == 0 { + debugInfo = []any{"Function did not complete within the given duration"} + } + + ctx, cancel := context.WithTimeout(context.Background(), duration) + + finished := false + go func() { + f() + finished = true + cancel() + }() + + <-ctx.Done() + + if !finished { + panic(fmt.Sprintf(debugInfo[0].(string), debugInfo[1:]...)) + } +} diff --git a/common/testutils/test_utils_test.go b/common/testutils/test_utils_test.go new file mode 100644 index 0000000000..7f6da80e1b --- /dev/null +++ b/common/testutils/test_utils_test.go @@ -0,0 +1,26 @@ +package testutils + +import ( + "github.com/stretchr/testify/assert" + "golang.org/x/exp/rand" + "testing" +) + +func TestRandomSetup(t *testing.T) { + InitializeRandom() + x := rand.Int() + + InitializeRandom() + y := rand.Int() + + assert.NotEqual(t, x, y) + + seed := uint64(rand.Int()) + InitializeRandom(seed) + a := rand.Int() + + InitializeRandom(seed) + b := rand.Int() + + assert.Equal(t, a, b) +}