2020from unittest import TestCase
2121from unittest .mock import Mock , patch
2222
23+ from opentelemetry import trace
2324from opentelemetry .baggage .propagation import W3CBaggagePropagator
25+ from opentelemetry .context .context import Context
2426from opentelemetry .environment_variables import OTEL_PROPAGATORS
2527from opentelemetry .trace .propagation .tracecontext import (
2628 TraceContextTextMapPropagator ,
@@ -44,6 +46,7 @@ def test_propagators(propagators):
4446 ** {"side_effect" : test_propagators }
4547 )
4648
49+ # pylint: disable=import-outside-toplevel
4750 import opentelemetry .propagate
4851
4952 reload (opentelemetry .propagate )
@@ -79,6 +82,7 @@ def test_propagators(propagators):
7982 ** {"side_effect" : test_propagators }
8083 )
8184
85+ # pylint: disable=import-outside-toplevel
8286 import opentelemetry .propagate
8387
8488 reload (opentelemetry .propagate )
@@ -88,6 +92,7 @@ def test_propagators(propagators):
8892 )
8993 def test_composite_propagators_error (self ):
9094
95+ # pylint: disable=import-outside-toplevel
9196 import opentelemetry .propagate
9297
9398 with self .assertRaises (Exception ):
@@ -97,3 +102,145 @@ def test_composite_propagators_error(self):
97102 "Failed to load configured propagator `unknown`" ,
98103 err .output [0 ],
99104 )
105+
106+
107+ class TestTraceContextTextMapPropagator (TestCase ):
108+ def setUp (self ):
109+ self .propagator = TraceContextTextMapPropagator ()
110+
111+ def traceparent_helper (
112+ self ,
113+ carrier ,
114+ ):
115+ # We purposefully start with an empty context so we can test later if anything is added to it.
116+ initial_context = Context ()
117+
118+ context = self .propagator .extract (carrier , context = initial_context )
119+ self .assertIsNotNone (context )
120+ self .assertIsInstance (context , Context )
121+
122+ return context
123+
124+ def traceparent_helper_generator (
125+ self ,
126+ version = 0x00 ,
127+ trace_id = 0x00000000000000000000000000000001 ,
128+ span_id = 0x0000000000000001 ,
129+ trace_flags = 0x00 ,
130+ suffix = "" ,
131+ ):
132+ traceparent = f"{ version :02x} -{ trace_id :032x} -{ span_id :016x} -{ trace_flags :02x} { suffix } "
133+ carrier = {"traceparent" : traceparent }
134+ return self .traceparent_helper (carrier )
135+
136+ def valid_traceparent_helper (
137+ self ,
138+ version = 0x00 ,
139+ trace_id = 0x00000000000000000000000000000001 ,
140+ span_id = 0x0000000000000001 ,
141+ trace_flags = 0x00 ,
142+ suffix = "" ,
143+ assert_context_msg = "A valid traceparent was provided, so the context should be non-empty." ,
144+ ):
145+ context = self .traceparent_helper_generator (
146+ version = version ,
147+ trace_id = trace_id ,
148+ span_id = span_id ,
149+ trace_flags = trace_flags ,
150+ suffix = suffix ,
151+ )
152+
153+ self .assertNotEqual (
154+ context ,
155+ Context (),
156+ assert_context_msg ,
157+ )
158+
159+ span = trace .get_current_span (context )
160+ self .assertIsNotNone (span )
161+ self .assertIsInstance (span , trace .span .Span )
162+
163+ span_context = span .get_span_context ()
164+ self .assertIsNotNone (span_context )
165+ self .assertIsInstance (span_context , trace .span .SpanContext )
166+
167+ # Note: No version in SpanContext, it is only used locally in TraceContextTextMapPropagator
168+ self .assertEqual (span_context .trace_id , trace_id )
169+ self .assertEqual (span_context .span_id , span_id )
170+ self .assertEqual (span_context .trace_flags , trace_flags )
171+
172+ self .assertIsInstance (span_context .trace_state , trace .TraceState )
173+ self .assertCountEqual (span_context .trace_state , [])
174+ self .assertEqual (span_context .is_remote , True )
175+
176+ return context , span , span_context
177+
178+ def invalid_traceparent_helper (
179+ self ,
180+ version = 0x00 ,
181+ trace_id = 0x00000000000000000000000000000001 ,
182+ span_id = 0x0000000000000001 ,
183+ trace_flags = 0x00 ,
184+ suffix = "" ,
185+ assert_context_msg = "An invalid traceparent was provided, so the context should still be empty." ,
186+ ):
187+ context = self .traceparent_helper_generator (
188+ version = version ,
189+ trace_id = trace_id ,
190+ span_id = span_id ,
191+ trace_flags = trace_flags ,
192+ suffix = suffix ,
193+ )
194+
195+ self .assertEqual (
196+ context ,
197+ Context (),
198+ assert_context_msg ,
199+ )
200+
201+ return context
202+
203+ def test_extract_nothing (self ):
204+ context = self .traceparent_helper (carrier = {})
205+ self .assertEqual (
206+ context ,
207+ {},
208+ "We didn't provide a valid traceparent, so we should still have an empty Context." ,
209+ )
210+
211+ def test_extract_simple_traceparent (self ):
212+ self .valid_traceparent_helper ()
213+
214+ # https://www.w3.org/TR/trace-context/#version
215+ def test_extract_version_forbidden_ff (self ):
216+ self .invalid_traceparent_helper (
217+ version = 0xFF ,
218+ assert_context_msg = "We provided ann invalid traceparent with a forbidden version=0xff, so the context should still be empty." ,
219+ )
220+
221+ # https://www.w3.org/TR/trace-context/#version-format
222+ def test_extract_version_00_with_unsupported_suffix (self ):
223+ self .invalid_traceparent_helper (
224+ suffix = "-f00" ,
225+ assert_context_msg = "We provided an invalid traceparent with version=0x00 and suffix information which is not supported in this version, so the context should still be empty." ,
226+ )
227+
228+ # https://www.w3.org/TR/trace-context/#versioning-of-traceparent
229+ # See the parsing of the sampled bit of flags.
230+ def test_extract_future_version_with_future_suffix_data (self ):
231+ self .valid_traceparent_helper (
232+ version = 0x99 ,
233+ suffix = "-f00" ,
234+ assert_context_msg = "We provided a traceparent that is possibly valid in the future with version=0x99 and suffix information, so the context be non-empty." ,
235+ )
236+
237+ # https://www.w3.org/TR/trace-context/#trace-id
238+ def test_extract_trace_id_invalid_all_zeros (self ):
239+ self .invalid_traceparent_helper (trace_id = 0 )
240+
241+ # https://www.w3.org/TR/trace-context/#parent-id
242+ def test_extract_span_id_invalid_all_zeros (self ):
243+ self .invalid_traceparent_helper (span_id = 0 )
244+
245+ def test_extract_non_decimal_trace_flags (self ):
246+ self .valid_traceparent_helper (trace_flags = 0xA0 )
0 commit comments