helpers.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
  2. //go:build go1.19
  3. package testutil // import "github.com/docker/docker/testutil"
  4. import (
  5. "context"
  6. "io"
  7. "os"
  8. "reflect"
  9. "strings"
  10. "sync"
  11. "testing"
  12. "github.com/containerd/log"
  13. "go.opentelemetry.io/otel"
  14. "go.opentelemetry.io/otel/attribute"
  15. "go.opentelemetry.io/otel/codes"
  16. "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
  17. "go.opentelemetry.io/otel/propagation"
  18. "go.opentelemetry.io/otel/sdk/resource"
  19. "go.opentelemetry.io/otel/sdk/trace"
  20. semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
  21. "gotest.tools/v3/icmd"
  22. )
  23. // DevZero acts like /dev/zero but in an OS-independent fashion.
  24. var DevZero io.Reader = devZero{}
  25. type devZero struct{}
  26. func (d devZero) Read(p []byte) (n int, err error) {
  27. for i := range p {
  28. p[i] = 0
  29. }
  30. return len(p), nil
  31. }
  32. var tracingOnce sync.Once
  33. // ConfigureTracing sets up an OTLP tracing exporter for use in tests.
  34. func ConfigureTracing() func(context.Context) {
  35. if os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") == "" {
  36. // No OTLP endpoint configured, so don't bother setting up tracing.
  37. // Since we are not using a batch exporter we don't want tracing to block up tests.
  38. return func(context.Context) {}
  39. }
  40. var tp *trace.TracerProvider
  41. tracingOnce.Do(func() {
  42. ctx := context.Background()
  43. exp := otlptracehttp.NewUnstarted()
  44. sp := trace.NewBatchSpanProcessor(exp)
  45. props := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})
  46. otel.SetTextMapPropagator(props)
  47. tp = trace.NewTracerProvider(
  48. trace.WithSpanProcessor(sp),
  49. trace.WithSampler(trace.AlwaysSample()),
  50. trace.WithResource(resource.NewSchemaless(semconv.ServiceName("integration-test-client"))),
  51. )
  52. otel.SetTracerProvider(tp)
  53. if err := exp.Start(ctx); err != nil {
  54. log.G(ctx).WithError(err).Warn("Failed to start tracing exporter")
  55. }
  56. })
  57. // if ConfigureTracing was called multiple times we'd have a nil `tp` here
  58. // Get the already configured tracer provider
  59. if tp == nil {
  60. tp = otel.GetTracerProvider().(*trace.TracerProvider)
  61. }
  62. return func(ctx context.Context) {
  63. if err := tp.Shutdown(ctx); err != nil {
  64. log.G(ctx).WithError(err).Warn("Failed to shutdown tracer")
  65. }
  66. }
  67. }
  68. // TestingT is an interface wrapper around *testing.T and *testing.B.
  69. type TestingT interface {
  70. Name() string
  71. Cleanup(func())
  72. Log(...any)
  73. Failed() bool
  74. }
  75. // StartSpan starts a span for the given test.
  76. func StartSpan(ctx context.Context, t TestingT) context.Context {
  77. ConfigureTracing()
  78. ctx, span := otel.Tracer("").Start(ctx, t.Name())
  79. t.Cleanup(func() {
  80. if t.Failed() {
  81. span.SetStatus(codes.Error, "test failed")
  82. }
  83. span.End()
  84. })
  85. return ctx
  86. }
  87. func RunCommand(ctx context.Context, cmd string, args ...string) *icmd.Result {
  88. _, span := otel.Tracer("").Start(ctx, "RunCommand "+cmd+" "+strings.Join(args, " "))
  89. res := icmd.RunCommand(cmd, args...)
  90. if res.Error != nil {
  91. span.SetStatus(codes.Error, res.Error.Error())
  92. }
  93. span.SetAttributes(attribute.String("cmd", cmd), attribute.String("args", strings.Join(args, " ")))
  94. span.SetAttributes(attribute.Int("exit", res.ExitCode))
  95. span.SetAttributes(attribute.String("stdout", res.Stdout()), attribute.String("stderr", res.Stderr()))
  96. span.End()
  97. return res
  98. }
  99. type testContextStore struct {
  100. mu sync.Mutex
  101. idx map[TestingT]context.Context
  102. }
  103. var testContexts = &testContextStore{idx: make(map[TestingT]context.Context)}
  104. func (s *testContextStore) Get(t TestingT) context.Context {
  105. s.mu.Lock()
  106. defer s.mu.Unlock()
  107. ctx, ok := s.idx[t]
  108. if ok {
  109. return ctx
  110. }
  111. ctx = context.Background()
  112. s.idx[t] = ctx
  113. return ctx
  114. }
  115. func (s *testContextStore) Set(ctx context.Context, t TestingT) {
  116. s.mu.Lock()
  117. if _, ok := s.idx[t]; ok {
  118. panic("test context already set")
  119. }
  120. s.idx[t] = ctx
  121. s.mu.Unlock()
  122. }
  123. func (s *testContextStore) Delete(t *testing.T) {
  124. s.mu.Lock()
  125. defer s.mu.Unlock()
  126. delete(s.idx, t)
  127. }
  128. func GetContext(t TestingT) context.Context {
  129. return testContexts.Get(t)
  130. }
  131. func SetContext(t TestingT, ctx context.Context) {
  132. testContexts.Set(ctx, t)
  133. }
  134. func CleanupContext(t *testing.T) {
  135. testContexts.Delete(t)
  136. }
  137. // CheckNotParallel checks if t.Parallel() was not called on the current test.
  138. // There's no public method to check this, so we use reflection to check the
  139. // internal field set by t.Parallel()
  140. // https://github.com/golang/go/blob/8e658eee9c7a67a8a79a8308695920ac9917566c/src/testing/testing.go#L1449
  141. //
  142. // Since this is not a public API, it might change at any time.
  143. func CheckNotParallel(t testing.TB) {
  144. t.Helper()
  145. field := reflect.ValueOf(t).Elem().FieldByName("isParallel")
  146. if field.IsValid() {
  147. if field.Bool() {
  148. t.Fatal("t.Parallel() was called before")
  149. }
  150. } else {
  151. t.Logf("FIXME: CheckParallel could not determine if test %s is parallel - did the t.Parallel() implementation change?", t.Name())
  152. }
  153. }