...

Source file src/go.opentelemetry.io/otel/sdk/trace/trace_test.go

Documentation: go.opentelemetry.io/otel/sdk/trace

     1  // Copyright The OpenTelemetry Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package trace
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"math"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"sync/atomic"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/google/go-cmp/cmp"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  
    33  	"go.opentelemetry.io/otel"
    34  	"go.opentelemetry.io/otel/attribute"
    35  	"go.opentelemetry.io/otel/codes"
    36  	"go.opentelemetry.io/otel/sdk/instrumentation"
    37  	ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
    38  	"go.opentelemetry.io/otel/sdk/resource"
    39  	semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
    40  	"go.opentelemetry.io/otel/trace"
    41  )
    42  
    43  const envVar = "OTEL_RESOURCE_ATTRIBUTES"
    44  
    45  type storingHandler struct {
    46  	errs []error
    47  }
    48  
    49  func (s *storingHandler) Handle(err error) {
    50  	s.errs = append(s.errs, err)
    51  }
    52  
    53  func (s *storingHandler) Reset() {
    54  	s.errs = nil
    55  }
    56  
    57  var (
    58  	tid trace.TraceID
    59  	sid trace.SpanID
    60  	sc  trace.SpanContext
    61  
    62  	handler = &storingHandler{}
    63  )
    64  
    65  func init() {
    66  	tid, _ = trace.TraceIDFromHex("01020304050607080102040810203040")
    67  	sid, _ = trace.SpanIDFromHex("0102040810203040")
    68  	sc = trace.NewSpanContext(trace.SpanContextConfig{
    69  		TraceID:    tid,
    70  		SpanID:     sid,
    71  		TraceFlags: 0x1,
    72  	})
    73  
    74  	otel.SetErrorHandler(handler)
    75  }
    76  
    77  func TestTracerFollowsExpectedAPIBehaviour(t *testing.T) {
    78  	harness := ottest.NewHarness(t)
    79  
    80  	harness.TestTracerProvider(func() trace.TracerProvider {
    81  		return NewTracerProvider(WithSampler(TraceIDRatioBased(0)))
    82  	})
    83  
    84  	tp := NewTracerProvider(WithSampler(TraceIDRatioBased(0)))
    85  	harness.TestTracer(func() trace.Tracer {
    86  		return tp.Tracer("")
    87  	})
    88  }
    89  
    90  type testExporter struct {
    91  	mu    sync.RWMutex
    92  	idx   map[string]int
    93  	spans []*snapshot
    94  }
    95  
    96  func NewTestExporter() *testExporter {
    97  	return &testExporter{idx: make(map[string]int)}
    98  }
    99  
   100  func (te *testExporter) ExportSpans(_ context.Context, spans []ReadOnlySpan) error {
   101  	te.mu.Lock()
   102  	defer te.mu.Unlock()
   103  
   104  	i := len(te.spans)
   105  	for _, s := range spans {
   106  		te.idx[s.Name()] = i
   107  		te.spans = append(te.spans, s.(*snapshot))
   108  		i++
   109  	}
   110  	return nil
   111  }
   112  
   113  func (te *testExporter) Spans() []*snapshot {
   114  	te.mu.RLock()
   115  	defer te.mu.RUnlock()
   116  
   117  	cp := make([]*snapshot, len(te.spans))
   118  	copy(cp, te.spans)
   119  	return cp
   120  }
   121  
   122  func (te *testExporter) GetSpan(name string) (*snapshot, bool) {
   123  	te.mu.RLock()
   124  	defer te.mu.RUnlock()
   125  	i, ok := te.idx[name]
   126  	if !ok {
   127  		return nil, false
   128  	}
   129  	return te.spans[i], true
   130  }
   131  
   132  func (te *testExporter) Len() int {
   133  	te.mu.RLock()
   134  	defer te.mu.RUnlock()
   135  	return len(te.spans)
   136  }
   137  
   138  func (te *testExporter) Shutdown(context.Context) error {
   139  	te.Reset()
   140  	return nil
   141  }
   142  
   143  func (te *testExporter) Reset() {
   144  	te.mu.Lock()
   145  	defer te.mu.Unlock()
   146  	te.idx = make(map[string]int)
   147  	te.spans = te.spans[:0]
   148  }
   149  
   150  type testSampler struct {
   151  	callCount int
   152  	prefix    string
   153  	t         *testing.T
   154  }
   155  
   156  func (ts *testSampler) ShouldSample(p SamplingParameters) SamplingResult {
   157  	ts.callCount++
   158  	ts.t.Logf("called sampler for name %q", p.Name)
   159  	decision := Drop
   160  	if strings.HasPrefix(p.Name, ts.prefix) {
   161  		decision = RecordAndSample
   162  	}
   163  	return SamplingResult{Decision: decision, Attributes: []attribute.KeyValue{attribute.Int("callCount", ts.callCount)}}
   164  }
   165  
   166  func (ts testSampler) Description() string {
   167  	return "testSampler"
   168  }
   169  
   170  func TestSetName(t *testing.T) {
   171  	tp := NewTracerProvider()
   172  
   173  	type testCase struct {
   174  		name    string
   175  		newName string
   176  	}
   177  	for idx, tt := range []testCase{
   178  		{ // 0
   179  			name:    "foobar",
   180  			newName: "foobaz",
   181  		},
   182  		{ // 1
   183  			name:    "foobar",
   184  			newName: "barbaz",
   185  		},
   186  		{ // 2
   187  			name:    "barbar",
   188  			newName: "barbaz",
   189  		},
   190  		{ // 3
   191  			name:    "barbar",
   192  			newName: "foobar",
   193  		},
   194  	} {
   195  		sp := startNamedSpan(tp, "SetName", tt.name)
   196  		if sdkspan, ok := sp.(*recordingSpan); ok {
   197  			if sdkspan.Name() != tt.name {
   198  				t.Errorf("%d: invalid name at span creation, expected %v, got %v", idx, tt.name, sdkspan.Name())
   199  			}
   200  		} else {
   201  			t.Errorf("%d: unable to coerce span to SDK span, is type %T", idx, sp)
   202  		}
   203  		sp.SetName(tt.newName)
   204  		if sdkspan, ok := sp.(*recordingSpan); ok {
   205  			if sdkspan.Name() != tt.newName {
   206  				t.Errorf("%d: span name not changed, expected %v, got %v", idx, tt.newName, sdkspan.Name())
   207  			}
   208  		} else {
   209  			t.Errorf("%d: unable to coerce span to SDK span, is type %T", idx, sp)
   210  		}
   211  		sp.End()
   212  	}
   213  }
   214  
   215  func TestSpanIsRecording(t *testing.T) {
   216  	t.Run("while Span active", func(t *testing.T) {
   217  		for name, tc := range map[string]struct {
   218  			sampler Sampler
   219  			want    bool
   220  		}{
   221  			"Always sample, recording on": {sampler: AlwaysSample(), want: true},
   222  			"Never sample recording off":  {sampler: NeverSample(), want: false},
   223  		} {
   224  			tp := NewTracerProvider(WithSampler(tc.sampler))
   225  			_, span := tp.Tracer(name).Start(context.Background(), "StartSpan")
   226  			got := span.IsRecording()
   227  			span.End()
   228  			assert.Equal(t, got, tc.want, name)
   229  		}
   230  	})
   231  
   232  	t.Run("after Span end", func(t *testing.T) {
   233  		for name, tc := range map[string]Sampler{
   234  			"Always Sample": AlwaysSample(),
   235  			"Never Sample":  NeverSample(),
   236  		} {
   237  			tp := NewTracerProvider(WithSampler(tc))
   238  			_, span := tp.Tracer(name).Start(context.Background(), "StartSpan")
   239  			span.End()
   240  			got := span.IsRecording()
   241  			assert.False(t, got, name)
   242  		}
   243  	})
   244  }
   245  
   246  func TestSampling(t *testing.T) {
   247  	idg := defaultIDGenerator()
   248  	const total = 10000
   249  	for name, tc := range map[string]struct {
   250  		sampler       Sampler
   251  		expect        float64
   252  		parent        bool
   253  		sampledParent bool
   254  	}{
   255  		// Span w/o a parent
   256  		"NeverSample":           {sampler: NeverSample(), expect: 0},
   257  		"AlwaysSample":          {sampler: AlwaysSample(), expect: 1.0},
   258  		"TraceIdRatioBased_-1":  {sampler: TraceIDRatioBased(-1.0), expect: 0},
   259  		"TraceIdRatioBased_.25": {sampler: TraceIDRatioBased(0.25), expect: .25},
   260  		"TraceIdRatioBased_.50": {sampler: TraceIDRatioBased(0.50), expect: .5},
   261  		"TraceIdRatioBased_.75": {sampler: TraceIDRatioBased(0.75), expect: .75},
   262  		"TraceIdRatioBased_2.0": {sampler: TraceIDRatioBased(2.0), expect: 1},
   263  
   264  		// Spans w/o a parent and using ParentBased(DelegateSampler()) Sampler, receive DelegateSampler's sampling decision
   265  		"ParentNeverSample":           {sampler: ParentBased(NeverSample()), expect: 0},
   266  		"ParentAlwaysSample":          {sampler: ParentBased(AlwaysSample()), expect: 1},
   267  		"ParentTraceIdRatioBased_.50": {sampler: ParentBased(TraceIDRatioBased(0.50)), expect: .5},
   268  
   269  		// An unadorned TraceIDRatioBased sampler ignores parent spans
   270  		"UnsampledParentSpanWithTraceIdRatioBased_.25": {sampler: TraceIDRatioBased(0.25), expect: .25, parent: true},
   271  		"SampledParentSpanWithTraceIdRatioBased_.25":   {sampler: TraceIDRatioBased(0.25), expect: .25, parent: true, sampledParent: true},
   272  		"UnsampledParentSpanWithTraceIdRatioBased_.50": {sampler: TraceIDRatioBased(0.50), expect: .5, parent: true},
   273  		"SampledParentSpanWithTraceIdRatioBased_.50":   {sampler: TraceIDRatioBased(0.50), expect: .5, parent: true, sampledParent: true},
   274  		"UnsampledParentSpanWithTraceIdRatioBased_.75": {sampler: TraceIDRatioBased(0.75), expect: .75, parent: true},
   275  		"SampledParentSpanWithTraceIdRatioBased_.75":   {sampler: TraceIDRatioBased(0.75), expect: .75, parent: true, sampledParent: true},
   276  
   277  		// Spans with a sampled parent but using NeverSample Sampler, are not sampled
   278  		"SampledParentSpanWithNeverSample": {sampler: NeverSample(), expect: 0, parent: true, sampledParent: true},
   279  
   280  		// Spans with a sampled parent and using ParentBased(DelegateSampler()) Sampler, inherit the parent span's sampling status
   281  		"SampledParentSpanWithParentNeverSample":             {sampler: ParentBased(NeverSample()), expect: 1, parent: true, sampledParent: true},
   282  		"UnsampledParentSpanWithParentNeverSampler":          {sampler: ParentBased(NeverSample()), expect: 0, parent: true, sampledParent: false},
   283  		"SampledParentSpanWithParentAlwaysSampler":           {sampler: ParentBased(AlwaysSample()), expect: 1, parent: true, sampledParent: true},
   284  		"UnsampledParentSpanWithParentAlwaysSampler":         {sampler: ParentBased(AlwaysSample()), expect: 0, parent: true, sampledParent: false},
   285  		"SampledParentSpanWithParentTraceIdRatioBased_.50":   {sampler: ParentBased(TraceIDRatioBased(0.50)), expect: 1, parent: true, sampledParent: true},
   286  		"UnsampledParentSpanWithParentTraceIdRatioBased_.50": {sampler: ParentBased(TraceIDRatioBased(0.50)), expect: 0, parent: true, sampledParent: false},
   287  	} {
   288  		tc := tc
   289  		t.Run(name, func(t *testing.T) {
   290  			t.Parallel()
   291  			p := NewTracerProvider(WithSampler(tc.sampler))
   292  			tr := p.Tracer("test")
   293  			var sampled int
   294  			for i := 0; i < total; i++ {
   295  				ctx := context.Background()
   296  				if tc.parent {
   297  					tid, sid := idg.NewIDs(ctx)
   298  					psc := trace.NewSpanContext(trace.SpanContextConfig{
   299  						TraceID: tid,
   300  						SpanID:  sid,
   301  					})
   302  					if tc.sampledParent {
   303  						psc = psc.WithTraceFlags(trace.FlagsSampled)
   304  					}
   305  					ctx = trace.ContextWithRemoteSpanContext(ctx, psc)
   306  				}
   307  				_, span := tr.Start(ctx, "test")
   308  				if span.SpanContext().IsSampled() {
   309  					sampled++
   310  				}
   311  			}
   312  			tolerance := 0.0
   313  			got := float64(sampled) / float64(total)
   314  
   315  			if tc.expect > 0 && tc.expect < 1 {
   316  				// See https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
   317  				const z = 4.75342 // This should succeed 99.9999% of the time
   318  				tolerance = z * math.Sqrt(got*(1-got)/total)
   319  			}
   320  
   321  			diff := math.Abs(got - tc.expect)
   322  			if diff > tolerance {
   323  				t.Errorf("got %f (diff: %f), expected %f (w/tolerance: %f)", got, diff, tc.expect, tolerance)
   324  			}
   325  		})
   326  	}
   327  }
   328  
   329  func TestStartSpanWithParent(t *testing.T) {
   330  	tp := NewTracerProvider()
   331  	tr := tp.Tracer("SpanWithParent")
   332  	ctx := context.Background()
   333  
   334  	_, s1 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span1-unsampled-parent1")
   335  	if err := checkChild(t, sc, s1); err != nil {
   336  		t.Error(err)
   337  	}
   338  
   339  	_, s2 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span2-unsampled-parent1")
   340  	if err := checkChild(t, sc, s2); err != nil {
   341  		t.Error(err)
   342  	}
   343  
   344  	ts, err := trace.ParseTraceState("k=v")
   345  	if err != nil {
   346  		t.Error(err)
   347  	}
   348  	sc2 := sc.WithTraceState(ts)
   349  	_, s3 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc2), "span3-sampled-parent2")
   350  	if err := checkChild(t, sc2, s3); err != nil {
   351  		t.Error(err)
   352  	}
   353  
   354  	ctx2, s4 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc2), "span4-sampled-parent2")
   355  	if err := checkChild(t, sc2, s4); err != nil {
   356  		t.Error(err)
   357  	}
   358  
   359  	s4Sc := s4.SpanContext()
   360  	_, s5 := tr.Start(ctx2, "span5-implicit-childof-span4")
   361  	if err := checkChild(t, s4Sc, s5); err != nil {
   362  		t.Error(err)
   363  	}
   364  }
   365  
   366  // Test we get a successful span as a new root if a nil context is sent in, as opposed to a panic.
   367  // See https://github.com/open-telemetry/opentelemetry-go/issues/3109
   368  func TestStartSpanWithNilContext(t *testing.T) {
   369  	tp := NewTracerProvider()
   370  	tr := tp.Tracer("NoPanic")
   371  
   372  	// nolint:staticcheck // no nil context, but that's the point of the test.
   373  	assert.NotPanics(t, func() { tr.Start(nil, "should-not-panic") })
   374  }
   375  
   376  func TestStartSpanNewRootNotSampled(t *testing.T) {
   377  	alwaysSampleTp := NewTracerProvider()
   378  	sampledTr := alwaysSampleTp.Tracer("AlwaysSampled")
   379  	neverSampleTp := NewTracerProvider(WithSampler(ParentBased(NeverSample())))
   380  	neverSampledTr := neverSampleTp.Tracer("ParentBasedNeverSample")
   381  	ctx := context.Background()
   382  
   383  	ctx, s1 := sampledTr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "span1-sampled")
   384  	if err := checkChild(t, sc, s1); err != nil {
   385  		t.Error(err)
   386  	}
   387  
   388  	_, s2 := neverSampledTr.Start(ctx, "span2-no-newroot")
   389  	if !s2.SpanContext().IsSampled() {
   390  		t.Error(fmt.Errorf("got child span is not sampled, want child span with sampler: ParentBased(NeverSample()) to be sampled"))
   391  	}
   392  
   393  	// Adding WithNewRoot causes child spans to not sample based on parent context
   394  	_, s3 := neverSampledTr.Start(ctx, "span3-newroot", trace.WithNewRoot())
   395  	if s3.SpanContext().IsSampled() {
   396  		t.Error(fmt.Errorf("got child span is sampled, want child span WithNewRoot() and with sampler: ParentBased(NeverSample()) to not be sampled"))
   397  	}
   398  }
   399  
   400  func TestSetSpanAttributesOnStart(t *testing.T) {
   401  	te := NewTestExporter()
   402  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
   403  	span := startSpan(tp,
   404  		"StartSpanAttribute",
   405  		trace.WithAttributes(attribute.String("key1", "value1")),
   406  		trace.WithAttributes(attribute.String("key2", "value2")),
   407  	)
   408  	got, err := endSpan(te, span)
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  
   413  	want := &snapshot{
   414  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   415  			TraceID:    tid,
   416  			TraceFlags: 0x1,
   417  		}),
   418  		parent: sc.WithRemote(true),
   419  		name:   "span0",
   420  		attributes: []attribute.KeyValue{
   421  			attribute.String("key1", "value1"),
   422  			attribute.String("key2", "value2"),
   423  		},
   424  		spanKind:             trace.SpanKindInternal,
   425  		instrumentationScope: instrumentation.Scope{Name: "StartSpanAttribute"},
   426  	}
   427  	if diff := cmpDiff(got, want); diff != "" {
   428  		t.Errorf("SetSpanAttributesOnStart: -got +want %s", diff)
   429  	}
   430  }
   431  
   432  func TestSamplerAttributesLocalChildSpan(t *testing.T) {
   433  	sampler := &testSampler{prefix: "span", t: t}
   434  	te := NewTestExporter()
   435  	tp := NewTracerProvider(WithSampler(sampler), WithSyncer(te), WithResource(resource.Empty()))
   436  
   437  	ctx := context.Background()
   438  	ctx, span := startLocalSpan(ctx, tp, "SpanOne", "span0")
   439  	_, spanTwo := startLocalSpan(ctx, tp, "SpanTwo", "span1")
   440  
   441  	spanTwo.End()
   442  	span.End()
   443  
   444  	got := te.Spans()
   445  	require.Len(t, got, 2)
   446  	// FILO order above means spanTwo <-> gotSpan0 and span <-> gotSpan1.
   447  	gotSpan0, gotSpan1 := got[0], got[1]
   448  	// Ensure sampler is called for local child spans by verifying the
   449  	// attributes set by the sampler are set on the child span.
   450  	assert.Equal(t, []attribute.KeyValue{attribute.Int("callCount", 2)}, gotSpan0.Attributes())
   451  	assert.Equal(t, []attribute.KeyValue{attribute.Int("callCount", 1)}, gotSpan1.Attributes())
   452  }
   453  
   454  func TestSpanSetAttributes(t *testing.T) {
   455  	attrs := [...]attribute.KeyValue{
   456  		attribute.String("key1", "value1"),
   457  		attribute.String("key2", "value2"),
   458  		attribute.String("key3", "value3"),
   459  		attribute.String("key4", "value4"),
   460  		attribute.String("key1", "value5"),
   461  		attribute.String("key2", "value6"),
   462  		attribute.String("key3", "value7"),
   463  	}
   464  	invalid := attribute.KeyValue{}
   465  
   466  	tests := []struct {
   467  		name        string
   468  		input       [][]attribute.KeyValue
   469  		wantAttrs   []attribute.KeyValue
   470  		wantDropped int
   471  	}{
   472  		{
   473  			name:      "array",
   474  			input:     [][]attribute.KeyValue{attrs[:3]},
   475  			wantAttrs: attrs[:3],
   476  		},
   477  		{
   478  			name:      "single_value:array",
   479  			input:     [][]attribute.KeyValue{attrs[:1], attrs[1:3]},
   480  			wantAttrs: attrs[:3],
   481  		},
   482  		{
   483  			name:      "array:single_value",
   484  			input:     [][]attribute.KeyValue{attrs[:2], attrs[2:3]},
   485  			wantAttrs: attrs[:3],
   486  		},
   487  		{
   488  			name:      "single_values",
   489  			input:     [][]attribute.KeyValue{attrs[:1], attrs[1:2], attrs[2:3]},
   490  			wantAttrs: attrs[:3],
   491  		},
   492  
   493  		// The tracing specification states:
   494  		//
   495  		//   For each unique attribute key, addition of which would result in
   496  		//   exceeding the limit, SDK MUST discard that key/value pair
   497  		//
   498  		// Therefore, adding attributes after the capacity is reached should
   499  		// result in those attributes being dropped.
   500  
   501  		{
   502  			name:        "drop_last_added",
   503  			input:       [][]attribute.KeyValue{attrs[:3], attrs[3:4], attrs[3:4]},
   504  			wantAttrs:   attrs[:3],
   505  			wantDropped: 2,
   506  		},
   507  
   508  		// The tracing specification states:
   509  		//
   510  		//   Setting an attribute with the same key as an existing attribute
   511  		//   SHOULD overwrite the existing attribute's value.
   512  		//
   513  		// Therefore, attributes are updated regardless of capacity state.
   514  
   515  		{
   516  			name:      "single_value_update",
   517  			input:     [][]attribute.KeyValue{attrs[:1], attrs[:3]},
   518  			wantAttrs: attrs[:3],
   519  		},
   520  		{
   521  			name:      "all_update",
   522  			input:     [][]attribute.KeyValue{attrs[:3], attrs[4:7]},
   523  			wantAttrs: attrs[4:7],
   524  		},
   525  		{
   526  			name:      "all_update/multi",
   527  			input:     [][]attribute.KeyValue{attrs[:3], attrs[4:7], attrs[:3]},
   528  			wantAttrs: attrs[:3],
   529  		},
   530  		{
   531  			name:      "deduplicate/under_capacity",
   532  			input:     [][]attribute.KeyValue{attrs[:1], attrs[:1], attrs[:1]},
   533  			wantAttrs: attrs[:1],
   534  		},
   535  		{
   536  			name:      "deduplicate/over_capacity",
   537  			input:     [][]attribute.KeyValue{attrs[:1], attrs[:1], attrs[:1], attrs[:3]},
   538  			wantAttrs: attrs[:3],
   539  		},
   540  		{
   541  			name: "deduplicate/added",
   542  			input: [][]attribute.KeyValue{
   543  				attrs[:2],
   544  				{attrs[2], attrs[2], attrs[2]},
   545  			},
   546  			wantAttrs: attrs[:3],
   547  		},
   548  		{
   549  			name: "deduplicate/added_at_cappacity",
   550  			input: [][]attribute.KeyValue{
   551  				attrs[:3],
   552  				{attrs[2], attrs[2], attrs[2]},
   553  			},
   554  			wantAttrs: attrs[:3],
   555  		},
   556  		{
   557  			name: "invalid",
   558  			input: [][]attribute.KeyValue{
   559  				{invalid},
   560  			},
   561  			wantDropped: 1,
   562  		},
   563  		{
   564  			name: "invalid_with_valid",
   565  			input: [][]attribute.KeyValue{
   566  				{invalid, attrs[0]},
   567  			},
   568  			wantAttrs:   attrs[:1],
   569  			wantDropped: 1,
   570  		},
   571  		{
   572  			name: "invalid_over_capacity",
   573  			input: [][]attribute.KeyValue{
   574  				{invalid, invalid, invalid, invalid, attrs[0]},
   575  			},
   576  			wantAttrs:   attrs[:1],
   577  			wantDropped: 4,
   578  		},
   579  		{
   580  			name: "valid:invalid/under_capacity",
   581  			input: [][]attribute.KeyValue{
   582  				attrs[:1],
   583  				{invalid},
   584  			},
   585  			wantAttrs:   attrs[:1],
   586  			wantDropped: 1,
   587  		},
   588  		{
   589  			name: "valid:invalid/over_capacity",
   590  			input: [][]attribute.KeyValue{
   591  				attrs[:1],
   592  				{invalid, invalid, invalid, invalid},
   593  			},
   594  			wantAttrs:   attrs[:1],
   595  			wantDropped: 4,
   596  		},
   597  		{
   598  			name: "valid_at_capacity:invalid",
   599  			input: [][]attribute.KeyValue{
   600  				attrs[:3],
   601  				{invalid, invalid, invalid, invalid},
   602  			},
   603  			wantAttrs:   attrs[:3],
   604  			wantDropped: 4,
   605  		},
   606  	}
   607  
   608  	const (
   609  		capacity = 3
   610  		instName = "TestSpanAttributeCapacity"
   611  		spanName = "test span"
   612  	)
   613  
   614  	for _, test := range tests {
   615  		t.Run(test.name, func(t *testing.T) {
   616  			te := NewTestExporter()
   617  			sl := NewSpanLimits()
   618  			sl.AttributeCountLimit = capacity
   619  			tp := NewTracerProvider(WithSyncer(te), WithSpanLimits(sl))
   620  			_, span := tp.Tracer(instName).Start(context.Background(), spanName)
   621  			for _, a := range test.input {
   622  				span.SetAttributes(a...)
   623  			}
   624  			span.End()
   625  
   626  			require.Implements(t, (*ReadOnlySpan)(nil), span)
   627  			roSpan := span.(ReadOnlySpan)
   628  
   629  			// Ensure the span itself is valid.
   630  			assert.ElementsMatch(t, test.wantAttrs, roSpan.Attributes(), "expected attributes")
   631  			assert.Equal(t, test.wantDropped, roSpan.DroppedAttributes(), "dropped attributes")
   632  
   633  			snap, ok := te.GetSpan(spanName)
   634  			require.Truef(t, ok, "span %s not exported", spanName)
   635  
   636  			// Ensure the exported span snapshot is valid.
   637  			assert.ElementsMatch(t, test.wantAttrs, snap.Attributes(), "expected attributes")
   638  			assert.Equal(t, test.wantDropped, snap.DroppedAttributes(), "dropped attributes")
   639  		})
   640  	}
   641  }
   642  
   643  func TestEvents(t *testing.T) {
   644  	te := NewTestExporter()
   645  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
   646  
   647  	span := startSpan(tp, "Events")
   648  	k1v1 := attribute.String("key1", "value1")
   649  	k2v2 := attribute.Bool("key2", true)
   650  	k3v3 := attribute.Int64("key3", 3)
   651  
   652  	span.AddEvent("foo", trace.WithAttributes(attribute.String("key1", "value1")))
   653  	span.AddEvent("bar", trace.WithAttributes(
   654  		attribute.Bool("key2", true),
   655  		attribute.Int64("key3", 3),
   656  	))
   657  	got, err := endSpan(te, span)
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  
   662  	for i := range got.Events() {
   663  		if !checkTime(&got.Events()[i].Time) {
   664  			t.Error("exporting span: expected nonzero Event Time")
   665  		}
   666  	}
   667  
   668  	want := &snapshot{
   669  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   670  			TraceID:    tid,
   671  			TraceFlags: 0x1,
   672  		}),
   673  		parent: sc.WithRemote(true),
   674  		name:   "span0",
   675  		events: []Event{
   676  			{Name: "foo", Attributes: []attribute.KeyValue{k1v1}},
   677  			{Name: "bar", Attributes: []attribute.KeyValue{k2v2, k3v3}},
   678  		},
   679  		spanKind:             trace.SpanKindInternal,
   680  		instrumentationScope: instrumentation.Scope{Name: "Events"},
   681  	}
   682  	if diff := cmpDiff(got, want); diff != "" {
   683  		t.Errorf("Message Events: -got +want %s", diff)
   684  	}
   685  }
   686  
   687  func TestEventsOverLimit(t *testing.T) {
   688  	te := NewTestExporter()
   689  	sl := NewSpanLimits()
   690  	sl.EventCountLimit = 2
   691  	tp := NewTracerProvider(WithSpanLimits(sl), WithSyncer(te), WithResource(resource.Empty()))
   692  
   693  	span := startSpan(tp, "EventsOverLimit")
   694  	k1v1 := attribute.String("key1", "value1")
   695  	k2v2 := attribute.Bool("key2", false)
   696  	k3v3 := attribute.String("key3", "value3")
   697  
   698  	span.AddEvent("fooDrop", trace.WithAttributes(attribute.String("key1", "value1")))
   699  	span.AddEvent("barDrop", trace.WithAttributes(
   700  		attribute.Bool("key2", true),
   701  		attribute.String("key3", "value3"),
   702  	))
   703  	span.AddEvent("foo", trace.WithAttributes(attribute.String("key1", "value1")))
   704  	span.AddEvent("bar", trace.WithAttributes(
   705  		attribute.Bool("key2", false),
   706  		attribute.String("key3", "value3"),
   707  	))
   708  	got, err := endSpan(te, span)
   709  	if err != nil {
   710  		t.Fatal(err)
   711  	}
   712  
   713  	for i := range got.Events() {
   714  		if !checkTime(&got.Events()[i].Time) {
   715  			t.Error("exporting span: expected nonzero Event Time")
   716  		}
   717  	}
   718  
   719  	want := &snapshot{
   720  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   721  			TraceID:    tid,
   722  			TraceFlags: 0x1,
   723  		}),
   724  		parent: sc.WithRemote(true),
   725  		name:   "span0",
   726  		events: []Event{
   727  			{Name: "foo", Attributes: []attribute.KeyValue{k1v1}},
   728  			{Name: "bar", Attributes: []attribute.KeyValue{k2v2, k3v3}},
   729  		},
   730  		droppedEventCount:    2,
   731  		spanKind:             trace.SpanKindInternal,
   732  		instrumentationScope: instrumentation.Scope{Name: "EventsOverLimit"},
   733  	}
   734  	if diff := cmpDiff(got, want); diff != "" {
   735  		t.Errorf("Message Event over limit: -got +want %s", diff)
   736  	}
   737  }
   738  
   739  func TestLinks(t *testing.T) {
   740  	te := NewTestExporter()
   741  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
   742  
   743  	k1v1 := attribute.String("key1", "value1")
   744  	k2v2 := attribute.String("key2", "value2")
   745  	k3v3 := attribute.String("key3", "value3")
   746  
   747  	sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
   748  	sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
   749  
   750  	l1 := trace.Link{SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1}}
   751  	l2 := trace.Link{SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2, k3v3}}
   752  
   753  	links := []trace.Link{l1, l2}
   754  	span := startSpan(tp, "Links", trace.WithLinks(links...))
   755  
   756  	got, err := endSpan(te, span)
   757  	if err != nil {
   758  		t.Fatal(err)
   759  	}
   760  
   761  	want := &snapshot{
   762  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   763  			TraceID:    tid,
   764  			TraceFlags: 0x1,
   765  		}),
   766  		parent:               sc.WithRemote(true),
   767  		name:                 "span0",
   768  		links:                []Link{{l1.SpanContext, l1.Attributes, 0}, {l2.SpanContext, l2.Attributes, 0}},
   769  		spanKind:             trace.SpanKindInternal,
   770  		instrumentationScope: instrumentation.Scope{Name: "Links"},
   771  	}
   772  	if diff := cmpDiff(got, want); diff != "" {
   773  		t.Errorf("Link: -got +want %s", diff)
   774  	}
   775  	sc1 = trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
   776  
   777  	span1 := startSpan(tp, "name", trace.WithLinks([]trace.Link{
   778  		{SpanContext: trace.SpanContext{}},
   779  		{SpanContext: sc1},
   780  	}...))
   781  
   782  	sdkspan, _ := span1.(*recordingSpan)
   783  	require.Len(t, sdkspan.Links(), 1)
   784  }
   785  
   786  func TestLinksOverLimit(t *testing.T) {
   787  	te := NewTestExporter()
   788  
   789  	sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
   790  	sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
   791  	sc3 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
   792  
   793  	sl := NewSpanLimits()
   794  	sl.LinkCountLimit = 2
   795  	tp := NewTracerProvider(WithSpanLimits(sl), WithSyncer(te), WithResource(resource.Empty()))
   796  
   797  	span := startSpan(tp, "LinksOverLimit",
   798  		trace.WithLinks(
   799  			trace.Link{SpanContext: sc1, Attributes: []attribute.KeyValue{attribute.String("key1", "value1")}},
   800  			trace.Link{SpanContext: sc2, Attributes: []attribute.KeyValue{attribute.String("key2", "value2")}},
   801  			trace.Link{SpanContext: sc3, Attributes: []attribute.KeyValue{attribute.String("key3", "value3")}},
   802  		),
   803  	)
   804  
   805  	k2v2 := attribute.String("key2", "value2")
   806  	k3v3 := attribute.String("key3", "value3")
   807  
   808  	got, err := endSpan(te, span)
   809  	if err != nil {
   810  		t.Fatal(err)
   811  	}
   812  
   813  	want := &snapshot{
   814  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   815  			TraceID:    tid,
   816  			TraceFlags: 0x1,
   817  		}),
   818  		parent: sc.WithRemote(true),
   819  		name:   "span0",
   820  		links: []Link{
   821  			{SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2}, DroppedAttributeCount: 0},
   822  			{SpanContext: sc3, Attributes: []attribute.KeyValue{k3v3}, DroppedAttributeCount: 0},
   823  		},
   824  		droppedLinkCount:     1,
   825  		spanKind:             trace.SpanKindInternal,
   826  		instrumentationScope: instrumentation.Scope{Name: "LinksOverLimit"},
   827  	}
   828  	if diff := cmpDiff(got, want); diff != "" {
   829  		t.Errorf("Link over limit: -got +want %s", diff)
   830  	}
   831  }
   832  
   833  func TestSetSpanName(t *testing.T) {
   834  	te := NewTestExporter()
   835  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
   836  	ctx := context.Background()
   837  
   838  	want := "SpanName-1"
   839  	ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
   840  	_, span := tp.Tracer("SetSpanName").Start(ctx, "SpanName-1")
   841  	got, err := endSpan(te, span)
   842  	if err != nil {
   843  		t.Fatal(err)
   844  	}
   845  
   846  	if got.Name() != want {
   847  		t.Errorf("span.Name: got %q; want %q", got.Name(), want)
   848  	}
   849  }
   850  
   851  func TestSetSpanStatus(t *testing.T) {
   852  	te := NewTestExporter()
   853  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
   854  
   855  	span := startSpan(tp, "SpanStatus")
   856  	span.SetStatus(codes.Error, "Error")
   857  	got, err := endSpan(te, span)
   858  	if err != nil {
   859  		t.Fatal(err)
   860  	}
   861  
   862  	want := &snapshot{
   863  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   864  			TraceID:    tid,
   865  			TraceFlags: 0x1,
   866  		}),
   867  		parent:   sc.WithRemote(true),
   868  		name:     "span0",
   869  		spanKind: trace.SpanKindInternal,
   870  		status: Status{
   871  			Code:        codes.Error,
   872  			Description: "Error",
   873  		},
   874  		instrumentationScope: instrumentation.Scope{Name: "SpanStatus"},
   875  	}
   876  	if diff := cmpDiff(got, want); diff != "" {
   877  		t.Errorf("SetSpanStatus: -got +want %s", diff)
   878  	}
   879  }
   880  
   881  func TestSetSpanStatusWithoutMessageWhenStatusIsNotError(t *testing.T) {
   882  	te := NewTestExporter()
   883  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
   884  
   885  	span := startSpan(tp, "SpanStatus")
   886  	span.SetStatus(codes.Ok, "This message will be ignored")
   887  	got, err := endSpan(te, span)
   888  	if err != nil {
   889  		t.Fatal(err)
   890  	}
   891  
   892  	want := &snapshot{
   893  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
   894  			TraceID:    tid,
   895  			TraceFlags: 0x1,
   896  		}),
   897  		parent:   sc.WithRemote(true),
   898  		name:     "span0",
   899  		spanKind: trace.SpanKindInternal,
   900  		status: Status{
   901  			Code:        codes.Ok,
   902  			Description: "",
   903  		},
   904  		instrumentationScope: instrumentation.Scope{Name: "SpanStatus"},
   905  	}
   906  	if diff := cmpDiff(got, want); diff != "" {
   907  		t.Errorf("SetSpanStatus: -got +want %s", diff)
   908  	}
   909  }
   910  
   911  func cmpDiff(x, y interface{}) string {
   912  	return cmp.Diff(x, y,
   913  		cmp.AllowUnexported(snapshot{}),
   914  		cmp.AllowUnexported(attribute.Value{}),
   915  		cmp.AllowUnexported(Event{}),
   916  		cmp.AllowUnexported(trace.TraceState{}))
   917  }
   918  
   919  // checkChild is test utility function that tests that c has fields set appropriately,
   920  // given that it is a child span of p.
   921  func checkChild(t *testing.T, p trace.SpanContext, apiSpan trace.Span) error {
   922  	s := apiSpan.(*recordingSpan)
   923  	if s == nil {
   924  		return fmt.Errorf("got nil child span, want non-nil")
   925  	}
   926  	if got, want := s.spanContext.TraceID().String(), p.TraceID().String(); got != want {
   927  		return fmt.Errorf("got child trace ID %s, want %s", got, want)
   928  	}
   929  	if childID, parentID := s.spanContext.SpanID().String(), p.SpanID().String(); childID == parentID {
   930  		return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID)
   931  	}
   932  	if got, want := s.spanContext.TraceFlags(), p.TraceFlags(); got != want {
   933  		return fmt.Errorf("got child trace options %d, want %d", got, want)
   934  	}
   935  	got, want := s.spanContext.TraceState(), p.TraceState()
   936  	assert.Equal(t, want, got)
   937  	return nil
   938  }
   939  
   940  // startSpan starts a span with a name "span0". See startNamedSpan for
   941  // details.
   942  func startSpan(tp *TracerProvider, trName string, args ...trace.SpanStartOption) trace.Span {
   943  	return startNamedSpan(tp, trName, "span0", args...)
   944  }
   945  
   946  // startNamed Span is a test utility func that starts a span with a
   947  // passed name and with remote span context as parent. The remote span
   948  // context contains TraceFlags with sampled bit set. This allows the
   949  // span to be automatically sampled.
   950  func startNamedSpan(tp *TracerProvider, trName, name string, args ...trace.SpanStartOption) trace.Span {
   951  	_, span := tp.Tracer(trName).Start(
   952  		trace.ContextWithRemoteSpanContext(context.Background(), sc),
   953  		name,
   954  		args...,
   955  	)
   956  	return span
   957  }
   958  
   959  // startLocalSpan is a test utility func that starts a span with a
   960  // passed name and with the passed context. The context is returned
   961  // along with the span so this parent can be used to create child
   962  // spans.
   963  func startLocalSpan(ctx context.Context, tp *TracerProvider, trName, name string, args ...trace.SpanStartOption) (context.Context, trace.Span) {
   964  	ctx, span := tp.Tracer(trName).Start(
   965  		ctx,
   966  		name,
   967  		args...,
   968  	)
   969  	return ctx, span
   970  }
   971  
   972  // endSpan is a test utility function that ends the span in the context and
   973  // returns the exported span.
   974  // It requires that span be sampled using one of these methods
   975  //  1. Passing parent span context in context
   976  //  2. Use WithSampler(AlwaysSample())
   977  //  3. Configuring AlwaysSample() as default sampler
   978  //
   979  // It also does some basic tests on the span.
   980  // It also clears spanID in the to make the comparison easier.
   981  func endSpan(te *testExporter, span trace.Span) (*snapshot, error) {
   982  	if !span.IsRecording() {
   983  		return nil, fmt.Errorf("method IsRecording: got false, want true")
   984  	}
   985  	if !span.SpanContext().IsSampled() {
   986  		return nil, fmt.Errorf("method IsSampled: got false, want true")
   987  	}
   988  	span.End()
   989  	if te.Len() != 1 {
   990  		return nil, fmt.Errorf("got %d exported spans, want one span", te.Len())
   991  	}
   992  	got := te.Spans()[0]
   993  	if !got.SpanContext().SpanID().IsValid() {
   994  		return nil, fmt.Errorf("exporting span: expected nonzero SpanID")
   995  	}
   996  	got.spanContext = got.SpanContext().WithSpanID(trace.SpanID{})
   997  	if !checkTime(&got.startTime) {
   998  		return nil, fmt.Errorf("exporting span: expected nonzero StartTime")
   999  	}
  1000  	if !checkTime(&got.endTime) {
  1001  		return nil, fmt.Errorf("exporting span: expected nonzero EndTime")
  1002  	}
  1003  	return got, nil
  1004  }
  1005  
  1006  // checkTime checks that a nonzero time was set in x, then clears it.
  1007  func checkTime(x *time.Time) bool {
  1008  	if x.IsZero() {
  1009  		return false
  1010  	}
  1011  	*x = time.Time{}
  1012  	return true
  1013  }
  1014  
  1015  func TestEndSpanTwice(t *testing.T) {
  1016  	te := NewTestExporter()
  1017  	tp := NewTracerProvider(WithSyncer(te))
  1018  
  1019  	st := time.Now()
  1020  	et1 := st.Add(100 * time.Millisecond)
  1021  	et2 := st.Add(200 * time.Millisecond)
  1022  
  1023  	span := startSpan(tp, "EndSpanTwice", trace.WithTimestamp(st))
  1024  	span.End(trace.WithTimestamp(et1))
  1025  	span.End(trace.WithTimestamp(et2))
  1026  
  1027  	if te.Len() != 1 {
  1028  		t.Fatalf("expected only a single span, got %#v", te.Spans())
  1029  	}
  1030  
  1031  	ro := span.(ReadOnlySpan)
  1032  	if ro.EndTime() != et1 {
  1033  		t.Fatalf("2nd call to End() should not modify end time")
  1034  	}
  1035  }
  1036  
  1037  func TestStartSpanAfterEnd(t *testing.T) {
  1038  	te := NewTestExporter()
  1039  	tp := NewTracerProvider(WithSampler(AlwaysSample()), WithSyncer(te))
  1040  	ctx := context.Background()
  1041  
  1042  	tr := tp.Tracer("SpanAfterEnd")
  1043  	ctx, span0 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc), "parent")
  1044  	ctx1, span1 := tr.Start(ctx, "span-1")
  1045  	span1.End()
  1046  	// Start a new span with the context containing span-1
  1047  	// even though span-1 is ended, we still add this as a new child of span-1
  1048  	_, span2 := tr.Start(ctx1, "span-2")
  1049  	span2.End()
  1050  	span0.End()
  1051  	if got, want := te.Len(), 3; got != want {
  1052  		t.Fatalf("len(%#v) = %d; want %d", te.Spans(), got, want)
  1053  	}
  1054  
  1055  	gotParent, ok := te.GetSpan("parent")
  1056  	if !ok {
  1057  		t.Fatal("parent not recorded")
  1058  	}
  1059  	gotSpan1, ok := te.GetSpan("span-1")
  1060  	if !ok {
  1061  		t.Fatal("span-1 not recorded")
  1062  	}
  1063  	gotSpan2, ok := te.GetSpan("span-2")
  1064  	if !ok {
  1065  		t.Fatal("span-2 not recorded")
  1066  	}
  1067  
  1068  	if got, want := gotSpan1.SpanContext().TraceID(), gotParent.SpanContext().TraceID(); got != want {
  1069  		t.Errorf("span-1.TraceID=%q; want %q", got, want)
  1070  	}
  1071  	if got, want := gotSpan2.SpanContext().TraceID(), gotParent.SpanContext().TraceID(); got != want {
  1072  		t.Errorf("span-2.TraceID=%q; want %q", got, want)
  1073  	}
  1074  	if got, want := gotSpan1.Parent().SpanID(), gotParent.SpanContext().SpanID(); got != want {
  1075  		t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want)
  1076  	}
  1077  	if got, want := gotSpan2.Parent().SpanID(), gotSpan1.SpanContext().SpanID(); got != want {
  1078  		t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want)
  1079  	}
  1080  }
  1081  
  1082  func TestChildSpanCount(t *testing.T) {
  1083  	te := NewTestExporter()
  1084  	tp := NewTracerProvider(WithSampler(AlwaysSample()), WithSyncer(te))
  1085  
  1086  	tr := tp.Tracer("ChidSpanCount")
  1087  	ctx, span0 := tr.Start(context.Background(), "parent")
  1088  	ctx1, span1 := tr.Start(ctx, "span-1")
  1089  	_, span2 := tr.Start(ctx1, "span-2")
  1090  	span2.End()
  1091  	span1.End()
  1092  
  1093  	_, span3 := tr.Start(ctx, "span-3")
  1094  	span3.End()
  1095  	span0.End()
  1096  	if got, want := te.Len(), 4; got != want {
  1097  		t.Fatalf("len(%#v) = %d; want %d", te.Spans(), got, want)
  1098  	}
  1099  
  1100  	gotParent, ok := te.GetSpan("parent")
  1101  	if !ok {
  1102  		t.Fatal("parent not recorded")
  1103  	}
  1104  	gotSpan1, ok := te.GetSpan("span-1")
  1105  	if !ok {
  1106  		t.Fatal("span-1 not recorded")
  1107  	}
  1108  	gotSpan2, ok := te.GetSpan("span-2")
  1109  	if !ok {
  1110  		t.Fatal("span-2 not recorded")
  1111  	}
  1112  	gotSpan3, ok := te.GetSpan("span-3")
  1113  	if !ok {
  1114  		t.Fatal("span-3 not recorded")
  1115  	}
  1116  
  1117  	if got, want := gotSpan3.ChildSpanCount(), 0; got != want {
  1118  		t.Errorf("span-3.ChildSpanCount=%d; want %d", got, want)
  1119  	}
  1120  	if got, want := gotSpan2.ChildSpanCount(), 0; got != want {
  1121  		t.Errorf("span-2.ChildSpanCount=%d; want %d", got, want)
  1122  	}
  1123  	if got, want := gotSpan1.ChildSpanCount(), 1; got != want {
  1124  		t.Errorf("span-1.ChildSpanCount=%d; want %d", got, want)
  1125  	}
  1126  	if got, want := gotParent.ChildSpanCount(), 2; got != want {
  1127  		t.Errorf("parent.ChildSpanCount=%d; want %d", got, want)
  1128  	}
  1129  }
  1130  
  1131  func TestNilSpanEnd(t *testing.T) {
  1132  	var span *recordingSpan
  1133  	span.End()
  1134  }
  1135  
  1136  func TestNonRecordingSpanDoesNotTrackRuntimeTracerTask(t *testing.T) {
  1137  	tp := NewTracerProvider(WithSampler(NeverSample()))
  1138  	tr := tp.Tracer("TestNonRecordingSpanDoesNotTrackRuntimeTracerTask")
  1139  
  1140  	_, apiSpan := tr.Start(context.Background(), "foo")
  1141  	if _, ok := apiSpan.(runtimeTracer); ok {
  1142  		t.Fatalf("non recording span implements runtime trace task tracking")
  1143  	}
  1144  }
  1145  
  1146  func TestRecordingSpanRuntimeTracerTaskEnd(t *testing.T) {
  1147  	tp := NewTracerProvider(WithSampler(AlwaysSample()))
  1148  	tr := tp.Tracer("TestRecordingSpanRuntimeTracerTaskEnd")
  1149  
  1150  	var n uint64
  1151  	executionTracerTaskEnd := func() {
  1152  		atomic.AddUint64(&n, 1)
  1153  	}
  1154  	_, apiSpan := tr.Start(context.Background(), "foo")
  1155  	s, ok := apiSpan.(*recordingSpan)
  1156  	if !ok {
  1157  		t.Fatal("recording span not returned from always sampled Tracer")
  1158  	}
  1159  
  1160  	s.executionTracerTaskEnd = executionTracerTaskEnd
  1161  	s.End()
  1162  
  1163  	if n != 1 {
  1164  		t.Error("recording span did not end runtime trace task")
  1165  	}
  1166  }
  1167  
  1168  func TestCustomStartEndTime(t *testing.T) {
  1169  	te := NewTestExporter()
  1170  	tp := NewTracerProvider(WithSyncer(te), WithSampler(AlwaysSample()))
  1171  
  1172  	startTime := time.Date(2019, time.August, 27, 14, 42, 0, 0, time.UTC)
  1173  	endTime := startTime.Add(time.Second * 20)
  1174  	_, span := tp.Tracer("Custom Start and End time").Start(
  1175  		context.Background(),
  1176  		"testspan",
  1177  		trace.WithTimestamp(startTime),
  1178  	)
  1179  	span.End(trace.WithTimestamp(endTime))
  1180  
  1181  	if te.Len() != 1 {
  1182  		t.Fatalf("got %d exported spans, want one span", te.Len())
  1183  	}
  1184  	got := te.Spans()[0]
  1185  	if !got.StartTime().Equal(startTime) {
  1186  		t.Errorf("expected start time to be %s, got %s", startTime, got.StartTime())
  1187  	}
  1188  	if !got.EndTime().Equal(endTime) {
  1189  		t.Errorf("expected end time to be %s, got %s", endTime, got.EndTime())
  1190  	}
  1191  }
  1192  
  1193  func TestRecordError(t *testing.T) {
  1194  	scenarios := []struct {
  1195  		err error
  1196  		typ string
  1197  		msg string
  1198  	}{
  1199  		{
  1200  			err: ottest.NewTestError("test error"),
  1201  			typ: "go.opentelemetry.io/otel/sdk/internal/internaltest.TestError",
  1202  			msg: "test error",
  1203  		},
  1204  		{
  1205  			err: errors.New("test error 2"),
  1206  			typ: "*errors.errorString",
  1207  			msg: "test error 2",
  1208  		},
  1209  	}
  1210  
  1211  	for _, s := range scenarios {
  1212  		te := NewTestExporter()
  1213  		tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
  1214  		span := startSpan(tp, "RecordError")
  1215  
  1216  		errTime := time.Now()
  1217  		span.RecordError(s.err, trace.WithTimestamp(errTime))
  1218  
  1219  		got, err := endSpan(te, span)
  1220  		if err != nil {
  1221  			t.Fatal(err)
  1222  		}
  1223  
  1224  		want := &snapshot{
  1225  			spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1226  				TraceID:    tid,
  1227  				TraceFlags: 0x1,
  1228  			}),
  1229  			parent:   sc.WithRemote(true),
  1230  			name:     "span0",
  1231  			status:   Status{Code: codes.Unset},
  1232  			spanKind: trace.SpanKindInternal,
  1233  			events: []Event{
  1234  				{
  1235  					Name: semconv.ExceptionEventName,
  1236  					Time: errTime,
  1237  					Attributes: []attribute.KeyValue{
  1238  						semconv.ExceptionType(s.typ),
  1239  						semconv.ExceptionMessage(s.msg),
  1240  					},
  1241  				},
  1242  			},
  1243  			instrumentationScope: instrumentation.Scope{Name: "RecordError"},
  1244  		}
  1245  		if diff := cmpDiff(got, want); diff != "" {
  1246  			t.Errorf("SpanErrorOptions: -got +want %s", diff)
  1247  		}
  1248  	}
  1249  }
  1250  
  1251  func TestRecordErrorWithStackTrace(t *testing.T) {
  1252  	err := ottest.NewTestError("test error")
  1253  	typ := "go.opentelemetry.io/otel/sdk/internal/internaltest.TestError"
  1254  	msg := "test error"
  1255  
  1256  	te := NewTestExporter()
  1257  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
  1258  	span := startSpan(tp, "RecordError")
  1259  
  1260  	errTime := time.Now()
  1261  	span.RecordError(err, trace.WithTimestamp(errTime), trace.WithStackTrace(true))
  1262  
  1263  	got, err := endSpan(te, span)
  1264  	if err != nil {
  1265  		t.Fatal(err)
  1266  	}
  1267  
  1268  	want := &snapshot{
  1269  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1270  			TraceID:    tid,
  1271  			TraceFlags: 0x1,
  1272  		}),
  1273  		parent:   sc.WithRemote(true),
  1274  		name:     "span0",
  1275  		status:   Status{Code: codes.Unset},
  1276  		spanKind: trace.SpanKindInternal,
  1277  		events: []Event{
  1278  			{
  1279  				Name: semconv.ExceptionEventName,
  1280  				Time: errTime,
  1281  				Attributes: []attribute.KeyValue{
  1282  					semconv.ExceptionType(typ),
  1283  					semconv.ExceptionMessage(msg),
  1284  				},
  1285  			},
  1286  		},
  1287  		instrumentationScope: instrumentation.Scope{Name: "RecordError"},
  1288  	}
  1289  
  1290  	assert.Equal(t, got.spanContext, want.spanContext)
  1291  	assert.Equal(t, got.parent, want.parent)
  1292  	assert.Equal(t, got.name, want.name)
  1293  	assert.Equal(t, got.status, want.status)
  1294  	assert.Equal(t, got.spanKind, want.spanKind)
  1295  	assert.Equal(t, got.events[0].Attributes[0].Value.AsString(), want.events[0].Attributes[0].Value.AsString())
  1296  	assert.Equal(t, got.events[0].Attributes[1].Value.AsString(), want.events[0].Attributes[1].Value.AsString())
  1297  	gotStackTraceFunctionName := strings.Split(got.events[0].Attributes[2].Value.AsString(), "\n")
  1298  
  1299  	assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace", gotStackTraceFunctionName[1])
  1300  	assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError", gotStackTraceFunctionName[3])
  1301  }
  1302  
  1303  func TestRecordErrorNil(t *testing.T) {
  1304  	te := NewTestExporter()
  1305  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
  1306  	span := startSpan(tp, "RecordErrorNil")
  1307  
  1308  	span.RecordError(nil)
  1309  
  1310  	got, err := endSpan(te, span)
  1311  	if err != nil {
  1312  		t.Fatal(err)
  1313  	}
  1314  
  1315  	want := &snapshot{
  1316  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1317  			TraceID:    tid,
  1318  			TraceFlags: 0x1,
  1319  		}),
  1320  		parent:   sc.WithRemote(true),
  1321  		name:     "span0",
  1322  		spanKind: trace.SpanKindInternal,
  1323  		status: Status{
  1324  			Code:        codes.Unset,
  1325  			Description: "",
  1326  		},
  1327  		instrumentationScope: instrumentation.Scope{Name: "RecordErrorNil"},
  1328  	}
  1329  	if diff := cmpDiff(got, want); diff != "" {
  1330  		t.Errorf("SpanErrorOptions: -got +want %s", diff)
  1331  	}
  1332  }
  1333  
  1334  func TestWithSpanKind(t *testing.T) {
  1335  	te := NewTestExporter()
  1336  	tp := NewTracerProvider(WithSyncer(te), WithSampler(AlwaysSample()), WithResource(resource.Empty()))
  1337  	tr := tp.Tracer("withSpanKind")
  1338  
  1339  	_, span := tr.Start(context.Background(), "WithoutSpanKind")
  1340  	spanData, err := endSpan(te, span)
  1341  	if err != nil {
  1342  		t.Error(err.Error())
  1343  	}
  1344  
  1345  	if spanData.SpanKind() != trace.SpanKindInternal {
  1346  		t.Errorf("Default value of Spankind should be Internal: got %+v, want %+v\n", spanData.SpanKind(), trace.SpanKindInternal)
  1347  	}
  1348  
  1349  	sks := []trace.SpanKind{
  1350  		trace.SpanKindInternal,
  1351  		trace.SpanKindServer,
  1352  		trace.SpanKindClient,
  1353  		trace.SpanKindProducer,
  1354  		trace.SpanKindConsumer,
  1355  	}
  1356  
  1357  	for _, sk := range sks {
  1358  		te.Reset()
  1359  
  1360  		_, span := tr.Start(context.Background(), fmt.Sprintf("SpanKind-%v", sk), trace.WithSpanKind(sk))
  1361  		spanData, err := endSpan(te, span)
  1362  		if err != nil {
  1363  			t.Error(err.Error())
  1364  		}
  1365  
  1366  		if spanData.SpanKind() != sk {
  1367  			t.Errorf("WithSpanKind check: got %+v, want %+v\n", spanData.SpanKind(), sks)
  1368  		}
  1369  	}
  1370  }
  1371  
  1372  func mergeResource(t *testing.T, r1, r2 *resource.Resource) *resource.Resource {
  1373  	r, err := resource.Merge(r1, r2)
  1374  	assert.NoError(t, err)
  1375  	return r
  1376  }
  1377  
  1378  func TestWithResource(t *testing.T) {
  1379  	store, err := ottest.SetEnvVariables(map[string]string{
  1380  		envVar: "key=value,rk5=7",
  1381  	})
  1382  	require.NoError(t, err)
  1383  	defer func() { require.NoError(t, store.Restore()) }()
  1384  
  1385  	cases := []struct {
  1386  		name    string
  1387  		options []TracerProviderOption
  1388  		want    *resource.Resource
  1389  		msg     string
  1390  	}{
  1391  		{
  1392  			name:    "explicitly empty resource",
  1393  			options: []TracerProviderOption{WithResource(resource.Empty())},
  1394  			want:    resource.Environment(),
  1395  		},
  1396  		{
  1397  			name:    "uses default if no resource option",
  1398  			options: []TracerProviderOption{},
  1399  			want:    resource.Default(),
  1400  		},
  1401  		{
  1402  			name:    "explicit resource",
  1403  			options: []TracerProviderOption{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)))},
  1404  			want:    mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5))),
  1405  		},
  1406  		{
  1407  			name: "last resource wins",
  1408  			options: []TracerProviderOption{
  1409  				WithResource(resource.NewSchemaless(attribute.String("rk1", "vk1"), attribute.Int64("rk2", 5))),
  1410  				WithResource(resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
  1411  			},
  1412  			want: mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk3", "rv3"), attribute.Int64("rk4", 10))),
  1413  		},
  1414  		{
  1415  			name:    "overlapping attributes with environment resource",
  1416  			options: []TracerProviderOption{WithResource(resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10)))},
  1417  			want:    mergeResource(t, resource.Environment(), resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk5", 10))),
  1418  		},
  1419  	}
  1420  	for _, tc := range cases {
  1421  		tc := tc
  1422  		t.Run(tc.name, func(t *testing.T) {
  1423  			te := NewTestExporter()
  1424  			defaultOptions := []TracerProviderOption{WithSyncer(te), WithSampler(AlwaysSample())}
  1425  			tp := NewTracerProvider(append(defaultOptions, tc.options...)...)
  1426  			span := startSpan(tp, "WithResource")
  1427  			span.SetAttributes(attribute.String("key1", "value1"))
  1428  			got, err := endSpan(te, span)
  1429  			if err != nil {
  1430  				t.Error(err.Error())
  1431  			}
  1432  			want := &snapshot{
  1433  				spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1434  					TraceID:    tid,
  1435  					TraceFlags: 0x1,
  1436  				}),
  1437  				parent: sc.WithRemote(true),
  1438  				name:   "span0",
  1439  				attributes: []attribute.KeyValue{
  1440  					attribute.String("key1", "value1"),
  1441  				},
  1442  				spanKind:             trace.SpanKindInternal,
  1443  				resource:             tc.want,
  1444  				instrumentationScope: instrumentation.Scope{Name: "WithResource"},
  1445  			}
  1446  			if diff := cmpDiff(got, want); diff != "" {
  1447  				t.Errorf("WithResource:\n  -got +want %s", diff)
  1448  			}
  1449  		})
  1450  	}
  1451  }
  1452  
  1453  func TestWithInstrumentationVersionAndSchema(t *testing.T) {
  1454  	te := NewTestExporter()
  1455  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
  1456  
  1457  	ctx := context.Background()
  1458  	ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
  1459  	_, span := tp.Tracer(
  1460  		"WithInstrumentationVersion",
  1461  		trace.WithInstrumentationVersion("v0.1.0"),
  1462  		trace.WithSchemaURL("https://opentelemetry.io/schemas/1.2.0"),
  1463  	).Start(ctx, "span0")
  1464  	got, err := endSpan(te, span)
  1465  	if err != nil {
  1466  		t.Error(err.Error())
  1467  	}
  1468  
  1469  	want := &snapshot{
  1470  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1471  			TraceID:    tid,
  1472  			TraceFlags: 0x1,
  1473  		}),
  1474  		parent:   sc.WithRemote(true),
  1475  		name:     "span0",
  1476  		spanKind: trace.SpanKindInternal,
  1477  		instrumentationScope: instrumentation.Scope{
  1478  			Name:      "WithInstrumentationVersion",
  1479  			Version:   "v0.1.0",
  1480  			SchemaURL: "https://opentelemetry.io/schemas/1.2.0",
  1481  		},
  1482  	}
  1483  	if diff := cmpDiff(got, want); diff != "" {
  1484  		t.Errorf("WithResource:\n  -got +want %s", diff)
  1485  	}
  1486  }
  1487  
  1488  func TestSpanCapturesPanic(t *testing.T) {
  1489  	te := NewTestExporter()
  1490  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
  1491  	_, span := tp.Tracer("CatchPanic").Start(
  1492  		context.Background(),
  1493  		"span",
  1494  	)
  1495  
  1496  	f := func() {
  1497  		defer span.End()
  1498  		panic(errors.New("error message"))
  1499  	}
  1500  	require.PanicsWithError(t, "error message", f)
  1501  	spans := te.Spans()
  1502  	require.Len(t, spans, 1)
  1503  	require.Len(t, spans[0].Events(), 1)
  1504  	assert.Equal(t, spans[0].Events()[0].Name, semconv.ExceptionEventName)
  1505  	assert.Equal(t, spans[0].Events()[0].Attributes, []attribute.KeyValue{
  1506  		semconv.ExceptionType("*errors.errorString"),
  1507  		semconv.ExceptionMessage("error message"),
  1508  	})
  1509  }
  1510  
  1511  func TestSpanCapturesPanicWithStackTrace(t *testing.T) {
  1512  	te := NewTestExporter()
  1513  	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
  1514  	_, span := tp.Tracer("CatchPanic").Start(
  1515  		context.Background(),
  1516  		"span",
  1517  	)
  1518  
  1519  	f := func() {
  1520  		defer span.End(trace.WithStackTrace(true))
  1521  		panic(errors.New("error message"))
  1522  	}
  1523  	require.PanicsWithError(t, "error message", f)
  1524  	spans := te.Spans()
  1525  	require.Len(t, spans, 1)
  1526  	require.Len(t, spans[0].Events(), 1)
  1527  	assert.Equal(t, spans[0].Events()[0].Name, semconv.ExceptionEventName)
  1528  	assert.Equal(t, spans[0].Events()[0].Attributes[0].Value.AsString(), "*errors.errorString")
  1529  	assert.Equal(t, spans[0].Events()[0].Attributes[1].Value.AsString(), "error message")
  1530  
  1531  	gotStackTraceFunctionName := strings.Split(spans[0].Events()[0].Attributes[2].Value.AsString(), "\n")
  1532  	assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[1], "go.opentelemetry.io/otel/sdk/trace.recordStackTrace"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace", gotStackTraceFunctionName[1])
  1533  	assert.Truef(t, strings.HasPrefix(gotStackTraceFunctionName[3], "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End"), "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End", gotStackTraceFunctionName[3])
  1534  }
  1535  
  1536  func TestReadOnlySpan(t *testing.T) {
  1537  	kv := attribute.String("foo", "bar")
  1538  
  1539  	tp := NewTracerProvider(WithResource(resource.NewSchemaless(kv)))
  1540  	tr := tp.Tracer("ReadOnlySpan", trace.WithInstrumentationVersion("3"))
  1541  
  1542  	// Initialize parent context.
  1543  	tID, sID := tp.idGenerator.NewIDs(context.Background())
  1544  	parent := trace.NewSpanContext(trace.SpanContextConfig{
  1545  		TraceID:    tID,
  1546  		SpanID:     sID,
  1547  		TraceFlags: 0x1,
  1548  		Remote:     true,
  1549  	})
  1550  	ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent)
  1551  
  1552  	// Initialize linked context.
  1553  	tID, sID = tp.idGenerator.NewIDs(context.Background())
  1554  	linked := trace.NewSpanContext(trace.SpanContextConfig{
  1555  		TraceID:    tID,
  1556  		SpanID:     sID,
  1557  		TraceFlags: 0x1,
  1558  	})
  1559  
  1560  	st := time.Now()
  1561  	ctx, s := tr.Start(ctx, "foo", trace.WithTimestamp(st),
  1562  		trace.WithLinks(trace.Link{SpanContext: linked}))
  1563  	s.SetAttributes(kv)
  1564  	s.AddEvent("foo", trace.WithAttributes(kv))
  1565  	s.SetStatus(codes.Ok, "foo")
  1566  
  1567  	// Verify span implements ReadOnlySpan.
  1568  	ro, ok := s.(ReadOnlySpan)
  1569  	require.True(t, ok)
  1570  
  1571  	assert.Equal(t, "foo", ro.Name())
  1572  	assert.Equal(t, trace.SpanContextFromContext(ctx), ro.SpanContext())
  1573  	assert.Equal(t, parent, ro.Parent())
  1574  	assert.Equal(t, trace.SpanKindInternal, ro.SpanKind())
  1575  	assert.Equal(t, st, ro.StartTime())
  1576  	assert.True(t, ro.EndTime().IsZero())
  1577  	assert.Equal(t, kv.Key, ro.Attributes()[0].Key)
  1578  	assert.Equal(t, kv.Value, ro.Attributes()[0].Value)
  1579  	assert.Equal(t, linked, ro.Links()[0].SpanContext)
  1580  	assert.Equal(t, kv.Key, ro.Events()[0].Attributes[0].Key)
  1581  	assert.Equal(t, kv.Value, ro.Events()[0].Attributes[0].Value)
  1582  	assert.Equal(t, codes.Ok, ro.Status().Code)
  1583  	assert.Equal(t, "", ro.Status().Description)
  1584  	assert.Equal(t, "ReadOnlySpan", ro.InstrumentationLibrary().Name)
  1585  	assert.Equal(t, "3", ro.InstrumentationLibrary().Version)
  1586  	assert.Equal(t, "ReadOnlySpan", ro.InstrumentationScope().Name)
  1587  	assert.Equal(t, "3", ro.InstrumentationScope().Version)
  1588  	assert.Equal(t, kv.Key, ro.Resource().Attributes()[0].Key)
  1589  	assert.Equal(t, kv.Value, ro.Resource().Attributes()[0].Value)
  1590  
  1591  	// Verify changes to the original span are reflected in the ReadOnlySpan.
  1592  	s.SetName("bar")
  1593  	assert.Equal(t, "bar", ro.Name())
  1594  
  1595  	// Verify snapshot() returns snapshots that are independent from the
  1596  	// original span and from one another.
  1597  	d1 := s.(*recordingSpan).snapshot()
  1598  	s.AddEvent("baz")
  1599  	d2 := s.(*recordingSpan).snapshot()
  1600  	for _, e := range d1.Events() {
  1601  		if e.Name == "baz" {
  1602  			t.Errorf("Didn't expect to find 'baz' event")
  1603  		}
  1604  	}
  1605  	var exists bool
  1606  	for _, e := range d2.Events() {
  1607  		if e.Name == "baz" {
  1608  			exists = true
  1609  		}
  1610  	}
  1611  	if !exists {
  1612  		t.Errorf("Expected to find 'baz' event")
  1613  	}
  1614  
  1615  	et := st.Add(time.Millisecond)
  1616  	s.End(trace.WithTimestamp(et))
  1617  	assert.Equal(t, et, ro.EndTime())
  1618  }
  1619  
  1620  func TestReadWriteSpan(t *testing.T) {
  1621  	tp := NewTracerProvider(WithResource(resource.Empty()))
  1622  	tr := tp.Tracer("ReadWriteSpan")
  1623  
  1624  	// Initialize parent context.
  1625  	tID, sID := tp.idGenerator.NewIDs(context.Background())
  1626  	parent := trace.NewSpanContext(trace.SpanContextConfig{
  1627  		TraceID:    tID,
  1628  		SpanID:     sID,
  1629  		TraceFlags: 0x1,
  1630  	})
  1631  	ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent)
  1632  
  1633  	_, span := tr.Start(ctx, "foo")
  1634  	defer span.End()
  1635  
  1636  	// Verify span implements ReadOnlySpan.
  1637  	rw, ok := span.(ReadWriteSpan)
  1638  	require.True(t, ok)
  1639  
  1640  	// Verify the span can be read from.
  1641  	assert.False(t, rw.StartTime().IsZero())
  1642  
  1643  	// Verify the span can be written to.
  1644  	rw.SetName("bar")
  1645  	assert.Equal(t, "bar", rw.Name())
  1646  	// NOTE: This function tests ReadWriteSpan which is an interface which
  1647  	// embeds trace.Span and ReadOnlySpan. Since both of these interfaces have
  1648  	// their own tests, there is no point in testing all the possible methods
  1649  	// available via ReadWriteSpan as doing so would mean creating a lot of
  1650  	// duplication.
  1651  }
  1652  
  1653  func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) {
  1654  	te := NewTestExporter()
  1655  	sl := NewSpanLimits()
  1656  	sl.AttributePerEventCountLimit = 2
  1657  	tp := NewTracerProvider(
  1658  		WithSpanLimits(sl),
  1659  		WithSyncer(te),
  1660  		WithResource(resource.Empty()),
  1661  	)
  1662  
  1663  	span := startSpan(tp, "AddSpanEventWithOverLimitedAttributes")
  1664  	span.AddEvent("test1", trace.WithAttributes(
  1665  		attribute.Bool("key1", true),
  1666  		attribute.String("key2", "value2"),
  1667  	))
  1668  	// Parts of the attribute should be discard
  1669  	span.AddEvent("test2", trace.WithAttributes(
  1670  		attribute.Bool("key1", true),
  1671  		attribute.String("key2", "value2"),
  1672  		attribute.String("key3", "value3"),
  1673  		attribute.String("key4", "value4"),
  1674  	))
  1675  	got, err := endSpan(te, span)
  1676  	if err != nil {
  1677  		t.Fatal(err)
  1678  	}
  1679  
  1680  	for i := range got.Events() {
  1681  		if !checkTime(&got.Events()[i].Time) {
  1682  			t.Error("exporting span: expected nonzero Event Time")
  1683  		}
  1684  	}
  1685  
  1686  	want := &snapshot{
  1687  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1688  			TraceID:    tid,
  1689  			TraceFlags: 0x1,
  1690  		}),
  1691  		parent:     sc.WithRemote(true),
  1692  		name:       "span0",
  1693  		attributes: nil,
  1694  		events: []Event{
  1695  			{
  1696  				Name: "test1",
  1697  				Attributes: []attribute.KeyValue{
  1698  					attribute.Bool("key1", true),
  1699  					attribute.String("key2", "value2"),
  1700  				},
  1701  			},
  1702  			{
  1703  				Name: "test2",
  1704  				Attributes: []attribute.KeyValue{
  1705  					attribute.Bool("key1", true),
  1706  					attribute.String("key2", "value2"),
  1707  				},
  1708  				DroppedAttributeCount: 2,
  1709  			},
  1710  		},
  1711  		spanKind:             trace.SpanKindInternal,
  1712  		instrumentationScope: instrumentation.Scope{Name: "AddSpanEventWithOverLimitedAttributes"},
  1713  	}
  1714  	if diff := cmpDiff(got, want); diff != "" {
  1715  		t.Errorf("SetSpanAttributesOverLimit: -got +want %s", diff)
  1716  	}
  1717  }
  1718  
  1719  func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) {
  1720  	te := NewTestExporter()
  1721  	sl := NewSpanLimits()
  1722  	sl.AttributePerLinkCountLimit = 1
  1723  	tp := NewTracerProvider(
  1724  		WithSpanLimits(sl),
  1725  		WithSyncer(te),
  1726  		WithResource(resource.Empty()),
  1727  	)
  1728  
  1729  	k1v1 := attribute.String("key1", "value1")
  1730  	k2v2 := attribute.String("key2", "value2")
  1731  	k3v3 := attribute.String("key3", "value3")
  1732  	k4v4 := attribute.String("key4", "value4")
  1733  
  1734  	sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
  1735  	sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}})
  1736  
  1737  	span := startSpan(tp, "Links", trace.WithLinks([]trace.Link{
  1738  		{SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1, k2v2}},
  1739  		{SpanContext: sc2, Attributes: []attribute.KeyValue{k2v2, k3v3, k4v4}},
  1740  	}...))
  1741  
  1742  	got, err := endSpan(te, span)
  1743  	if err != nil {
  1744  		t.Fatal(err)
  1745  	}
  1746  
  1747  	want := &snapshot{
  1748  		spanContext: trace.NewSpanContext(trace.SpanContextConfig{
  1749  			TraceID:    tid,
  1750  			TraceFlags: 0x1,
  1751  		}),
  1752  		parent: sc.WithRemote(true),
  1753  		name:   "span0",
  1754  		links: []Link{
  1755  			{
  1756  				SpanContext:           sc1,
  1757  				Attributes:            []attribute.KeyValue{k1v1},
  1758  				DroppedAttributeCount: 1,
  1759  			},
  1760  			{
  1761  				SpanContext:           sc2,
  1762  				Attributes:            []attribute.KeyValue{k2v2},
  1763  				DroppedAttributeCount: 2,
  1764  			},
  1765  		},
  1766  		spanKind:             trace.SpanKindInternal,
  1767  		instrumentationScope: instrumentation.Scope{Name: "Links"},
  1768  	}
  1769  	if diff := cmpDiff(got, want); diff != "" {
  1770  		t.Errorf("Link: -got +want %s", diff)
  1771  	}
  1772  }
  1773  
  1774  type stateSampler struct {
  1775  	prefix string
  1776  	f      func(trace.TraceState) trace.TraceState
  1777  }
  1778  
  1779  func (s *stateSampler) ShouldSample(p SamplingParameters) SamplingResult {
  1780  	decision := Drop
  1781  	if strings.HasPrefix(p.Name, s.prefix) {
  1782  		decision = RecordAndSample
  1783  	}
  1784  	ts := s.f(trace.SpanContextFromContext(p.ParentContext).TraceState())
  1785  	return SamplingResult{Decision: decision, Tracestate: ts}
  1786  }
  1787  
  1788  func (s stateSampler) Description() string {
  1789  	return "stateSampler"
  1790  }
  1791  
  1792  // Check that a new span propagates the SamplerResult.TraceState.
  1793  func TestSamplerTraceState(t *testing.T) {
  1794  	mustTS := func(ts trace.TraceState, err error) trace.TraceState {
  1795  		require.NoError(t, err)
  1796  		return ts
  1797  	}
  1798  	makeInserter := func(k, v, prefix string) Sampler {
  1799  		return &stateSampler{
  1800  			prefix: prefix,
  1801  			f:      func(t trace.TraceState) trace.TraceState { return mustTS(t.Insert(k, v)) },
  1802  		}
  1803  	}
  1804  	makeDeleter := func(k, prefix string) Sampler {
  1805  		return &stateSampler{
  1806  			prefix: prefix,
  1807  			f:      func(t trace.TraceState) trace.TraceState { return t.Delete(k) },
  1808  		}
  1809  	}
  1810  	clearer := func(prefix string) Sampler {
  1811  		return &stateSampler{
  1812  			prefix: prefix,
  1813  			f:      func(t trace.TraceState) trace.TraceState { return trace.TraceState{} },
  1814  		}
  1815  	}
  1816  
  1817  	tests := []struct {
  1818  		name       string
  1819  		sampler    Sampler
  1820  		spanName   string
  1821  		input      trace.TraceState
  1822  		want       trace.TraceState
  1823  		exportSpan bool
  1824  	}{
  1825  		{
  1826  			name:       "alwaysOn",
  1827  			sampler:    AlwaysSample(),
  1828  			input:      mustTS(trace.ParseTraceState("k1=v1")),
  1829  			want:       mustTS(trace.ParseTraceState("k1=v1")),
  1830  			exportSpan: true,
  1831  		},
  1832  		{
  1833  			name:       "alwaysOff",
  1834  			sampler:    NeverSample(),
  1835  			input:      mustTS(trace.ParseTraceState("k1=v1")),
  1836  			want:       mustTS(trace.ParseTraceState("k1=v1")),
  1837  			exportSpan: false,
  1838  		},
  1839  		{
  1840  			name:       "insertKeySampled",
  1841  			sampler:    makeInserter("k2", "v2", "span"),
  1842  			spanName:   "span0",
  1843  			input:      mustTS(trace.ParseTraceState("k1=v1")),
  1844  			want:       mustTS(trace.ParseTraceState("k2=v2,k1=v1")),
  1845  			exportSpan: true,
  1846  		},
  1847  		{
  1848  			name:       "insertKeyDropped",
  1849  			sampler:    makeInserter("k2", "v2", "span"),
  1850  			spanName:   "nospan0",
  1851  			input:      mustTS(trace.ParseTraceState("k1=v1")),
  1852  			want:       mustTS(trace.ParseTraceState("k2=v2,k1=v1")),
  1853  			exportSpan: false,
  1854  		},
  1855  		{
  1856  			name:       "deleteKeySampled",
  1857  			sampler:    makeDeleter("k1", "span"),
  1858  			spanName:   "span0",
  1859  			input:      mustTS(trace.ParseTraceState("k1=v1,k2=v2")),
  1860  			want:       mustTS(trace.ParseTraceState("k2=v2")),
  1861  			exportSpan: true,
  1862  		},
  1863  		{
  1864  			name:       "deleteKeyDropped",
  1865  			sampler:    makeDeleter("k1", "span"),
  1866  			spanName:   "nospan0",
  1867  			input:      mustTS(trace.ParseTraceState("k1=v1,k2=v2,k3=v3")),
  1868  			want:       mustTS(trace.ParseTraceState("k2=v2,k3=v3")),
  1869  			exportSpan: false,
  1870  		},
  1871  		{
  1872  			name:       "clearer",
  1873  			sampler:    clearer("span"),
  1874  			spanName:   "span0",
  1875  			input:      mustTS(trace.ParseTraceState("k1=v1,k3=v3")),
  1876  			want:       mustTS(trace.ParseTraceState("")),
  1877  			exportSpan: true,
  1878  		},
  1879  	}
  1880  
  1881  	for _, ts := range tests {
  1882  		ts := ts
  1883  		t.Run(ts.name, func(t *testing.T) {
  1884  			te := NewTestExporter()
  1885  			tp := NewTracerProvider(WithSampler(ts.sampler), WithSyncer(te), WithResource(resource.Empty()))
  1886  			tr := tp.Tracer("TraceState")
  1887  
  1888  			sc1 := trace.NewSpanContext(trace.SpanContextConfig{
  1889  				TraceID:    tid,
  1890  				SpanID:     sid,
  1891  				TraceFlags: trace.FlagsSampled,
  1892  				TraceState: ts.input,
  1893  			})
  1894  			ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc1)
  1895  			_, span := tr.Start(ctx, ts.spanName)
  1896  
  1897  			// span's TraceState should be set regardless of Sampled/NonSampled state.
  1898  			require.Equal(t, ts.want, span.SpanContext().TraceState())
  1899  
  1900  			span.End()
  1901  
  1902  			got := te.Spans()
  1903  			if len(got) > 0 != ts.exportSpan {
  1904  				t.Errorf("unexpected number of exported spans %d", len(got))
  1905  			}
  1906  			if len(got) == 0 {
  1907  				return
  1908  			}
  1909  
  1910  			receivedState := got[0].SpanContext().TraceState()
  1911  
  1912  			if diff := cmpDiff(receivedState, ts.want); diff != "" {
  1913  				t.Errorf("TraceState not propagated: -got +want %s", diff)
  1914  			}
  1915  		})
  1916  	}
  1917  }
  1918  
  1919  type testIDGenerator struct {
  1920  	traceID int
  1921  	spanID  int
  1922  }
  1923  
  1924  func (gen *testIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) {
  1925  	traceIDHex := fmt.Sprintf("%032x", gen.traceID)
  1926  	traceID, _ := trace.TraceIDFromHex(traceIDHex)
  1927  	gen.traceID++
  1928  
  1929  	spanID := gen.NewSpanID(ctx, traceID)
  1930  	return traceID, spanID
  1931  }
  1932  
  1933  func (gen *testIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID {
  1934  	spanIDHex := fmt.Sprintf("%016x", gen.spanID)
  1935  	spanID, _ := trace.SpanIDFromHex(spanIDHex)
  1936  	gen.spanID++
  1937  	return spanID
  1938  }
  1939  
  1940  var _ IDGenerator = (*testIDGenerator)(nil)
  1941  
  1942  func TestWithIDGenerator(t *testing.T) {
  1943  	const (
  1944  		startTraceID = 1
  1945  		startSpanID  = 1
  1946  		numSpan      = 10
  1947  	)
  1948  
  1949  	gen := &testIDGenerator{traceID: startSpanID, spanID: startSpanID}
  1950  
  1951  	for i := 0; i < numSpan; i++ {
  1952  		te := NewTestExporter()
  1953  		tp := NewTracerProvider(
  1954  			WithSyncer(te),
  1955  			WithIDGenerator(gen),
  1956  		)
  1957  		span := startSpan(tp, "TestWithIDGenerator")
  1958  		got, err := strconv.ParseUint(span.SpanContext().SpanID().String(), 16, 64)
  1959  		require.NoError(t, err)
  1960  		want := uint64(startSpanID + i)
  1961  		assert.Equal(t, got, want)
  1962  		_, err = endSpan(te, span)
  1963  		require.NoError(t, err)
  1964  	}
  1965  }
  1966  
  1967  func TestEmptyRecordingSpanAttributes(t *testing.T) {
  1968  	assert.Nil(t, (&recordingSpan{}).Attributes())
  1969  }
  1970  
  1971  func TestEmptyRecordingSpanDroppedAttributes(t *testing.T) {
  1972  	assert.Equal(t, 0, (&recordingSpan{}).DroppedAttributes())
  1973  }
  1974  

View as plain text