...

Source file src/go.opencensus.io/trace/trace_test.go

Documentation: go.opencensus.io/trace

     1  // Copyright 2017, OpenCensus 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  	"fmt"
    20  	"reflect"
    21  	"sync/atomic"
    22  	"testing"
    23  	"time"
    24  
    25  	"go.opencensus.io/trace/tracestate"
    26  )
    27  
    28  var (
    29  	tid               = TraceID{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 4, 8, 16, 32, 64, 128}
    30  	sid               = SpanID{1, 2, 4, 8, 16, 32, 64, 128}
    31  	testTracestate, _ = tracestate.New(nil, tracestate.Entry{Key: "foo", Value: "bar"})
    32  )
    33  
    34  func init() {
    35  	// no random sampling, but sample children of sampled spans.
    36  	ApplyConfig(Config{DefaultSampler: ProbabilitySampler(0)})
    37  }
    38  
    39  func TestStrings(t *testing.T) {
    40  	if got, want := tid.String(), "01020304050607080102040810204080"; got != want {
    41  		t.Errorf("TraceID.String: got %q want %q", got, want)
    42  	}
    43  	if got, want := sid.String(), "0102040810204080"; got != want {
    44  		t.Errorf("SpanID.String: got %q want %q", got, want)
    45  	}
    46  }
    47  
    48  func TestFromContext(t *testing.T) {
    49  	want := &Span{}
    50  	ctx := NewContext(context.Background(), want)
    51  	got := FromContext(ctx)
    52  	if got != want {
    53  		t.Errorf("got Span pointer %p want %p", got, want)
    54  	}
    55  }
    56  
    57  type foo int
    58  
    59  func (f foo) String() string {
    60  	return "foo"
    61  }
    62  
    63  // checkChild tests that c has fields set appropriately, given that it is a child span of p.
    64  func checkChild(p SpanContext, c *Span) error {
    65  	if c == nil {
    66  		return fmt.Errorf("got nil child span, want non-nil")
    67  	}
    68  	if got, want := c.SpanContext().TraceID, p.TraceID; got != want {
    69  		return fmt.Errorf("got child trace ID %s, want %s", got, want)
    70  	}
    71  	if childID, parentID := c.SpanContext().SpanID, p.SpanID; childID == parentID {
    72  		return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID)
    73  	}
    74  	if got, want := c.SpanContext().TraceOptions, p.TraceOptions; got != want {
    75  		return fmt.Errorf("got child trace options %d, want %d", got, want)
    76  	}
    77  	if got, want := c.SpanContext().Tracestate, p.Tracestate; got != want {
    78  		return fmt.Errorf("got child tracestate %v, want %v", got, want)
    79  	}
    80  	return nil
    81  }
    82  
    83  func TestStartSpan(t *testing.T) {
    84  	ctx, _ := StartSpan(context.Background(), "StartSpan")
    85  	s := FromContext(ctx).internal.(*span)
    86  	if s.data != nil {
    87  		t.Error("StartSpan: new span is recording events")
    88  	}
    89  }
    90  
    91  func TestSampling(t *testing.T) {
    92  	for _, test := range []struct {
    93  		remoteParent       bool
    94  		localParent        bool
    95  		parentTraceOptions TraceOptions
    96  		sampler            Sampler
    97  		wantTraceOptions   TraceOptions
    98  	}{
    99  		{true, false, 0, nil, 0},
   100  		{true, false, 1, nil, 1},
   101  		{true, false, 0, NeverSample(), 0},
   102  		{true, false, 1, NeverSample(), 0},
   103  		{true, false, 0, AlwaysSample(), 1},
   104  		{true, false, 1, AlwaysSample(), 1},
   105  		{false, true, 0, NeverSample(), 0},
   106  		{false, true, 1, NeverSample(), 0},
   107  		{false, true, 0, AlwaysSample(), 1},
   108  		{false, true, 1, AlwaysSample(), 1},
   109  		{false, false, 0, nil, 0},
   110  		{false, false, 0, NeverSample(), 0},
   111  		{false, false, 0, AlwaysSample(), 1},
   112  	} {
   113  		var ctx context.Context
   114  		if test.remoteParent {
   115  			sc := SpanContext{
   116  				TraceID:      tid,
   117  				SpanID:       sid,
   118  				TraceOptions: test.parentTraceOptions,
   119  			}
   120  			ctx, _ = StartSpanWithRemoteParent(context.Background(), "foo", sc, WithSampler(test.sampler))
   121  		} else if test.localParent {
   122  			sampler := NeverSample()
   123  			if test.parentTraceOptions == 1 {
   124  				sampler = AlwaysSample()
   125  			}
   126  			ctx2, _ := StartSpan(context.Background(), "foo", WithSampler(sampler))
   127  			ctx, _ = StartSpan(ctx2, "foo", WithSampler(test.sampler))
   128  		} else {
   129  			ctx, _ = StartSpan(context.Background(), "foo", WithSampler(test.sampler))
   130  		}
   131  		sc := FromContext(ctx).SpanContext()
   132  		if (sc == SpanContext{}) {
   133  			t.Errorf("case %#v: starting new span: no span in context", test)
   134  			continue
   135  		}
   136  		if sc.SpanID == (SpanID{}) {
   137  			t.Errorf("case %#v: starting new span: got zero SpanID, want nonzero", test)
   138  		}
   139  		if sc.TraceOptions != test.wantTraceOptions {
   140  			t.Errorf("case %#v: starting new span: got TraceOptions %x, want %x", test, sc.TraceOptions, test.wantTraceOptions)
   141  		}
   142  	}
   143  
   144  	// Test that for children of local spans, the default sampler has no effect.
   145  	for _, test := range []struct {
   146  		parentTraceOptions TraceOptions
   147  		wantTraceOptions   TraceOptions
   148  	}{
   149  		{0, 0},
   150  		{0, 0},
   151  		{1, 1},
   152  		{1, 1},
   153  	} {
   154  		for _, defaultSampler := range []Sampler{
   155  			NeverSample(),
   156  			AlwaysSample(),
   157  			ProbabilitySampler(0),
   158  		} {
   159  			ApplyConfig(Config{DefaultSampler: defaultSampler})
   160  			sampler := NeverSample()
   161  			if test.parentTraceOptions == 1 {
   162  				sampler = AlwaysSample()
   163  			}
   164  			ctx2, _ := StartSpan(context.Background(), "foo", WithSampler(sampler))
   165  			ctx, _ := StartSpan(ctx2, "foo")
   166  			sc := FromContext(ctx).SpanContext()
   167  			if (sc == SpanContext{}) {
   168  				t.Errorf("case %#v: starting new child of local span: no span in context", test)
   169  				continue
   170  			}
   171  			if sc.SpanID == (SpanID{}) {
   172  				t.Errorf("case %#v: starting new child of local span: got zero SpanID, want nonzero", test)
   173  			}
   174  			if sc.TraceOptions != test.wantTraceOptions {
   175  				t.Errorf("case %#v: starting new child of local span: got TraceOptions %x, want %x", test, sc.TraceOptions, test.wantTraceOptions)
   176  			}
   177  		}
   178  	}
   179  	ApplyConfig(Config{DefaultSampler: ProbabilitySampler(0)}) // reset the default sampler.
   180  }
   181  
   182  func TestProbabilitySampler(t *testing.T) {
   183  	exported := 0
   184  	for i := 0; i < 1000; i++ {
   185  		_, span := StartSpan(context.Background(), "foo", WithSampler(ProbabilitySampler(0.3)))
   186  		if span.SpanContext().IsSampled() {
   187  			exported++
   188  		}
   189  	}
   190  	if exported < 200 || exported > 400 {
   191  		t.Errorf("got %f%% exported spans, want approximately 30%%", float64(exported)*0.1)
   192  	}
   193  }
   194  
   195  func TestStartSpanWithRemoteParent(t *testing.T) {
   196  	sc := SpanContext{
   197  		TraceID:      tid,
   198  		SpanID:       sid,
   199  		TraceOptions: 0x0,
   200  	}
   201  	ctx, _ := StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
   202  	if err := checkChild(sc, FromContext(ctx)); err != nil {
   203  		t.Error(err)
   204  	}
   205  
   206  	ctx, _ = StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
   207  	if err := checkChild(sc, FromContext(ctx)); err != nil {
   208  		t.Error(err)
   209  	}
   210  
   211  	sc = SpanContext{
   212  		TraceID:      tid,
   213  		SpanID:       sid,
   214  		TraceOptions: 0x1,
   215  		Tracestate:   testTracestate,
   216  	}
   217  	ctx, _ = StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
   218  	if err := checkChild(sc, FromContext(ctx)); err != nil {
   219  		t.Error(err)
   220  	}
   221  
   222  	ctx, _ = StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
   223  	if err := checkChild(sc, FromContext(ctx)); err != nil {
   224  		t.Error(err)
   225  	}
   226  
   227  	ctx2, _ := StartSpan(ctx, "StartSpan")
   228  	parent := FromContext(ctx).SpanContext()
   229  	if err := checkChild(parent, FromContext(ctx2)); err != nil {
   230  		t.Error(err)
   231  	}
   232  }
   233  
   234  // startSpan returns a context with a new Span that is recording events and will be exported.
   235  func startSpan(o StartOptions) *Span {
   236  	_, span := StartSpanWithRemoteParent(context.Background(), "span0",
   237  		SpanContext{
   238  			TraceID:      tid,
   239  			SpanID:       sid,
   240  			TraceOptions: 1,
   241  		},
   242  		WithSampler(o.Sampler),
   243  		WithSpanKind(o.SpanKind),
   244  	)
   245  	return span
   246  }
   247  
   248  type testExporter struct {
   249  	spans []*SpanData
   250  }
   251  
   252  func (t *testExporter) ExportSpan(s *SpanData) {
   253  	t.spans = append(t.spans, s)
   254  }
   255  
   256  // endSpan ends the Span in the context and returns the exported SpanData.
   257  //
   258  // It also does some tests on the Span, and tests and clears some fields in the SpanData.
   259  func endSpan(span *Span) (*SpanData, error) {
   260  
   261  	if !span.IsRecordingEvents() {
   262  		return nil, fmt.Errorf("IsRecordingEvents: got false, want true")
   263  	}
   264  	if !span.SpanContext().IsSampled() {
   265  		return nil, fmt.Errorf("IsSampled: got false, want true")
   266  	}
   267  	var te testExporter
   268  	RegisterExporter(&te)
   269  	span.End()
   270  	UnregisterExporter(&te)
   271  	if len(te.spans) != 1 {
   272  		return nil, fmt.Errorf("got exported spans %#v, want one span", te.spans)
   273  	}
   274  	got := te.spans[0]
   275  	if got.SpanContext.SpanID == (SpanID{}) {
   276  		return nil, fmt.Errorf("exporting span: expected nonzero SpanID")
   277  	}
   278  	got.SpanContext.SpanID = SpanID{}
   279  	if !checkTime(&got.StartTime) {
   280  		return nil, fmt.Errorf("exporting span: expected nonzero StartTime")
   281  	}
   282  	if !checkTime(&got.EndTime) {
   283  		return nil, fmt.Errorf("exporting span: expected nonzero EndTime")
   284  	}
   285  	return got, nil
   286  }
   287  
   288  // checkTime checks that a nonzero time was set in x, then clears it.
   289  func checkTime(x *time.Time) bool {
   290  	if x.IsZero() {
   291  		return false
   292  	}
   293  	*x = time.Time{}
   294  	return true
   295  }
   296  
   297  func TestSpanKind(t *testing.T) {
   298  	tests := []struct {
   299  		name         string
   300  		startOptions StartOptions
   301  		want         *SpanData
   302  	}{
   303  		{
   304  			name:         "zero StartOptions",
   305  			startOptions: StartOptions{},
   306  			want: &SpanData{
   307  				SpanContext: SpanContext{
   308  					TraceID:      tid,
   309  					SpanID:       SpanID{},
   310  					TraceOptions: 0x1,
   311  				},
   312  				ParentSpanID:    sid,
   313  				Name:            "span0",
   314  				SpanKind:        SpanKindUnspecified,
   315  				HasRemoteParent: true,
   316  			},
   317  		},
   318  		{
   319  			name: "client span",
   320  			startOptions: StartOptions{
   321  				SpanKind: SpanKindClient,
   322  			},
   323  			want: &SpanData{
   324  				SpanContext: SpanContext{
   325  					TraceID:      tid,
   326  					SpanID:       SpanID{},
   327  					TraceOptions: 0x1,
   328  				},
   329  				ParentSpanID:    sid,
   330  				Name:            "span0",
   331  				SpanKind:        SpanKindClient,
   332  				HasRemoteParent: true,
   333  			},
   334  		},
   335  		{
   336  			name: "server span",
   337  			startOptions: StartOptions{
   338  				SpanKind: SpanKindServer,
   339  			},
   340  			want: &SpanData{
   341  				SpanContext: SpanContext{
   342  					TraceID:      tid,
   343  					SpanID:       SpanID{},
   344  					TraceOptions: 0x1,
   345  				},
   346  				ParentSpanID:    sid,
   347  				Name:            "span0",
   348  				SpanKind:        SpanKindServer,
   349  				HasRemoteParent: true,
   350  			},
   351  		},
   352  	}
   353  
   354  	for _, tt := range tests {
   355  		span := startSpan(tt.startOptions)
   356  		got, err := endSpan(span)
   357  		if err != nil {
   358  			t.Fatal(err)
   359  		}
   360  		if !reflect.DeepEqual(got, tt.want) {
   361  			t.Errorf("exporting span: got %#v want %#v", got, tt.want)
   362  		}
   363  	}
   364  }
   365  
   366  func TestSetSpanAttributes(t *testing.T) {
   367  	span := startSpan(StartOptions{})
   368  	span.AddAttributes(StringAttribute("key1", "value1"))
   369  	got, err := endSpan(span)
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	want := &SpanData{
   375  		SpanContext: SpanContext{
   376  			TraceID:      tid,
   377  			SpanID:       SpanID{},
   378  			TraceOptions: 0x1,
   379  		},
   380  		ParentSpanID:    sid,
   381  		Name:            "span0",
   382  		Attributes:      map[string]interface{}{"key1": "value1"},
   383  		HasRemoteParent: true,
   384  	}
   385  	if !reflect.DeepEqual(got, want) {
   386  		t.Errorf("exporting span: got %#v want %#v", got, want)
   387  	}
   388  }
   389  
   390  func TestSetSpanAttributesOverLimit(t *testing.T) {
   391  	cfg := Config{MaxAttributesPerSpan: 2}
   392  	ApplyConfig(cfg)
   393  
   394  	span := startSpan(StartOptions{})
   395  	span.AddAttributes(StringAttribute("key1", "value1"))
   396  	span.AddAttributes(StringAttribute("key2", "value2"))
   397  	span.AddAttributes(StringAttribute("key1", "value3")) // Replace key1.
   398  	span.AddAttributes(StringAttribute("key4", "value4")) // Remove key2 and add key4
   399  	got, err := endSpan(span)
   400  	if err != nil {
   401  		t.Fatal(err)
   402  	}
   403  
   404  	want := &SpanData{
   405  		SpanContext: SpanContext{
   406  			TraceID:      tid,
   407  			SpanID:       SpanID{},
   408  			TraceOptions: 0x1,
   409  		},
   410  		ParentSpanID:          sid,
   411  		Name:                  "span0",
   412  		Attributes:            map[string]interface{}{"key1": "value3", "key4": "value4"},
   413  		HasRemoteParent:       true,
   414  		DroppedAttributeCount: 1,
   415  	}
   416  	if !reflect.DeepEqual(got, want) {
   417  		t.Errorf("exporting span: got %#v want %#v", got, want)
   418  	}
   419  }
   420  
   421  func TestAnnotations(t *testing.T) {
   422  	span := startSpan(StartOptions{})
   423  	span.Annotatef([]Attribute{StringAttribute("key1", "value1")}, "%f", 1.5)
   424  	span.Annotate([]Attribute{StringAttribute("key2", "value2")}, "Annotate")
   425  	got, err := endSpan(span)
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  
   430  	for i := range got.Annotations {
   431  		if !checkTime(&got.Annotations[i].Time) {
   432  			t.Error("exporting span: expected nonzero Annotation Time")
   433  		}
   434  	}
   435  
   436  	want := &SpanData{
   437  		SpanContext: SpanContext{
   438  			TraceID:      tid,
   439  			SpanID:       SpanID{},
   440  			TraceOptions: 0x1,
   441  		},
   442  		ParentSpanID: sid,
   443  		Name:         "span0",
   444  		Annotations: []Annotation{
   445  			{Message: "1.500000", Attributes: map[string]interface{}{"key1": "value1"}},
   446  			{Message: "Annotate", Attributes: map[string]interface{}{"key2": "value2"}},
   447  		},
   448  		HasRemoteParent: true,
   449  	}
   450  	if !reflect.DeepEqual(got, want) {
   451  		t.Errorf("exporting span: got %#v want %#v", got, want)
   452  	}
   453  }
   454  
   455  func TestAnnotationsOverLimit(t *testing.T) {
   456  	cfg := Config{MaxAnnotationEventsPerSpan: 2}
   457  	ApplyConfig(cfg)
   458  	span := startSpan(StartOptions{})
   459  	span.Annotatef([]Attribute{StringAttribute("key4", "value4")}, "%d", 1)
   460  	span.Annotate([]Attribute{StringAttribute("key3", "value3")}, "Annotate oldest")
   461  	span.Annotatef([]Attribute{StringAttribute("key1", "value1")}, "%f", 1.5)
   462  	span.Annotate([]Attribute{StringAttribute("key2", "value2")}, "Annotate")
   463  	got, err := endSpan(span)
   464  	if err != nil {
   465  		t.Fatal(err)
   466  	}
   467  
   468  	for i := range got.Annotations {
   469  		if !checkTime(&got.Annotations[i].Time) {
   470  			t.Error("exporting span: expected nonzero Annotation Time")
   471  		}
   472  	}
   473  
   474  	want := &SpanData{
   475  		SpanContext: SpanContext{
   476  			TraceID:      tid,
   477  			SpanID:       SpanID{},
   478  			TraceOptions: 0x1,
   479  		},
   480  		ParentSpanID: sid,
   481  		Name:         "span0",
   482  		Annotations: []Annotation{
   483  			{Message: "1.500000", Attributes: map[string]interface{}{"key1": "value1"}},
   484  			{Message: "Annotate", Attributes: map[string]interface{}{"key2": "value2"}},
   485  		},
   486  		DroppedAnnotationCount: 2,
   487  		HasRemoteParent:        true,
   488  	}
   489  	if !reflect.DeepEqual(got, want) {
   490  		t.Errorf("exporting span: got %#v want %#v", got, want)
   491  	}
   492  }
   493  
   494  func TestMessageEvents(t *testing.T) {
   495  	span := startSpan(StartOptions{})
   496  	span.AddMessageReceiveEvent(3, 400, 300)
   497  	span.AddMessageSendEvent(1, 200, 100)
   498  	got, err := endSpan(span)
   499  	if err != nil {
   500  		t.Fatal(err)
   501  	}
   502  
   503  	for i := range got.MessageEvents {
   504  		if !checkTime(&got.MessageEvents[i].Time) {
   505  			t.Error("exporting span: expected nonzero MessageEvent Time")
   506  		}
   507  	}
   508  
   509  	want := &SpanData{
   510  		SpanContext: SpanContext{
   511  			TraceID:      tid,
   512  			SpanID:       SpanID{},
   513  			TraceOptions: 0x1,
   514  		},
   515  		ParentSpanID: sid,
   516  		Name:         "span0",
   517  		MessageEvents: []MessageEvent{
   518  			{EventType: 2, MessageID: 0x3, UncompressedByteSize: 0x190, CompressedByteSize: 0x12c},
   519  			{EventType: 1, MessageID: 0x1, UncompressedByteSize: 0xc8, CompressedByteSize: 0x64},
   520  		},
   521  		HasRemoteParent: true,
   522  	}
   523  	if !reflect.DeepEqual(got, want) {
   524  		t.Errorf("exporting span: got %#v want %#v", got, want)
   525  	}
   526  }
   527  
   528  func TestMessageEventsOverLimit(t *testing.T) {
   529  	cfg := Config{MaxMessageEventsPerSpan: 2}
   530  	ApplyConfig(cfg)
   531  	span := startSpan(StartOptions{})
   532  	span.AddMessageReceiveEvent(5, 300, 120)
   533  	span.AddMessageSendEvent(4, 100, 50)
   534  	span.AddMessageReceiveEvent(3, 400, 300)
   535  	span.AddMessageSendEvent(1, 200, 100)
   536  	got, err := endSpan(span)
   537  	if err != nil {
   538  		t.Fatal(err)
   539  	}
   540  
   541  	for i := range got.MessageEvents {
   542  		if !checkTime(&got.MessageEvents[i].Time) {
   543  			t.Error("exporting span: expected nonzero MessageEvent Time")
   544  		}
   545  	}
   546  
   547  	want := &SpanData{
   548  		SpanContext: SpanContext{
   549  			TraceID:      tid,
   550  			SpanID:       SpanID{},
   551  			TraceOptions: 0x1,
   552  		},
   553  		ParentSpanID: sid,
   554  		Name:         "span0",
   555  		MessageEvents: []MessageEvent{
   556  			{EventType: 2, MessageID: 0x3, UncompressedByteSize: 0x190, CompressedByteSize: 0x12c},
   557  			{EventType: 1, MessageID: 0x1, UncompressedByteSize: 0xc8, CompressedByteSize: 0x64},
   558  		},
   559  		DroppedMessageEventCount: 2,
   560  		HasRemoteParent:          true,
   561  	}
   562  	if !reflect.DeepEqual(got, want) {
   563  		t.Errorf("exporting span: got %#v want %#v", got, want)
   564  	}
   565  }
   566  
   567  func TestSetSpanName(t *testing.T) {
   568  	want := "SpanName-1"
   569  	span := startSpan(StartOptions{})
   570  	span.SetName(want)
   571  	got, err := endSpan(span)
   572  	if err != nil {
   573  		t.Fatal(err)
   574  	}
   575  
   576  	if got.Name != want {
   577  		t.Errorf("span.Name=%q; want %q", got.Name, want)
   578  	}
   579  }
   580  
   581  func TestSetSpanNameUnsampledSpan(t *testing.T) {
   582  	var nilSpanData *SpanData
   583  	s := startSpan(StartOptions{Sampler: NeverSample()})
   584  	s.SetName("NoopName")
   585  	sp := s.internal.(*span)
   586  	if want, got := nilSpanData, sp.data; want != got {
   587  		t.Errorf("span.data=%+v; want %+v", got, want)
   588  	}
   589  }
   590  
   591  func TestSetSpanNameAfterSpanEnd(t *testing.T) {
   592  	want := "SpanName-2"
   593  	span := startSpan(StartOptions{})
   594  	span.SetName(want)
   595  	got, err := endSpan(span)
   596  	if err != nil {
   597  		t.Fatal(err)
   598  	}
   599  
   600  	// updating name after span.End
   601  	span.SetName("NoopName")
   602  
   603  	// exported span should not be updated by previous call to SetName
   604  	if got.Name != want {
   605  		t.Errorf("span.Name=%q; want %q", got.Name, want)
   606  	}
   607  
   608  	// span should not be exported again
   609  	var te testExporter
   610  	RegisterExporter(&te)
   611  	span.End()
   612  	UnregisterExporter(&te)
   613  	if len(te.spans) != 0 {
   614  		t.Errorf("got exported spans %#v, wanted no spans", te.spans)
   615  	}
   616  }
   617  
   618  func TestSetSpanStatus(t *testing.T) {
   619  	span := startSpan(StartOptions{})
   620  	span.SetStatus(Status{Code: int32(1), Message: "request failed"})
   621  	got, err := endSpan(span)
   622  	if err != nil {
   623  		t.Fatal(err)
   624  	}
   625  
   626  	want := &SpanData{
   627  		SpanContext: SpanContext{
   628  			TraceID:      tid,
   629  			SpanID:       SpanID{},
   630  			TraceOptions: 0x1,
   631  		},
   632  		ParentSpanID:    sid,
   633  		Name:            "span0",
   634  		Status:          Status{Code: 1, Message: "request failed"},
   635  		HasRemoteParent: true,
   636  	}
   637  	if !reflect.DeepEqual(got, want) {
   638  		t.Errorf("exporting span: got %#v want %#v", got, want)
   639  	}
   640  }
   641  
   642  func TestAddLink(t *testing.T) {
   643  	span := startSpan(StartOptions{})
   644  	span.AddLink(Link{
   645  		TraceID:    tid,
   646  		SpanID:     sid,
   647  		Type:       LinkTypeParent,
   648  		Attributes: map[string]interface{}{"key5": "value5"},
   649  	})
   650  	got, err := endSpan(span)
   651  	if err != nil {
   652  		t.Fatal(err)
   653  	}
   654  
   655  	want := &SpanData{
   656  		SpanContext: SpanContext{
   657  			TraceID:      tid,
   658  			SpanID:       SpanID{},
   659  			TraceOptions: 0x1,
   660  		},
   661  		ParentSpanID: sid,
   662  		Name:         "span0",
   663  		Links: []Link{{
   664  			TraceID:    tid,
   665  			SpanID:     sid,
   666  			Type:       2,
   667  			Attributes: map[string]interface{}{"key5": "value5"},
   668  		}},
   669  		HasRemoteParent: true,
   670  	}
   671  	if !reflect.DeepEqual(got, want) {
   672  		t.Errorf("exporting span: got %#v want %#v", got, want)
   673  	}
   674  }
   675  
   676  func TestAddLinkOverLimit(t *testing.T) {
   677  	cfg := Config{MaxLinksPerSpan: 1}
   678  	ApplyConfig(cfg)
   679  	span := startSpan(StartOptions{})
   680  	span.AddLink(Link{
   681  		TraceID:    tid,
   682  		SpanID:     sid,
   683  		Type:       LinkTypeParent,
   684  		Attributes: map[string]interface{}{"key4": "value4"},
   685  	})
   686  	span.AddLink(Link{
   687  		TraceID:    tid,
   688  		SpanID:     sid,
   689  		Type:       LinkTypeParent,
   690  		Attributes: map[string]interface{}{"key5": "value5"},
   691  	})
   692  	got, err := endSpan(span)
   693  	if err != nil {
   694  		t.Fatal(err)
   695  	}
   696  
   697  	want := &SpanData{
   698  		SpanContext: SpanContext{
   699  			TraceID:      tid,
   700  			SpanID:       SpanID{},
   701  			TraceOptions: 0x1,
   702  		},
   703  		ParentSpanID: sid,
   704  		Name:         "span0",
   705  		Links: []Link{{
   706  			TraceID:    tid,
   707  			SpanID:     sid,
   708  			Type:       2,
   709  			Attributes: map[string]interface{}{"key5": "value5"},
   710  		}},
   711  		DroppedLinkCount: 1,
   712  		HasRemoteParent:  true,
   713  	}
   714  	if !reflect.DeepEqual(got, want) {
   715  		t.Errorf("exporting span: got %#v want %#v", got, want)
   716  	}
   717  }
   718  
   719  func TestUnregisterExporter(t *testing.T) {
   720  	var te testExporter
   721  	RegisterExporter(&te)
   722  	UnregisterExporter(&te)
   723  
   724  	ctx := startSpan(StartOptions{})
   725  	endSpan(ctx)
   726  	if len(te.spans) != 0 {
   727  		t.Error("unregistered Exporter was called")
   728  	}
   729  }
   730  
   731  func TestBucket(t *testing.T) {
   732  	// make a bucket of size 5 and add 10 spans
   733  	b := makeBucket(5)
   734  	for i := 1; i <= 10; i++ {
   735  		b.nextTime = time.Time{} // reset the time so that the next span is accepted.
   736  		// add a span, with i stored in the TraceID so we can test for it later.
   737  		b.add(&SpanData{SpanContext: SpanContext{TraceID: TraceID{byte(i)}}, EndTime: time.Now()})
   738  		if i <= 5 {
   739  			if b.size() != i {
   740  				t.Fatalf("got bucket size %d, want %d %#v\n", b.size(), i, b)
   741  			}
   742  			for j := 0; j < i; j++ {
   743  				if b.span(j).TraceID[0] != byte(j+1) {
   744  					t.Errorf("got span index %d, want %d\n", b.span(j).TraceID[0], j+1)
   745  				}
   746  			}
   747  		} else {
   748  			if b.size() != 5 {
   749  				t.Fatalf("got bucket size %d, want 5\n", b.size())
   750  			}
   751  			for j := 0; j < 5; j++ {
   752  				want := i - 4 + j
   753  				if b.span(j).TraceID[0] != byte(want) {
   754  					t.Errorf("got span index %d, want %d\n", b.span(j).TraceID[0], want)
   755  				}
   756  			}
   757  		}
   758  	}
   759  	// expand the bucket
   760  	b.resize(20)
   761  	if b.size() != 5 {
   762  		t.Fatalf("after resizing upwards: got bucket size %d, want 5\n", b.size())
   763  	}
   764  	for i := 0; i < 5; i++ {
   765  		want := 6 + i
   766  		if b.span(i).TraceID[0] != byte(want) {
   767  			t.Errorf("after resizing upwards: got span index %d, want %d\n", b.span(i).TraceID[0], want)
   768  		}
   769  	}
   770  	// shrink the bucket
   771  	b.resize(3)
   772  	if b.size() != 3 {
   773  		t.Fatalf("after resizing downwards: got bucket size %d, want 3\n", b.size())
   774  	}
   775  	for i := 0; i < 3; i++ {
   776  		want := 8 + i
   777  		if b.span(i).TraceID[0] != byte(want) {
   778  			t.Errorf("after resizing downwards: got span index %d, want %d\n", b.span(i).TraceID[0], want)
   779  		}
   780  	}
   781  }
   782  
   783  type exporter map[string]*SpanData
   784  
   785  func (e exporter) ExportSpan(s *SpanData) {
   786  	e[s.Name] = s
   787  }
   788  
   789  func Test_Issue328_EndSpanTwice(t *testing.T) {
   790  	spans := make(exporter)
   791  	RegisterExporter(&spans)
   792  	defer UnregisterExporter(&spans)
   793  	ctx := context.Background()
   794  	ctx, span := StartSpan(ctx, "span-1", WithSampler(AlwaysSample()))
   795  	span.End()
   796  	span.End()
   797  	UnregisterExporter(&spans)
   798  	if len(spans) != 1 {
   799  		t.Fatalf("expected only a single span, got %#v", spans)
   800  	}
   801  }
   802  
   803  func TestStartSpanAfterEnd(t *testing.T) {
   804  	spans := make(exporter)
   805  	RegisterExporter(&spans)
   806  	defer UnregisterExporter(&spans)
   807  	ctx, span0 := StartSpan(context.Background(), "parent", WithSampler(AlwaysSample()))
   808  	ctx1, span1 := StartSpan(ctx, "span-1", WithSampler(AlwaysSample()))
   809  	span1.End()
   810  	// Start a new span with the context containing span-1
   811  	// even though span-1 is ended, we still add this as a new child of span-1
   812  	_, span2 := StartSpan(ctx1, "span-2", WithSampler(AlwaysSample()))
   813  	span2.End()
   814  	span0.End()
   815  	UnregisterExporter(&spans)
   816  	if got, want := len(spans), 3; got != want {
   817  		t.Fatalf("len(%#v) = %d; want %d", spans, got, want)
   818  	}
   819  	if got, want := spans["span-1"].TraceID, spans["parent"].TraceID; got != want {
   820  		t.Errorf("span-1.TraceID=%q; want %q", got, want)
   821  	}
   822  	if got, want := spans["span-2"].TraceID, spans["parent"].TraceID; got != want {
   823  		t.Errorf("span-2.TraceID=%q; want %q", got, want)
   824  	}
   825  	if got, want := spans["span-1"].ParentSpanID, spans["parent"].SpanID; got != want {
   826  		t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want)
   827  	}
   828  	if got, want := spans["span-2"].ParentSpanID, spans["span-1"].SpanID; got != want {
   829  		t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want)
   830  	}
   831  }
   832  
   833  func TestChildSpanCount(t *testing.T) {
   834  	spans := make(exporter)
   835  	RegisterExporter(&spans)
   836  	defer UnregisterExporter(&spans)
   837  	ctx, span0 := StartSpan(context.Background(), "parent", WithSampler(AlwaysSample()))
   838  	ctx1, span1 := StartSpan(ctx, "span-1", WithSampler(AlwaysSample()))
   839  	_, span2 := StartSpan(ctx1, "span-2", WithSampler(AlwaysSample()))
   840  	span2.End()
   841  	span1.End()
   842  
   843  	_, span3 := StartSpan(ctx, "span-3", WithSampler(AlwaysSample()))
   844  	span3.End()
   845  	span0.End()
   846  	UnregisterExporter(&spans)
   847  	if got, want := len(spans), 4; got != want {
   848  		t.Fatalf("len(%#v) = %d; want %d", spans, got, want)
   849  	}
   850  	if got, want := spans["span-3"].ChildSpanCount, 0; got != want {
   851  		t.Errorf("span-3.ChildSpanCount=%q; want %q", got, want)
   852  	}
   853  	if got, want := spans["span-2"].ChildSpanCount, 0; got != want {
   854  		t.Errorf("span-2.ChildSpanCount=%q; want %q", got, want)
   855  	}
   856  	if got, want := spans["span-1"].ChildSpanCount, 1; got != want {
   857  		t.Errorf("span-1.ChildSpanCount=%q; want %q", got, want)
   858  	}
   859  	if got, want := spans["parent"].ChildSpanCount, 2; got != want {
   860  		t.Errorf("parent.ChildSpanCount=%q; want %q", got, want)
   861  	}
   862  }
   863  
   864  func TestNilSpanEnd(t *testing.T) {
   865  	var span *Span
   866  	span.End()
   867  }
   868  
   869  func TestExecutionTracerTaskEnd(t *testing.T) {
   870  	var n uint64
   871  	executionTracerTaskEnd := func() {
   872  		atomic.AddUint64(&n, 1)
   873  	}
   874  
   875  	var spans []*span
   876  	_, s := StartSpan(context.Background(), "foo", WithSampler(NeverSample()))
   877  	sp := s.internal.(*span)
   878  	sp.executionTracerTaskEnd = executionTracerTaskEnd
   879  	spans = append(spans, sp) // never sample
   880  
   881  	_, s = StartSpanWithRemoteParent(context.Background(), "foo", SpanContext{
   882  		TraceID:      TraceID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
   883  		SpanID:       SpanID{0, 1, 2, 3, 4, 5, 6, 7},
   884  		TraceOptions: 0,
   885  	})
   886  	sp = s.internal.(*span)
   887  	sp.executionTracerTaskEnd = executionTracerTaskEnd
   888  	spans = append(spans, sp) // parent not sampled
   889  
   890  	_, s = StartSpan(context.Background(), "foo", WithSampler(AlwaysSample()))
   891  	sp = s.internal.(*span)
   892  	sp.executionTracerTaskEnd = executionTracerTaskEnd
   893  	spans = append(spans, sp) // always sample
   894  
   895  	for _, span := range spans {
   896  		span.End()
   897  	}
   898  	if got, want := n, uint64(len(spans)); got != want {
   899  		t.Fatalf("Execution tracer task ended for %v spans; want %v", got, want)
   900  	}
   901  }
   902  

View as plain text