1 /*
2  * Copyright 2017, OpenCensus Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.opencensus.implcore.trace.propagation;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_FLAGS;
21 import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_PARENT_SPAN_ID;
22 import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_SAMPLED;
23 import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_SPAN_ID;
24 import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_TRACE_ID;
25 
26 import io.opencensus.trace.SpanContext;
27 import io.opencensus.trace.SpanId;
28 import io.opencensus.trace.TraceId;
29 import io.opencensus.trace.TraceOptions;
30 import io.opencensus.trace.propagation.SpanContextParseException;
31 import io.opencensus.trace.propagation.TextFormat.Getter;
32 import io.opencensus.trace.propagation.TextFormat.Setter;
33 import java.util.HashMap;
34 import java.util.Map;
35 import javax.annotation.Nullable;
36 import org.junit.Rule;
37 import org.junit.Test;
38 import org.junit.rules.ExpectedException;
39 import org.junit.runner.RunWith;
40 import org.junit.runners.JUnit4;
41 
42 /** Unit tests for {@link B3Format}. */
43 @RunWith(JUnit4.class)
44 public class B3FormatTest {
45   private static final String TRACE_ID_BASE16 = "ff000000000000000000000000000041";
46   private static final TraceId TRACE_ID = TraceId.fromLowerBase16(TRACE_ID_BASE16);
47   private static final String TRACE_ID_BASE16_EIGHT_BYTES = "0000000000000041";
48   private static final TraceId TRACE_ID_EIGHT_BYTES =
49       TraceId.fromLowerBase16("0000000000000000" + TRACE_ID_BASE16_EIGHT_BYTES);
50   private static final String SPAN_ID_BASE16 = "ff00000000000041";
51   private static final SpanId SPAN_ID = SpanId.fromLowerBase16(SPAN_ID_BASE16);
52   private static final byte TRACE_OPTIONS_BYTE = 1;
53   private static final TraceOptions TRACE_OPTIONS = TraceOptions.fromByte(TRACE_OPTIONS_BYTE);
54   private static final Setter<Map<String, String>> setter =
55       new Setter<Map<String, String>>() {
56         @Override
57         public void put(Map<String, String> carrier, String key, String value) {
58           carrier.put(key, value);
59         }
60       };
61   private static final Getter<Map<String, String>> getter =
62       new Getter<Map<String, String>>() {
63         @Nullable
64         @Override
65         public String get(Map<String, String> carrier, String key) {
66           return carrier.get(key);
67         }
68       };
69   private final B3Format b3Format = new B3Format();
70   @Rule public ExpectedException thrown = ExpectedException.none();
71 
72   @Test
serialize_SampledContext()73   public void serialize_SampledContext() {
74     Map<String, String> carrier = new HashMap<String, String>();
75     b3Format.inject(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS), carrier, setter);
76     assertThat(carrier)
77         .containsExactly(
78             X_B3_TRACE_ID, TRACE_ID_BASE16, X_B3_SPAN_ID, SPAN_ID_BASE16, X_B3_SAMPLED, "1");
79   }
80 
81   @Test
serialize_NotSampledContext()82   public void serialize_NotSampledContext() {
83     Map<String, String> carrier = new HashMap<String, String>();
84     b3Format.inject(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT), carrier, setter);
85     assertThat(carrier)
86         .containsExactly(X_B3_TRACE_ID, TRACE_ID_BASE16, X_B3_SPAN_ID, SPAN_ID_BASE16);
87   }
88 
89   @Test
parseMissingSampledAndMissingFlag()90   public void parseMissingSampledAndMissingFlag() throws SpanContextParseException {
91     Map<String, String> headersNotSampled = new HashMap<String, String>();
92     headersNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
93     headersNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
94     SpanContext spanContext = SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT);
95     assertThat(b3Format.extract(headersNotSampled, getter)).isEqualTo(spanContext);
96   }
97 
98   @Test
parseSampled()99   public void parseSampled() throws SpanContextParseException {
100     Map<String, String> headersSampled = new HashMap<String, String>();
101     headersSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
102     headersSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
103     headersSampled.put(X_B3_SAMPLED, "1");
104     assertThat(b3Format.extract(headersSampled, getter))
105         .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS));
106   }
107 
108   @Test
parseZeroSampled()109   public void parseZeroSampled() throws SpanContextParseException {
110     Map<String, String> headersNotSampled = new HashMap<String, String>();
111     headersNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
112     headersNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
113     headersNotSampled.put(X_B3_SAMPLED, "0");
114     assertThat(b3Format.extract(headersNotSampled, getter))
115         .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
116   }
117 
118   @Test
parseFlag()119   public void parseFlag() throws SpanContextParseException {
120     Map<String, String> headersFlagSampled = new HashMap<String, String>();
121     headersFlagSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
122     headersFlagSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
123     headersFlagSampled.put(X_B3_FLAGS, "1");
124     assertThat(b3Format.extract(headersFlagSampled, getter))
125         .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS));
126   }
127 
128   @Test
parseZeroFlag()129   public void parseZeroFlag() throws SpanContextParseException {
130     Map<String, String> headersFlagNotSampled = new HashMap<String, String>();
131     headersFlagNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
132     headersFlagNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
133     headersFlagNotSampled.put(X_B3_FLAGS, "0");
134     assertThat(b3Format.extract(headersFlagNotSampled, getter))
135         .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
136   }
137 
138   @Test
parseEightBytesTraceId()139   public void parseEightBytesTraceId() throws SpanContextParseException {
140     Map<String, String> headersEightBytes = new HashMap<String, String>();
141     headersEightBytes.put(X_B3_TRACE_ID, TRACE_ID_BASE16_EIGHT_BYTES);
142     headersEightBytes.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
143     headersEightBytes.put(X_B3_SAMPLED, "1");
144     assertThat(b3Format.extract(headersEightBytes, getter))
145         .isEqualTo(SpanContext.create(TRACE_ID_EIGHT_BYTES, SPAN_ID, TRACE_OPTIONS));
146   }
147 
148   @Test
parseEightBytesTraceId_NotSampledSpanContext()149   public void parseEightBytesTraceId_NotSampledSpanContext() throws SpanContextParseException {
150     Map<String, String> headersEightBytes = new HashMap<String, String>();
151     headersEightBytes.put(X_B3_TRACE_ID, TRACE_ID_BASE16_EIGHT_BYTES);
152     headersEightBytes.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
153     assertThat(b3Format.extract(headersEightBytes, getter))
154         .isEqualTo(SpanContext.create(TRACE_ID_EIGHT_BYTES, SPAN_ID, TraceOptions.DEFAULT));
155   }
156 
157   @Test
parseInvalidTraceId()158   public void parseInvalidTraceId() throws SpanContextParseException {
159     Map<String, String> invalidHeaders = new HashMap<String, String>();
160     invalidHeaders.put(X_B3_TRACE_ID, "abcdefghijklmnop");
161     invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
162     thrown.expect(SpanContextParseException.class);
163     thrown.expectMessage("Invalid input.");
164     b3Format.extract(invalidHeaders, getter);
165   }
166 
167   @Test
parseInvalidTraceId_Size()168   public void parseInvalidTraceId_Size() throws SpanContextParseException {
169     Map<String, String> invalidHeaders = new HashMap<String, String>();
170     invalidHeaders.put(X_B3_TRACE_ID, "0123456789abcdef00");
171     invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
172     thrown.expect(SpanContextParseException.class);
173     thrown.expectMessage("Invalid input.");
174     b3Format.extract(invalidHeaders, getter);
175   }
176 
177   @Test
parseMissingTraceId()178   public void parseMissingTraceId() throws SpanContextParseException {
179     Map<String, String> invalidHeaders = new HashMap<String, String>();
180     invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
181     thrown.expect(SpanContextParseException.class);
182     thrown.expectMessage("Missing X_B3_TRACE_ID.");
183     b3Format.extract(invalidHeaders, getter);
184   }
185 
186   @Test
parseInvalidSpanId()187   public void parseInvalidSpanId() throws SpanContextParseException {
188     Map<String, String> invalidHeaders = new HashMap<String, String>();
189     invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
190     invalidHeaders.put(X_B3_SPAN_ID, "abcdefghijklmnop");
191     thrown.expect(SpanContextParseException.class);
192     thrown.expectMessage("Invalid input.");
193     b3Format.extract(invalidHeaders, getter);
194   }
195 
196   @Test
parseInvalidSpanId_Size()197   public void parseInvalidSpanId_Size() throws SpanContextParseException {
198     Map<String, String> invalidHeaders = new HashMap<String, String>();
199     invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
200     invalidHeaders.put(X_B3_SPAN_ID, "0123456789abcdef00");
201     thrown.expect(SpanContextParseException.class);
202     thrown.expectMessage("Invalid input.");
203     b3Format.extract(invalidHeaders, getter);
204   }
205 
206   @Test
parseMissingSpanId()207   public void parseMissingSpanId() throws SpanContextParseException {
208     Map<String, String> invalidHeaders = new HashMap<String, String>();
209     invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
210     thrown.expect(SpanContextParseException.class);
211     thrown.expectMessage("Missing X_B3_SPAN_ID.");
212     b3Format.extract(invalidHeaders, getter);
213   }
214 
215   @Test
fields_list()216   public void fields_list() {
217     assertThat(b3Format.fields())
218         .containsExactly(
219             X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS);
220   }
221 }
222