302 lines
12 KiB
C#
302 lines
12 KiB
C#
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer;
|
|
using Xunit;
|
|
using OtlpCommon = OpenTelemetry.Proto.Common.V1;
|
|
|
|
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests;
|
|
|
|
public class OtlpAttributeTests
|
|
{
|
|
[Fact]
|
|
public void NullValueAttribute()
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", null);
|
|
Assert.False(TryTransformTag(kvp, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void EmptyArrays()
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", Array.Empty<int>());
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
Assert.Empty(attribute.Value.ArrayValue.Values);
|
|
|
|
kvp = new KeyValuePair<string, object?>("key", Array.Empty<object>());
|
|
Assert.True(TryTransformTag(kvp, out attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
Assert.Empty(attribute.Value.ArrayValue.Values);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(sbyte.MaxValue)]
|
|
[InlineData(byte.MaxValue)]
|
|
[InlineData(short.MaxValue)]
|
|
[InlineData(ushort.MaxValue)]
|
|
[InlineData(int.MaxValue)]
|
|
[InlineData(uint.MaxValue)]
|
|
[InlineData(long.MaxValue)]
|
|
[InlineData(new sbyte[] { 1, 2, 3 })]
|
|
[InlineData(new byte[] { 1, 2, 3 })]
|
|
[InlineData(new short[] { 1, 2, 3 })]
|
|
[InlineData(new ushort[] { 1, 2, 3 })]
|
|
[InlineData(new int[] { 1, 2, 3 })]
|
|
[InlineData(new uint[] { 1, 2, 3 })]
|
|
[InlineData(new long[] { 1, 2, 3 })]
|
|
public void IntegralTypesSupported(object value)
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", value);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
|
|
switch (value)
|
|
{
|
|
case Array array:
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
var expectedArray = new long[array.Length];
|
|
for (var i = 0; i < array.Length; i++)
|
|
{
|
|
expectedArray[i] = Convert.ToInt64(array.GetValue(i));
|
|
}
|
|
|
|
Assert.Equal(expectedArray, attribute.Value.ArrayValue.Values.Select(x => x.IntValue));
|
|
break;
|
|
default:
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.IntValue, attribute.Value.ValueCase);
|
|
Assert.Equal(Convert.ToInt64(value), attribute.Value.IntValue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(float.MaxValue)]
|
|
[InlineData(double.MaxValue)]
|
|
[InlineData(new float[] { 1, 2, 3 })]
|
|
[InlineData(new double[] { 1, 2, 3 })]
|
|
public void FloatingPointTypesSupported(object value)
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", value);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
|
|
switch (value)
|
|
{
|
|
case Array array:
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
var expectedArray = new double[array.Length];
|
|
for (var i = 0; i < array.Length; i++)
|
|
{
|
|
expectedArray[i] = Convert.ToDouble(array.GetValue(i));
|
|
}
|
|
|
|
Assert.Equal(expectedArray, attribute.Value.ArrayValue.Values.Select(x => x.DoubleValue));
|
|
break;
|
|
default:
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.DoubleValue, attribute.Value.ValueCase);
|
|
Assert.Equal(Convert.ToDouble(value), attribute.Value.DoubleValue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData(new bool[] { true, false, true })]
|
|
public void BooleanTypeSupported(object value)
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", value);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
|
|
switch (value)
|
|
{
|
|
case Array array:
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
var expectedArray = new bool[array.Length];
|
|
for (var i = 0; i < array.Length; i++)
|
|
{
|
|
expectedArray[i] = Convert.ToBoolean(array.GetValue(i));
|
|
}
|
|
|
|
Assert.Equal(expectedArray, attribute.Value.ArrayValue.Values.Select(x => x.BoolValue));
|
|
break;
|
|
default:
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.BoolValue, attribute.Value.ValueCase);
|
|
Assert.Equal(Convert.ToBoolean(value), attribute.Value.BoolValue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(char.MaxValue)]
|
|
[InlineData("string")]
|
|
public void StringTypesSupported(object value)
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", value);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.StringValue, attribute.Value.ValueCase);
|
|
Assert.Equal(Convert.ToString(value), attribute.Value.StringValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void ObjectArrayTypesSupported()
|
|
{
|
|
var obj = new object();
|
|
var objectArray = new object?[] { null, "a", 'b', true, int.MaxValue, long.MaxValue, float.MaxValue, double.MaxValue, obj };
|
|
|
|
var kvp = new KeyValuePair<string, object?>("key", objectArray);
|
|
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.None, attribute.Value.ArrayValue.Values[0].ValueCase);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.StringValue, attribute.Value.ArrayValue.Values[1].ValueCase);
|
|
Assert.Equal("a", attribute.Value.ArrayValue.Values[1].StringValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.StringValue, attribute.Value.ArrayValue.Values[2].ValueCase);
|
|
Assert.Equal("b", attribute.Value.ArrayValue.Values[2].StringValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.BoolValue, attribute.Value.ArrayValue.Values[3].ValueCase);
|
|
Assert.True(attribute.Value.ArrayValue.Values[3].BoolValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.IntValue, attribute.Value.ArrayValue.Values[4].ValueCase);
|
|
Assert.Equal(int.MaxValue, attribute.Value.ArrayValue.Values[4].IntValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.IntValue, attribute.Value.ArrayValue.Values[5].ValueCase);
|
|
Assert.Equal(long.MaxValue, attribute.Value.ArrayValue.Values[5].IntValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.DoubleValue, attribute.Value.ArrayValue.Values[6].ValueCase);
|
|
Assert.Equal(float.MaxValue, attribute.Value.ArrayValue.Values[6].DoubleValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.DoubleValue, attribute.Value.ArrayValue.Values[7].ValueCase);
|
|
Assert.Equal(double.MaxValue, attribute.Value.ArrayValue.Values[7].DoubleValue);
|
|
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.StringValue, attribute.Value.ArrayValue.Values[8].ValueCase);
|
|
Assert.Equal(obj.ToString(), attribute.Value.ArrayValue.Values[8].StringValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void StringArrayTypesSupported()
|
|
{
|
|
var charArray = new char[] { 'a', 'b', 'c' };
|
|
var stringArray = new string?[] { "a", "b", "c", string.Empty, null };
|
|
|
|
var kvp = new KeyValuePair<string, object?>("key", charArray);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
Assert.Equal(charArray.Select(x => x.ToString()), attribute.Value.ArrayValue.Values.Select(x => x.StringValue));
|
|
|
|
kvp = new KeyValuePair<string, object?>("key", stringArray);
|
|
Assert.True(TryTransformTag(kvp, out attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
|
|
for (var i = 0; i < stringArray.Length; ++i)
|
|
{
|
|
var expectedValue = stringArray[i];
|
|
var expectedValueCase = expectedValue != null
|
|
? OtlpCommon.AnyValue.ValueOneofCase.StringValue
|
|
: OtlpCommon.AnyValue.ValueOneofCase.None;
|
|
|
|
Assert.Equal(expectedValueCase, attribute.Value.ArrayValue.Values[i].ValueCase);
|
|
if (expectedValueCase != OtlpCommon.AnyValue.ValueOneofCase.None)
|
|
{
|
|
Assert.Equal(expectedValue, attribute.Value.ArrayValue.Values[i].StringValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ToStringIsCalledForAllOtherTypes()
|
|
{
|
|
var testValues = new object[]
|
|
{
|
|
(nint)int.MaxValue,
|
|
(nuint)uint.MaxValue,
|
|
decimal.MaxValue,
|
|
new object(),
|
|
};
|
|
|
|
var testArrayValues = new object[]
|
|
{
|
|
new nint[] { 1, 2, 3 },
|
|
new nuint[] { 1, 2, 3 },
|
|
new decimal[] { 1, 2, 3 },
|
|
new object?[] { new object[3], new object(), null },
|
|
};
|
|
|
|
foreach (var value in testValues)
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", value);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.StringValue, attribute.Value.ValueCase);
|
|
Assert.Equal(value.ToString(), attribute.Value.StringValue);
|
|
}
|
|
|
|
foreach (var value in testArrayValues)
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", value);
|
|
Assert.True(TryTransformTag(kvp, out var attribute));
|
|
Assert.Equal(OtlpCommon.AnyValue.ValueOneofCase.ArrayValue, attribute.Value.ValueCase);
|
|
|
|
var array = value as Array;
|
|
Assert.NotNull(array);
|
|
for (var i = 0; i < attribute.Value.ArrayValue.Values.Count; ++i)
|
|
{
|
|
var expectedValue = array.GetValue(i)?.ToString();
|
|
var expectedValueCase = expectedValue != null
|
|
? OtlpCommon.AnyValue.ValueOneofCase.StringValue
|
|
: OtlpCommon.AnyValue.ValueOneofCase.None;
|
|
|
|
Assert.Equal(expectedValueCase, attribute.Value.ArrayValue.Values[i].ValueCase);
|
|
if (expectedValueCase != OtlpCommon.AnyValue.ValueOneofCase.None)
|
|
{
|
|
Assert.Equal(array.GetValue(i)!.ToString(), attribute.Value.ArrayValue.Values[i].StringValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ExceptionInToStringIsCaught()
|
|
{
|
|
var kvp = new KeyValuePair<string, object?>("key", new MyToStringMethodThrowsAnException());
|
|
Assert.False(TryTransformTag(kvp, out _));
|
|
|
|
kvp = new KeyValuePair<string, object?>("key", new object[] { 1, false, new MyToStringMethodThrowsAnException() });
|
|
Assert.False(TryTransformTag(kvp, out _));
|
|
}
|
|
|
|
private static bool TryTransformTag(KeyValuePair<string, object?> tag, [NotNullWhen(true)] out OtlpCommon.KeyValue? attribute)
|
|
{
|
|
ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState = new ProtobufOtlpTagWriter.OtlpTagWriterState
|
|
{
|
|
Buffer = new byte[1024],
|
|
WritePosition = 0,
|
|
};
|
|
|
|
if (ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, tag))
|
|
{
|
|
// Deserialize the ResourceSpans and validate the attributes.
|
|
using (var stream = new MemoryStream(otlpTagWriterState.Buffer, 0, otlpTagWriterState.WritePosition))
|
|
{
|
|
var keyValue = OtlpCommon.KeyValue.Parser.ParseFrom(stream);
|
|
Assert.NotNull(keyValue);
|
|
attribute = keyValue;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
attribute = null;
|
|
return false;
|
|
}
|
|
|
|
private class MyToStringMethodThrowsAnException
|
|
{
|
|
public override string ToString()
|
|
{
|
|
throw new Exception("Nope.");
|
|
}
|
|
}
|
|
}
|