// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using System; using System.Collections; using System.Collections.Generic; using System.IO; using global::OpenTracing; using global::OpenTracing.Propagation; using Moq; using OpenTelemetry.Context.Propagation; using Xunit; namespace OpenTelemetry.Shims.OpenTracing.Tests { public class TracerShimTests { [Fact] public void CtorArgumentValidation() { // null tracer and text format Assert.Throws(() => new TracerShim(null, null)); // null tracer Assert.Throws(() => new TracerShim(null, new TraceContextFormat())); // null context format var tracerMock = new Mock(); Assert.Throws(() => new TracerShim(tracerMock.Object, null)); } [Fact] public void ScopeManager_NotNull() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); // Internals of the ScopeManagerShim tested elsewhere Assert.NotNull(shim.ScopeManager as ScopeManagerShim); } [Fact] public void BuildSpan_NotNull() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); // Internals of the SpanBuilderShim tested elsewhere Assert.NotNull(shim.BuildSpan("foo") as SpanBuilderShim); } [Fact] public void Inject_ArgumentValidation() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); var spanContextShim = new SpanContextShim(Defaults.GetOpenTelemetrySpanContext()); var mockFormat = new Mock>(); var mockCarrier = new Mock(); Assert.Throws(() => shim.Inject(null, mockFormat.Object, mockCarrier.Object)); Assert.Throws(() => shim.Inject(new Mock().Object, mockFormat.Object, mockCarrier.Object)); Assert.Throws(() => shim.Inject(spanContextShim, null, mockCarrier.Object)); Assert.Throws(() => shim.Inject(spanContextShim, mockFormat.Object, null)); } [Fact] public void Inject_UnknownFormatIgnored() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); var spanContextShim = new SpanContextShim(Defaults.GetOpenTelemetrySpanContext()); // Only two specific types of ITextMap are supported, and neither is a Mock. var mockCarrier = new Mock(); shim.Inject(spanContextShim, new Mock>().Object, mockCarrier.Object); // Verify that the carrier mock was never called. mockCarrier.Verify(x => x.Set(It.IsAny(), It.IsAny()), Times.Never); } [Fact] public void Extract_ArgumentValidation() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); Assert.Throws(() => shim.Extract(null, new Mock().Object)); Assert.Throws(() => shim.Extract(new Mock>().Object, null)); } [Fact] public void Extract_UnknownFormatIgnored() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); var spanContextShim = new SpanContextShim(Defaults.GetOpenTelemetrySpanContext()); // Only two specific types of ITextMap are supported, and neither is a Mock. var mockCarrier = new Mock(); var context = shim.Extract(new Mock>().Object, mockCarrier.Object); // Verify that the carrier mock was never called. mockCarrier.Verify(x => x.GetEnumerator(), Times.Never); } [Fact] public void Extract_InvalidTraceParent() { var tracerMock = new Mock(); var shim = new TracerShim(tracerMock.Object, new TraceContextFormat()); var mockCarrier = new Mock(); // The ProxyTracer uses OpenTelemetry.Context.Propagation.TraceContextFormat, so we need to satisfy the traceparent key at the least var carrierMap = new Dictionary { // This is an invalid traceparent value { "traceparent", "unused" }, }; mockCarrier.Setup(x => x.GetEnumerator()).Returns(carrierMap.GetEnumerator()); var spanContextShim = shim.Extract(BuiltinFormats.TextMap, mockCarrier.Object) as SpanContextShim; // Verify that the carrier was called mockCarrier.Verify(x => x.GetEnumerator(), Times.Once); Assert.Null(spanContextShim); } [Fact] public void InjectExtract_TextMap_Ok() { var tracerMock = new Mock(); var carrier = new TextMapCarrier(); var spanContextShim = new SpanContextShim(Defaults.GetOpenTelemetrySpanContext()); var format = new TraceContextFormat(); var shim = new TracerShim(tracerMock.Object, format); // first inject shim.Inject(spanContextShim, BuiltinFormats.TextMap, carrier); // then extract var extractedSpanContext = shim.Extract(BuiltinFormats.TextMap, carrier); AssertOpenTracerSpanContextEqual(spanContextShim, extractedSpanContext); } private static void AssertOpenTracerSpanContextEqual(ISpanContext source, ISpanContext target) { Assert.Equal(source.TraceId, target.TraceId); Assert.Equal(source.SpanId, target.SpanId); // TODO BaggageItems are not implemented yet. } /// /// Simple ITextMap implementation used for the inject/extract tests. /// /// private class TextMapCarrier : ITextMap { private readonly Dictionary map = new Dictionary(); public IDictionary Map => this.map; public IEnumerator> GetEnumerator() => this.map.GetEnumerator(); public void Set(string key, string value) { this.map[key] = value; } IEnumerator IEnumerable.GetEnumerator() => this.map.GetEnumerator(); } /// /// Simple IBinary implementation used for the inject/extract tests. /// /// private class BinaryCarrier : IBinary { private readonly MemoryStream carrierStream = new MemoryStream(); public MemoryStream Get() => this.carrierStream; public void Set(MemoryStream stream) { this.carrierStream.SetLength(stream.Length); this.carrierStream.Seek(0, SeekOrigin.Begin); stream.CopyTo(this.carrierStream, (int)this.carrierStream.Length); } } } }