1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package 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/internal/matchers"
30 "go.opentelemetry.io/otel/trace"
31 )
32
33
34
35 type Harness struct {
36 t *testing.T
37 }
38
39
40 func NewHarness(t *testing.T) *Harness {
41 return &Harness{
42 t: t,
43 }
44 }
45
46
47
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
82
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
97
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