...

Source file src/go.opentelemetry.io/otel/sdk/internal/internaltest/harness.go

Documentation: go.opentelemetry.io/otel/sdk/internal/internaltest

     1  // Code created by gotmpl. DO NOT MODIFY.
     2  // source: internal/shared/internaltest/harness.go.tmpl
     3  
     4  // Copyright The OpenTelemetry Authors
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package internaltest // import "go.opentelemetry.io/otel/sdk/internal/internaltest"
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"go.opentelemetry.io/otel/attribute"
    28  	"go.opentelemetry.io/otel/codes"
    29  	"go.opentelemetry.io/otel/sdk/internal/matchers"
    30  	"go.opentelemetry.io/otel/trace"
    31  )
    32  
    33  // Harness is a testing harness used to test implementations of the
    34  // OpenTelemetry API.
    35  type Harness struct {
    36  	t *testing.T
    37  }
    38  
    39  // NewHarness returns an instantiated *Harness using t.
    40  func NewHarness(t *testing.T) *Harness {
    41  	return &Harness{
    42  		t: t,
    43  	}
    44  }
    45  
    46  // TestTracerProvider runs validation tests for an implementation of the OpenTelemetry
    47  // TracerProvider API.
    48  func (h *Harness) TestTracerProvider(subjectFactory func() trace.TracerProvider) {
    49  	h.t.Run("#Start", func(t *testing.T) {
    50  		t.Run("allow creating an arbitrary number of TracerProvider instances", func(t *testing.T) {
    51  			t.Parallel()
    52  
    53  			e := matchers.NewExpecter(t)
    54  
    55  			tp1 := subjectFactory()
    56  			tp2 := subjectFactory()
    57  
    58  			e.Expect(tp1).NotToEqual(tp2)
    59  		})
    60  		t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
    61  			t.Parallel()
    62  
    63  			runner := func(tp trace.TracerProvider) <-chan struct{} {
    64  				done := make(chan struct{})
    65  				go func(tp trace.TracerProvider) {
    66  					var wg sync.WaitGroup
    67  					for i := 0; i < 20; i++ {
    68  						wg.Add(1)
    69  						go func(name, version string) {
    70  							_ = tp.Tracer(name, trace.WithInstrumentationVersion(version))
    71  							wg.Done()
    72  						}(fmt.Sprintf("tracer %d", i%5), fmt.Sprintf("%d", i))
    73  					}
    74  					wg.Wait()
    75  					done <- struct{}{}
    76  				}(tp)
    77  				return done
    78  			}
    79  
    80  			matchers.NewExpecter(t).Expect(func() {
    81  				// Run with multiple TracerProvider to ensure they encapsulate
    82  				// their own Tracers.
    83  				tp1 := subjectFactory()
    84  				tp2 := subjectFactory()
    85  
    86  				done1 := runner(tp1)
    87  				done2 := runner(tp2)
    88  
    89  				<-done1
    90  				<-done2
    91  			}).NotToPanic()
    92  		})
    93  	})
    94  }
    95  
    96  // TestTracer runs validation tests for an implementation of the OpenTelemetry
    97  // Tracer API.
    98  func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
    99  	h.t.Run("#Start", func(t *testing.T) {
   100  		t.Run("propagates the original context", func(t *testing.T) {
   101  			t.Parallel()
   102  
   103  			e := matchers.NewExpecter(t)
   104  			subject := subjectFactory()
   105  
   106  			ctxKey := testCtxKey{}
   107  			ctxValue := "ctx value"
   108  			ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
   109  
   110  			ctx, _ = subject.Start(ctx, "test")
   111  
   112  			e.Expect(ctx.Value(ctxKey)).ToEqual(ctxValue)
   113  		})
   114  
   115  		t.Run("returns a span containing the expected properties", func(t *testing.T) {
   116  			t.Parallel()
   117  
   118  			e := matchers.NewExpecter(t)
   119  			subject := subjectFactory()
   120  
   121  			_, span := subject.Start(context.Background(), "test")
   122  
   123  			e.Expect(span).NotToBeNil()
   124  
   125  			e.Expect(span.SpanContext().IsValid()).ToBeTrue()
   126  		})
   127  
   128  		t.Run("stores the span on the provided context", func(t *testing.T) {
   129  			t.Parallel()
   130  
   131  			e := matchers.NewExpecter(t)
   132  			subject := subjectFactory()
   133  
   134  			ctx, span := subject.Start(context.Background(), "test")
   135  
   136  			e.Expect(span).NotToBeNil()
   137  			e.Expect(span.SpanContext()).NotToEqual(trace.SpanContext{})
   138  			e.Expect(trace.SpanFromContext(ctx)).ToEqual(span)
   139  		})
   140  
   141  		t.Run("starts spans with unique trace and span IDs", func(t *testing.T) {
   142  			t.Parallel()
   143  
   144  			e := matchers.NewExpecter(t)
   145  			subject := subjectFactory()
   146  
   147  			_, span1 := subject.Start(context.Background(), "span1")
   148  			_, span2 := subject.Start(context.Background(), "span2")
   149  
   150  			sc1 := span1.SpanContext()
   151  			sc2 := span2.SpanContext()
   152  
   153  			e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID())
   154  			e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID())
   155  		})
   156  
   157  		t.Run("propagates a parent's trace ID through the context", func(t *testing.T) {
   158  			t.Parallel()
   159  
   160  			e := matchers.NewExpecter(t)
   161  			subject := subjectFactory()
   162  
   163  			ctx, parent := subject.Start(context.Background(), "parent")
   164  			_, child := subject.Start(ctx, "child")
   165  
   166  			psc := parent.SpanContext()
   167  			csc := child.SpanContext()
   168  
   169  			e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
   170  			e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
   171  		})
   172  
   173  		t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) {
   174  			t.Parallel()
   175  
   176  			e := matchers.NewExpecter(t)
   177  			subject := subjectFactory()
   178  
   179  			ctx, parent := subject.Start(context.Background(), "parent")
   180  			_, child := subject.Start(ctx, "child", trace.WithNewRoot())
   181  
   182  			psc := parent.SpanContext()
   183  			csc := child.SpanContext()
   184  
   185  			e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
   186  			e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
   187  		})
   188  
   189  		t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) {
   190  			t.Parallel()
   191  
   192  			e := matchers.NewExpecter(t)
   193  			subject := subjectFactory()
   194  
   195  			_, remoteParent := subject.Start(context.Background(), "remote parent")
   196  			parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
   197  			_, child := subject.Start(parentCtx, "child")
   198  
   199  			psc := remoteParent.SpanContext()
   200  			csc := child.SpanContext()
   201  
   202  			e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
   203  			e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
   204  		})
   205  
   206  		t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) {
   207  			t.Parallel()
   208  
   209  			e := matchers.NewExpecter(t)
   210  			subject := subjectFactory()
   211  
   212  			_, remoteParent := subject.Start(context.Background(), "remote parent")
   213  			parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
   214  			_, child := subject.Start(parentCtx, "child", trace.WithNewRoot())
   215  
   216  			psc := remoteParent.SpanContext()
   217  			csc := child.SpanContext()
   218  
   219  			e.Expect(csc.TraceID()).NotToEqual(psc.TraceID())
   220  			e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
   221  		})
   222  
   223  		t.Run("all methods are safe to be called concurrently", func(t *testing.T) {
   224  			t.Parallel()
   225  
   226  			e := matchers.NewExpecter(t)
   227  			tracer := subjectFactory()
   228  
   229  			ctx, parent := tracer.Start(context.Background(), "span")
   230  
   231  			runner := func(tp trace.Tracer) <-chan struct{} {
   232  				done := make(chan struct{})
   233  				go func(tp trace.Tracer) {
   234  					var wg sync.WaitGroup
   235  					for i := 0; i < 20; i++ {
   236  						wg.Add(1)
   237  						go func(name string) {
   238  							defer wg.Done()
   239  							_, child := tp.Start(ctx, name)
   240  
   241  							psc := parent.SpanContext()
   242  							csc := child.SpanContext()
   243  
   244  							e.Expect(csc.TraceID()).ToEqual(psc.TraceID())
   245  							e.Expect(csc.SpanID()).NotToEqual(psc.SpanID())
   246  						}(fmt.Sprintf("span %d", i))
   247  					}
   248  					wg.Wait()
   249  					done <- struct{}{}
   250  				}(tp)
   251  				return done
   252  			}
   253  
   254  			e.Expect(func() {
   255  				done := runner(tracer)
   256  
   257  				<-done
   258  			}).NotToPanic()
   259  		})
   260  	})
   261  
   262  	h.testSpan(subjectFactory)
   263  }
   264  
   265  func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
   266  	methods := map[string]func(span trace.Span){
   267  		"#End": func(span trace.Span) {
   268  			span.End()
   269  		},
   270  		"#AddEvent": func(span trace.Span) {
   271  			span.AddEvent("test event")
   272  		},
   273  		"#AddEventWithTimestamp": func(span trace.Span) {
   274  			span.AddEvent("test event", trace.WithTimestamp(time.Now().Add(1*time.Second)))
   275  		},
   276  		"#SetStatus": func(span trace.Span) {
   277  			span.SetStatus(codes.Error, "internal")
   278  		},
   279  		"#SetName": func(span trace.Span) {
   280  			span.SetName("new name")
   281  		},
   282  		"#SetAttributes": func(span trace.Span) {
   283  			span.SetAttributes(attribute.String("key1", "value"), attribute.Int("key2", 123))
   284  		},
   285  	}
   286  	mechanisms := map[string]func() trace.Span{
   287  		"Span created via Tracer#Start": func() trace.Span {
   288  			tracer := tracerFactory()
   289  			_, subject := tracer.Start(context.Background(), "test")
   290  
   291  			return subject
   292  		},
   293  		"Span created via span.TracerProvider()": func() trace.Span {
   294  			ctx, spanA := tracerFactory().Start(context.Background(), "span1")
   295  
   296  			_, spanB := spanA.TracerProvider().Tracer("second").Start(ctx, "span2")
   297  			return spanB
   298  		},
   299  	}
   300  
   301  	for mechanismName, mechanism := range mechanisms {
   302  		h.t.Run(mechanismName, func(t *testing.T) {
   303  			for methodName, method := range methods {
   304  				t.Run(methodName, func(t *testing.T) {
   305  					t.Run("is thread-safe", func(t *testing.T) {
   306  						t.Parallel()
   307  
   308  						span := mechanism()
   309  
   310  						wg := &sync.WaitGroup{}
   311  						wg.Add(2)
   312  
   313  						go func() {
   314  							defer wg.Done()
   315  
   316  							method(span)
   317  						}()
   318  
   319  						go func() {
   320  							defer wg.Done()
   321  
   322  							method(span)
   323  						}()
   324  
   325  						wg.Wait()
   326  					})
   327  				})
   328  			}
   329  
   330  			t.Run("#End", func(t *testing.T) {
   331  				t.Run("can be called multiple times", func(t *testing.T) {
   332  					t.Parallel()
   333  
   334  					span := mechanism()
   335  
   336  					span.End()
   337  					span.End()
   338  				})
   339  			})
   340  		})
   341  	}
   342  }
   343  
   344  type testCtxKey struct{}
   345  

View as plain text