lazy_obj.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. package testutil
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/stretchr/testify/require"
  6. )
  7. // LazyObj is a function that returns an object of type T.
  8. type LazyObj[T any] func() T
  9. // LazyObjT is an interface that provides access to the [testing.T] object.
  10. type LazyObjT interface {
  11. T() *testing.T
  12. }
  13. // LazyObjNew is a function that creates and returns an object of type T and an error if any.
  14. type LazyObjNew[T any] func() (T, error)
  15. // LazyObjDrop is a callback that is called when [LazyObj] is reset.
  16. // It receives a pointer to the object being dropped.
  17. // If the object was not yet initialized, the callback is not called.
  18. type LazyObjDrop[T any] func(T) error
  19. // newLazyObj creates a new [LazyObj] that initializes the object on the first call.
  20. // It returns a function that can be called to get the object and a cancel function
  21. // that can be called to reset the object.
  22. func newLazyObj[T any](
  23. s LazyObjT,
  24. newFn LazyObjNew[T],
  25. dropFn ...LazyObjDrop[T],
  26. ) (LazyObj[T], context.CancelFunc) {
  27. s.T().Helper()
  28. var obj *T
  29. init := func() T {
  30. if obj != nil {
  31. return *obj
  32. }
  33. o, err := newFn()
  34. require.NoError(s.T(), err, "Failed to initialize lazy object")
  35. obj = &o
  36. return o
  37. }
  38. cancel := func() {
  39. if obj == nil {
  40. return
  41. }
  42. for _, fn := range dropFn {
  43. if fn == nil {
  44. continue
  45. }
  46. require.NoError(s.T(), fn(*obj), "Failed to reset lazy object")
  47. }
  48. obj = nil
  49. }
  50. return init, cancel
  51. }