1// Copyright (c) 2019 The Jaeger 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
15package jaeger
16
17import (
18	"fmt"
19	"testing"
20
21	"github.com/opentracing/opentracing-go"
22	"github.com/opentracing/opentracing-go/ext"
23	"github.com/stretchr/testify/assert"
24	"github.com/stretchr/testify/require"
25)
26
27var (
28	_ Sampler   = new(SamplerV2Base)
29	_ SamplerV2 = new(ConstSampler)
30	_ SamplerV2 = new(ProbabilisticSampler)
31	_ SamplerV2 = new(RateLimitingSampler)
32	_ SamplerV2 = new(PerOperationSampler)
33	// Note: GuaranteedThroughputProbabilisticSampler is currently not V2
34
35	_ Sampler   = new(retryableTestSampler)
36	_ SamplerV2 = new(retryableTestSampler)
37)
38
39// retryableTestSampler leaves the sampling unfinalized (retryable) after OnCreateSpan,
40// and makes it final after OnSetOperationName.
41type retryableTestSampler struct {
42	SamplerV2Base
43	decision bool
44	tags     []Tag
45}
46
47func newRetryableSampler(decision bool) *retryableTestSampler {
48	return &retryableTestSampler{
49		decision: decision,
50		tags:     makeSamplerTags("retryable", decision),
51	}
52}
53
54func (s *retryableTestSampler) OnCreateSpan(span *Span) SamplingDecision {
55	return SamplingDecision{Sample: s.decision, Retryable: true, Tags: s.tags}
56}
57
58func (s *retryableTestSampler) OnSetOperationName(span *Span, operationName string) SamplingDecision {
59	return SamplingDecision{Sample: s.decision, Retryable: false, Tags: s.tags}
60}
61
62func (s *retryableTestSampler) OnSetTag(span *Span, key string, value interface{}) SamplingDecision {
63	return SamplingDecision{Sample: s.decision, Retryable: true, Tags: s.tags}
64}
65
66func (s *retryableTestSampler) OnFinishSpan(span *Span) SamplingDecision {
67	return SamplingDecision{Sample: s.decision, Retryable: true, Tags: s.tags}
68}
69
70func TestSpanRemainsWriteable(t *testing.T) {
71	tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter())
72	defer closer.Close()
73
74	span := tracer.StartSpan("op1").(*Span)
75	assert.True(t, span.isWriteable(), "span is writeable when created")
76	assert.False(t, span.isSamplingFinalized(), "span is not finalized when created")
77
78	span.SetTag("k1", "v1")
79	assert.True(t, span.isWriteable(), "span is writeable after setting tags")
80	assert.Equal(t, opentracing.Tags{"k1": "v1"}, span.Tags())
81
82	span.SetOperationName("op2")
83	assert.False(t, span.isWriteable(), "span is not writeable after sampler returns retryable=true")
84	assert.True(t, span.isSamplingFinalized(), "span is finalized after sampler returns retryable=true")
85}
86
87func TestSpanSharesSamplingStateWithChildren(t *testing.T) {
88	tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter())
89	defer closer.Close()
90
91	sp1 := tracer.StartSpan("op1").(*Span)
92	assert.False(t, sp1.isSamplingFinalized(), "span is not finalized when created")
93
94	sp2 := tracer.StartSpan("op2", opentracing.ChildOf(sp1.Context())).(*Span)
95	assert.False(t, sp2.isSamplingFinalized(), "child span is not finalized when created")
96
97	sp2.SetOperationName("op3")
98	assert.True(t, sp2.isSamplingFinalized(), "child span is finalized after changing operation name")
99	assert.True(t, sp1.isSamplingFinalized(), "parent span is also finalized after child is finalized")
100}
101
102func TestSamplingIsFinalizedOnSamplingPriorityTag(t *testing.T) {
103	tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter())
104	defer closer.Close()
105
106	tests := []struct {
107		name            string
108		priority        uint16
109		expectedSampled bool
110		expectedTags    opentracing.Tags
111	}{
112		{
113			name:            "sampling.priority=1",
114			priority:        1,
115			expectedSampled: true,
116			expectedTags:    opentracing.Tags{"sampling.priority": uint16(1)},
117		},
118		{
119			name:            "sampling.priority=0",
120			expectedSampled: false,
121			priority:        0,
122			expectedTags:    opentracing.Tags{},
123		},
124	}
125	for _, tt := range tests {
126		t.Run(tt.name, func(t *testing.T) {
127			span := tracer.StartSpan("op1").(*Span)
128			assert.False(t, span.isSamplingFinalized(), "span is not finalized when created")
129
130			ext.SamplingPriority.Set(span, tt.priority)
131			assert.True(t, span.isSamplingFinalized(), "span is finalized after sampling.priority tag")
132			assert.Equal(t, tt.expectedSampled, span.context.IsSampled(), "span is sampled after sampling.priority tag")
133			assert.Equal(t, tt.expectedTags, span.Tags(), "sampling.priority tag in the span")
134		})
135	}
136}
137
138func TestSamplingIsFinalizedWithV1Samplers(t *testing.T) {
139	probabilistic, err := NewProbabilisticSampler(0.5)
140	require.NoError(t, err)
141	samplers := []Sampler{
142		NewConstSampler(false),
143		probabilistic,
144		NewRateLimitingSampler(1),
145	}
146	for _, sampler := range samplers {
147		t.Run(fmt.Sprintf("%s", sampler), func(t *testing.T) {
148			tracer, closer := NewTracer("test-service", sampler, NewNullReporter())
149			defer closer.Close()
150
151			span := tracer.StartSpan("op1").(*Span)
152			assert.True(t, span.isSamplingFinalized(), "span is finalized when created with V1 sampler")
153		})
154	}
155}
156
157func TestSamplingIsNotFinalizedWhenContextIsInjected(t *testing.T) {
158	tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter())
159	defer closer.Close()
160
161	span := tracer.StartSpan("op1").(*Span)
162	assert.False(t, span.isSamplingFinalized(), "span is not finalized when created")
163
164	err := tracer.Inject(span.Context(), opentracing.TextMap, opentracing.TextMapCarrier{})
165	require.NoError(t, err)
166
167	assert.False(t, span.isSamplingFinalized(), "span is not finalized after context is serialized")
168}
169
170func TestSamplingIsFinalizedInChildSpanOfRemoteParent(t *testing.T) {
171	for _, sampled := range []bool{false, true} {
172		t.Run(fmt.Sprintf("sampled=%t", sampled), func(t *testing.T) {
173			tracer, closer := NewTracer("test-service", newRetryableSampler(sampled), NewNullReporter())
174			defer closer.Close()
175
176			span := tracer.StartSpan("parent").(*Span)
177			assert.False(t, span.isSamplingFinalized(), "span is not finalized when created")
178			assert.Equal(t, sampled, span.context.IsSampled())
179
180			carrier := opentracing.TextMapCarrier{}
181			err := tracer.Inject(span.Context(), opentracing.TextMap, carrier)
182			require.NoError(t, err)
183
184			parentContext, err := tracer.Extract(opentracing.TextMap, carrier)
185			require.NoError(t, err)
186			childSpan := tracer.StartSpan("child", opentracing.ChildOf(parentContext)).(*Span)
187			assert.True(t, childSpan.isSamplingFinalized(), "child span is finalized")
188			assert.Equal(t, sampled, childSpan.context.IsSampled())
189		})
190	}
191}
192
193func TestSpanIsWriteableIfSampledOrNotFinalized(t *testing.T) {
194	tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter())
195	defer closer.Close()
196
197	span := tracer.StartSpan("span").(*Span)
198	assert.False(t, span.isSamplingFinalized(), "span is not finalized when created")
199	assert.False(t, span.context.IsSampled(), "span is not sampled")
200	assert.True(t, span.isWriteable(), "span is writeable")
201
202	tracer.(*Tracer).sampler = NewConstSampler(true)
203	span = tracer.StartSpan("span").(*Span)
204	assert.True(t, span.isSamplingFinalized(), "span is finalized when created")
205	assert.True(t, span.context.IsSampled(), "span is sampled")
206	assert.True(t, span.isWriteable(), "span is writeable")
207}
208
209func TestSetOperationOverridesOperationOnSampledSpan(t *testing.T) {
210	tracer, closer := NewTracer("test-service", NewConstSampler(true), NewNullReporter())
211	defer closer.Close()
212
213	span := tracer.StartSpan("op1").(*Span)
214	assert.True(t, span.isSamplingFinalized(), "span is finalized when created")
215	assert.True(t, span.context.IsSampled(), "span is sampled")
216	assert.Equal(t, "op1", span.OperationName())
217	assert.Equal(t, makeSamplerTags("const", true), span.tags)
218
219	span.tags = []Tag{} // easier to check that tags are not re-added
220	span.SetOperationName("op2")
221	assert.True(t, span.isSamplingFinalized(), "span is finalized when created")
222	assert.True(t, span.context.IsSampled(), "span is sampled")
223	assert.Equal(t, "op2", span.OperationName())
224	assert.Equal(t, []Tag{}, span.tags, "sampling tags are not re-added")
225}
226