// // 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.Generic; using System.Diagnostics; using System.Linq; using global::OpenTracing.Tag; using OpenTelemetry.Trace; using Xunit; namespace OpenTelemetry.Shims.OpenTracing.Tests { public class SpanShimTests { private const string SpanName = "MySpanName/1"; private const string TracerName = "defaultactivitysource"; static SpanShimTests() { Activity.DefaultIdFormat = ActivityIdFormat.W3C; Activity.ForceDefaultIdFormat = true; var listener = new ActivityListener { ShouldListenTo = _ => true, GetRequestedDataUsingParentId = (ref ActivityCreationOptions options) => ActivityDataRequest.AllData, GetRequestedDataUsingContext = (ref ActivityCreationOptions options) => ActivityDataRequest.AllData, }; ActivitySource.AddActivityListener(listener); } [Fact] public void CtorArgumentValidation() { Assert.Throws(() => new SpanShim(null)); } [Fact] public void SpanContextIsNotNull() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); // ISpanContext validation handled in a separate test class Assert.NotNull(shim.Context); } [Fact] public void FinishSpan() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); shim.Finish(); Assert.NotEqual(default, shim.Span.Activity.Duration); } [Fact] public void FinishSpanUsingSpecificTimestamp() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); var endTime = DateTimeOffset.UtcNow; shim.Finish(endTime); Assert.Equal(endTime - shim.Span.Activity.StartTimeUtc, shim.Span.Activity.Duration); } [Fact] public void SetOperationName() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); // parameter validation Assert.Throws(() => shim.SetOperationName(null)); shim.SetOperationName("bar"); Assert.Equal("bar", shim.Span.Activity.DisplayName); } [Fact] public void GetBaggageItem() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); // parameter validation Assert.Throws(() => shim.GetBaggageItem(null)); // TODO - Method not implemented } [Fact] public void Log() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); shim.Log("foo"); Assert.Single(shim.Span.Activity.Events); var first = shim.Span.Activity.Events.First(); Assert.Equal("foo", first.Name); Assert.False(first.Tags.Any()); } [Fact] public void LogWithExplicitTimestamp() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); var now = DateTimeOffset.UtcNow; shim.Log(now, "foo"); Assert.Single(shim.Span.Activity.Events); var first = shim.Span.Activity.Events.First(); Assert.Equal("foo", first.Name); Assert.Equal(now, first.Timestamp); Assert.False(first.Tags.Any()); } [Fact] public void LogUsingFields() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.Log((IEnumerable>)null)); shim.Log(new List> { new KeyValuePair("foo", "bar"), }); // "event" is a special event name shim.Log(new List> { new KeyValuePair("event", "foo"), }); var first = shim.Span.Activity.Events.FirstOrDefault(); var last = shim.Span.Activity.Events.LastOrDefault(); Assert.Equal(2, shim.Span.Activity.Events.Count()); Assert.Equal(SpanShim.DefaultEventName, first.Name); Assert.True(first.Tags.Any()); Assert.Equal("foo", last.Name); Assert.False(last.Tags.Any()); } [Fact] public void LogUsingFieldsWithExplicitTimestamp() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.Log((IEnumerable>)null)); var now = DateTimeOffset.UtcNow; shim.Log(now, new List> { new KeyValuePair("foo", "bar"), }); // "event" is a special event name shim.Log(now, new List> { new KeyValuePair("event", "foo"), }); Assert.Equal(2, shim.Span.Activity.Events.Count()); var first = shim.Span.Activity.Events.First(); var last = shim.Span.Activity.Events.Last(); Assert.Equal(SpanShim.DefaultEventName, first.Name); Assert.True(first.Tags.Any()); Assert.Equal(now, first.Timestamp); Assert.Equal("foo", last.Name); Assert.False(last.Tags.Any()); Assert.Equal(now, last.Timestamp); } [Fact] public void SetTagStringValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((string)null, "foo")); shim.SetTag("foo", "bar"); Assert.Single(shim.Span.Activity.Tags); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.Equal("bar", shim.Span.Activity.Tags.First().Value); } [Fact] public void SetTagBoolValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((string)null, true)); shim.SetTag("foo", true); shim.SetTag(global::OpenTracing.Tag.Tags.Error.Key, true); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.True(bool.Parse(shim.Span.Activity.Tags.First().Value)); // A boolean tag named "error" is a special case that must be checked Assert.Equal(Status.Unknown, shim.Span.Activity.GetStatus()); // TODO: Activity object does not allow Tags update. Below lines of code needs to be enabled after .NET introducing SetTag on Activity. // shim.SetTag(global::OpenTracing.Tag.Tags.Error.Key, false); // Assert.Equal(Status.Ok, shim.Span.Activity.GetStatus()); } [Fact] public void SetTagIntValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((string)null, 1)); shim.SetTag("foo", 1); Assert.Single(shim.Span.Activity.Tags); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.Equal(1L, int.Parse(shim.Span.Activity.Tags.First().Value)); } [Fact] public void SetTagDoubleValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag(null, 1D)); shim.SetTag("foo", 1D); Assert.Single(shim.Span.Activity.Tags); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.Equal(1, double.Parse(shim.Span.Activity.Tags.First().Value)); } [Fact] public void SetTagBooleanTagValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((BooleanTag)null, true)); shim.SetTag(new BooleanTag("foo"), true); shim.SetTag(new BooleanTag(global::OpenTracing.Tag.Tags.Error.Key), true); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.True(bool.Parse(shim.Span.Activity.Tags.First().Value)); // A boolean tag named "error" is a special case that must be checked Assert.Equal(Status.Unknown, shim.Span.Activity.GetStatus()); // TODO: .NET does not allow Tags update. Below lines of code needs to be enabled after .NET introducing SetTag on Activity. // shim.SetTag(global::OpenTracing.Tag.Tags.Error.Key, false); // Assert.Equal(Status.Ok, shim.Span.Activity.GetStatus()); } [Fact] public void SetTagStringTagValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((StringTag)null, "foo")); shim.SetTag(new StringTag("foo"), "bar"); Assert.Single(shim.Span.Activity.Tags); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.Equal("bar", shim.Span.Activity.Tags.First().Value); } [Fact] public void SetTagIntTagValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((IntTag)null, 1)); shim.SetTag(new IntTag("foo"), 1); Assert.Single(shim.Span.Activity.Tags); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.Equal(1L, int.Parse(shim.Span.Activity.Tags.First().Value)); } [Fact] public void SetTagIntOrStringTagValue() { var tracer = TracerProvider.Default.GetTracer(TracerName); var shim = new SpanShim(tracer.StartSpan(SpanName)); Assert.Throws(() => shim.SetTag((IntOrStringTag)null, "foo")); shim.SetTag(new IntOrStringTag("foo"), 1); shim.SetTag(new IntOrStringTag("bar"), "baz"); Assert.Equal(2, shim.Span.Activity.Tags.Count()); Assert.Equal("foo", shim.Span.Activity.Tags.First().Key); Assert.Equal(1L, int.Parse(shim.Span.Activity.Tags.First().Value)); Assert.Equal("bar", shim.Span.Activity.Tags.Last().Key); Assert.Equal("baz", shim.Span.Activity.Tags.Last().Value); } } }