From c8c1090f2e19ccdf19a465967d9a10069fd2ad4d Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 00:23:48 +0100 Subject: [PATCH 1/9] [CI Visibility] Adding nullability checks --- tracer/missing-nullability-files.csv | 11 ------- .../Agent/MessagePack/CIFormatterResolver.cs | 1 + .../CIVisibilityEventMessagePackFormatter.cs | 1 + .../CoveragePayloadMessagePackFormatter.cs | 1 + .../MessagePack/EventMessagePackFormatter.cs | 3 +- .../MessagePack/IEventMessagePackFormatter.cs | 1 + .../MessagePack/SpanMessagePackFormatter.cs | 31 +++++++++--------- .../TestCoverageMessagePackFormatter.cs | 1 + .../src/Datadog.Trace/Ci/CITracerManager.cs | 29 +++++++++-------- .../Ci/CITracerManagerFactory.cs | 9 +++--- tracer/src/Datadog.Trace/Ci/CodeOwners.cs | 32 +++++++++---------- tracer/src/Datadog.Trace/Ci/IEvent.cs | 1 + 12 files changed, 60 insertions(+), 61 deletions(-) diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index 5d3bd99227bd..75eab46d42d8 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -66,10 +66,6 @@ src/Datadog.Trace/AppSec/SecurityConstants.cs src/Datadog.Trace/AppSec/SpanExtensions.cs src/Datadog.Trace/AspNet/MimeTypes.cs src/Datadog.Trace/AspNet/TracingHttpModule.cs -src/Datadog.Trace/Ci/CITracerManager.cs -src/Datadog.Trace/Ci/CITracerManagerFactory.cs -src/Datadog.Trace/Ci/CodeOwners.cs -src/Datadog.Trace/Ci/IEvent.cs src/Datadog.Trace/ClrProfiler/AutomaticTracer.cs src/Datadog.Trace/ClrProfiler/CallTargetKind.cs src/Datadog.Trace/ClrProfiler/ClrNames.cs @@ -403,13 +399,6 @@ src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/Rule.cs src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/RuleMatch.cs src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/Tags.cs src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/WafMatch.cs -src/Datadog.Trace/Ci/Agent/MessagePack/CIFormatterResolver.cs -src/Datadog.Trace/Ci/Agent/MessagePack/CIVisibilityEventMessagePackFormatter.cs -src/Datadog.Trace/Ci/Agent/MessagePack/CoveragePayloadMessagePackFormatter.cs -src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs -src/Datadog.Trace/Ci/Agent/MessagePack/IEventMessagePackFormatter.cs -src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs -src/Datadog.Trace/Ci/Agent/MessagePack/TestCoverageMessagePackFormatter.cs src/Datadog.Trace/Ci/Agent/Payloads/CICodeCoveragePayload.cs src/Datadog.Trace/Ci/Agent/Payloads/CITestCyclePayload.cs src/Datadog.Trace/Ci/Agent/Payloads/CIVisibilityProtocolPayload.cs diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIFormatterResolver.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIFormatterResolver.cs index d22cf301e985..e3bbf5d9b431 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIFormatterResolver.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIFormatterResolver.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using Datadog.Trace.Ci.Agent.Payloads; diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIVisibilityEventMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIVisibilityEventMessagePackFormatter.cs index 3bb26d2d12b3..cb12ca766447 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIVisibilityEventMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIVisibilityEventMessagePackFormatter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.Ci.EventModel; using Datadog.Trace.Vendors.MessagePack; diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CoveragePayloadMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CoveragePayloadMessagePackFormatter.cs index 49f37108232c..114b22b51d02 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CoveragePayloadMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CoveragePayloadMessagePackFormatter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using Datadog.Trace.Ci.Agent.Payloads; diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs index 1987c6b35b23..1259e16f6da1 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using Datadog.Trace.Logging; @@ -18,7 +19,7 @@ internal abstract class EventMessagePackFormatter : IMessagePackFormatter protected static readonly byte[] VersionBytes = StringEncoding.UTF8.GetBytes("version"); protected static readonly byte[] ContentBytes = StringEncoding.UTF8.GetBytes("content"); - public EventMessagePackFormatter() + protected EventMessagePackFormatter() { _log = DatadogLogging.GetLoggerFor(GetType()); } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/IEventMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/IEventMessagePackFormatter.cs index 7aa187ec4b9d..200c4e46c225 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/IEventMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/IEventMessagePackFormatter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using Datadog.Trace.Ci.Coverage.Models.Tests; diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs index ee1fc7b19028..d365af1d7bcd 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; @@ -58,7 +59,7 @@ internal class SpanMessagePackFormatter : IMessagePackFormatter private readonly byte[][] _samplingPriorityValueBytes; private readonly byte[] _processIdNameBytes = StringEncoding.UTF8.GetBytes(Trace.Metrics.ProcessId); - private readonly byte[] _processIdValueBytes; + private readonly byte[]? _processIdValueBytes; private SpanMessagePackFormatter() { @@ -190,7 +191,7 @@ public int Serialize(ref byte[] bytes, int offset, Span value, IFormatterResolve offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _errorBytes); offset += MessagePackBinary.WriteByte(ref bytes, offset, (byte)(value.Error ? 1 : 0)); - ITagProcessor[] tagProcessors = null; + ITagProcessor[]? tagProcessors = null; if (context.TraceContext?.Tracer is Tracer tracer) { tagProcessors = tracer.TracerManager.TagProcessors; @@ -201,7 +202,7 @@ public int Serialize(ref byte[] bytes, int offset, Span value, IFormatterResolve return offset - originalOffset; } - private int SerializeTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[] tagProcessors) + private int SerializeTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) { int originalOffset = offset; @@ -213,7 +214,7 @@ private int SerializeTags(ref byte[] bytes, int offset, Span span, ITags tags, I // TAGS - private int WriteTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[] tagProcessors) + private int WriteTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) { int originalOffset = offset; var traceContext = span.Context.TraceContext; @@ -293,7 +294,7 @@ private int WriteTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagP } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteTag(ref byte[] bytes, ref int offset, string key, string value, ITagProcessor[] tagProcessors) + private void WriteTag(ref byte[] bytes, ref int offset, string key, string value, ITagProcessor[]? tagProcessors) { if (tagProcessors is not null) { @@ -308,11 +309,11 @@ private void WriteTag(ref byte[] bytes, ref int offset, string key, string value } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteTag(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, string value, ITagProcessor[] tagProcessors) + private void WriteTag(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, string value, ITagProcessor[]? tagProcessors) { if (tagProcessors is not null) { - string key = null; + string? key = null; for (var i = 0; i < tagProcessors.Length; i++) { tagProcessors[i]?.ProcessMeta(ref key, ref value); @@ -326,7 +327,7 @@ private void WriteTag(ref byte[] bytes, ref int offset, ReadOnlySpan keyBy // METRICS - private int WriteMetrics(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[] tagProcessors) + private int WriteMetrics(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) { int originalOffset = offset; @@ -385,7 +386,7 @@ private int WriteMetrics(ref byte[] bytes, int offset, Span span, ITags tags, IT } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMetric(ref byte[] bytes, ref int offset, string key, double value, ITagProcessor[] tagProcessors) + private void WriteMetric(ref byte[] bytes, ref int offset, string key, double value, ITagProcessor[]? tagProcessors) { if (tagProcessors is not null) { @@ -400,11 +401,11 @@ private void WriteMetric(ref byte[] bytes, ref int offset, string key, double va } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMetric(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, double value, ITagProcessor[] tagProcessors) + private void WriteMetric(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, double value, ITagProcessor[]? tagProcessors) { if (tagProcessors is not null) { - string key = null; + string? key = null; for (var i = 0; i < tagProcessors.Length; i++) { tagProcessors[i]?.ProcessMetric(ref key, ref value); @@ -424,14 +425,14 @@ public Span Deserialize(byte[] bytes, int offset, IFormatterResolver formatterRe internal struct TagWriter : IItemProcessor, IItemProcessor { private readonly SpanMessagePackFormatter _formatter; - private readonly ITagProcessor[] _tagProcessors; + private readonly ITagProcessor[]? _tagProcessors; public byte[] Bytes; public int Offset; public int Count; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal TagWriter(SpanMessagePackFormatter formatter, ITagProcessor[] tagProcessors, byte[] bytes, int offset) + internal TagWriter(SpanMessagePackFormatter formatter, ITagProcessor[]? tagProcessors, byte[] bytes, int offset) { _formatter = formatter; _tagProcessors = tagProcessors; @@ -474,14 +475,14 @@ public void Process(TagItem item) internal struct TraceTagWriter : TraceTagCollection.ITagEnumerator { private readonly SpanMessagePackFormatter _formatter; - private readonly ITagProcessor[] _tagProcessors; + private readonly ITagProcessor[]? _tagProcessors; public byte[] Bytes; public int Offset; public int Count; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal TraceTagWriter(SpanMessagePackFormatter formatter, ITagProcessor[] tagProcessors, byte[] bytes, int offset) + internal TraceTagWriter(SpanMessagePackFormatter formatter, ITagProcessor[]? tagProcessors, byte[] bytes, int offset) { _formatter = formatter; _tagProcessors = tagProcessors; diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/TestCoverageMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/TestCoverageMessagePackFormatter.cs index 6384461013de..abb17efeb86c 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/TestCoverageMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/TestCoverageMessagePackFormatter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.Ci.Coverage.Models.Tests; using Datadog.Trace.Vendors.MessagePack; diff --git a/tracer/src/Datadog.Trace/Ci/CITracerManager.cs b/tracer/src/Datadog.Trace/Ci/CITracerManager.cs index 5d782f4de46e..1fbd536e4ede 100644 --- a/tracer/src/Datadog.Trace/Ci/CITracerManager.cs +++ b/tracer/src/Datadog.Trace/Ci/CITracerManager.cs @@ -2,8 +2,10 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Datadog.Trace.Agent; using Datadog.Trace.Agent.DiscoveryService; @@ -68,24 +70,25 @@ private static Trace.Processors.ITraceProcessor[] GetProcessors(bool partialFlus { if (isCiVisibilityProtocol) { - return new Trace.Processors.ITraceProcessor[] - { + return + [ new Trace.Processors.NormalizerTraceProcessor(), new Trace.Processors.TruncatorTraceProcessor(), - new Processors.OriginTagTraceProcessor(partialFlushEnabled, true), - }; + new Processors.OriginTagTraceProcessor(partialFlushEnabled, true) + ]; } - return new Trace.Processors.ITraceProcessor[] - { + return + [ new Trace.Processors.NormalizerTraceProcessor(), new Trace.Processors.TruncatorTraceProcessor(), new Processors.TestSuiteVisibilityProcessor(), - new Processors.OriginTagTraceProcessor(partialFlushEnabled, false), - }; + new Processors.OriginTagTraceProcessor(partialFlushEnabled, false) + ]; } - private Span ProcessSpan(Span span) + [return:NotNullIfNotNull(nameof(span))] + private Span? ProcessSpan(Span? span) { if (span is null) { @@ -115,13 +118,13 @@ private Span ProcessSpan(Span span) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteEvent(IEvent @event) { - if (@event is TestEvent testEvent) + if (@event is TestEvent { Content: { } test } testEvent) { - testEvent.Content = ProcessSpan(testEvent.Content); + testEvent.Content = ProcessSpan(test); } - else if (@event is SpanEvent spanEvent) + else if (@event is SpanEvent { Content: { } span } spanEvent) { - spanEvent.Content = ProcessSpan(spanEvent.Content); + spanEvent.Content = ProcessSpan(span); } ((IEventWriter)AgentWriter).WriteEvent(@event); diff --git a/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs b/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs index d0bc1910e969..154a3723b54e 100644 --- a/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs +++ b/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; @@ -59,10 +60,8 @@ protected override TracerManager CreateTracerManagerFrom( { return new CITracerManager.LockedManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, defaultServiceName, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager); } - else - { - return new CITracerManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, defaultServiceName, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager); - } + + return new CITracerManager(settings, agentWriter, scopeManager, statsd, runtimeMetrics, logSubmissionManager, telemetry, discoveryService, dataStreamsManager, defaultServiceName, gitMetadataTagsProvider, traceSampler, spanSampler, remoteConfigurationManager, dynamicConfigurationManager, tracerFlareManager); } protected override ITelemetryController CreateTelemetryController(ImmutableTracerSettings settings, IDiscoveryService discoveryService) @@ -82,7 +81,7 @@ protected override ITraceSampler GetSampler(ImmutableTracerSettings settings) protected override bool ShouldEnableRemoteConfiguration(ImmutableTracerSettings settings) => false; - protected override IAgentWriter GetAgentWriter(ImmutableTracerSettings settings, IDogStatsd statsd, Action> updateSampleRates, IDiscoveryService discoveryService) + protected override IAgentWriter? GetAgentWriter(ImmutableTracerSettings settings, IDogStatsd statsd, Action> updateSampleRates, IDiscoveryService discoveryService) { // Check for agentless scenario if (_settings.Agentless) diff --git a/tracer/src/Datadog.Trace/Ci/CodeOwners.cs b/tracer/src/Datadog.Trace/Ci/CodeOwners.cs index c6cfbc9c413c..190fae7efa66 100644 --- a/tracer/src/Datadog.Trace/Ci/CodeOwners.cs +++ b/tracer/src/Datadog.Trace/Ci/CodeOwners.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; @@ -13,7 +14,7 @@ namespace Datadog.Trace.Ci { internal class CodeOwners { - private readonly IGrouping[] _sections; + private readonly IGrouping[] _sections; public CodeOwners(string filePath) { @@ -23,10 +24,10 @@ public CodeOwners(string filePath) } var entriesList = new List(); - var lines = File.ReadAllLines(filePath); var sectionsList = new List(); - string currentSectionName = null; - foreach (var line in lines) + string? currentSectionName = null; + var atSpan = "@".AsSpan(); + foreach (var line in File.ReadLines(filePath)) { if (string.IsNullOrEmpty(line) || line[0] == '#') { @@ -48,10 +49,9 @@ public CodeOwners(string filePath) var finalLine = line; var ownersList = new List(); - var terms = line.Split(new[] { ' ' }, StringSplitOptions.None); - for (var i = 0; i < terms.Length; i++) + foreach (var currentTermSplitValue in line.SplitIntoSpans(' ')) { - var currentTerm = terms[i]; + var currentTerm = currentTermSplitValue.AsSpan(); if (currentTerm.Length == 0) { continue; @@ -59,13 +59,13 @@ public CodeOwners(string filePath) // Teams and users handles starts with @ // Emails contains @ - if (currentTerm[0] == '@' || currentTerm.Contains("@")) + if (currentTerm[0] == '@' || currentTerm.Contains(atSpan, StringComparison.Ordinal)) { - ownersList.Add(currentTerm); - var pos = finalLine.IndexOf(currentTerm, StringComparison.Ordinal); + ownersList.Add(currentTerm.ToString()); + var pos = finalLine.AsSpan().IndexOf(currentTerm, StringComparison.Ordinal); if (pos > 0) { - finalLine = finalLine.Substring(0, pos) + finalLine.Substring(pos + currentTerm.Length); + finalLine = finalLine.Remove(pos, currentTerm.Length); } } } @@ -180,19 +180,19 @@ public CodeOwners(string filePath) internal readonly struct Entry { public readonly string Pattern; - public readonly string[] Owners; - public readonly string Section; + public readonly IEnumerable Owners; + public readonly string? Section; - public Entry(string pattern, string[] owners, string section) + public Entry(string pattern, IEnumerable owners, string? section) { Pattern = pattern; Owners = owners; Section = section; } - public string GetOwnersString() + public string? GetOwnersString() { - if (Owners is null || Owners.Length == 0) + if (Owners is null || !Owners.Any()) { return null; } diff --git a/tracer/src/Datadog.Trace/Ci/IEvent.cs b/tracer/src/Datadog.Trace/Ci/IEvent.cs index 48560fb4dc4c..b87ff7379b04 100644 --- a/tracer/src/Datadog.Trace/Ci/IEvent.cs +++ b/tracer/src/Datadog.Trace/Ci/IEvent.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci { From ac7ae72b678755dd33c48deb5f08bae727d75db8 Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 00:49:05 +0100 Subject: [PATCH 2/9] add more files --- tracer/missing-nullability-files.csv | 5 ----- .../src/Datadog.Trace/Ci/CITracerManager.cs | 22 ++++++++++++++----- .../Ci/Processors/OriginTagTraceProcessor.cs | 3 ++- .../TestSuiteVisibilityProcessor.cs | 10 +++++---- .../Datadog.Trace/Ci/Sampling/CISampler.cs | 1 + .../Ci/Telemetry/TelemetryHelper.cs | 3 ++- .../Processors/ITraceProcessor.cs | 5 +++-- 7 files changed, 31 insertions(+), 18 deletions(-) diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index 75eab46d42d8..db26e3d4b38c 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -147,7 +147,6 @@ src/Datadog.Trace/PlatformHelpers/ContainerMetadata.cs src/Datadog.Trace/PlatformHelpers/PlatformStrategy.cs src/Datadog.Trace/PlatformHelpers/ServiceFabric.cs src/Datadog.Trace/Processors/ITagProcessor.cs -src/Datadog.Trace/Processors/ITraceProcessor.cs src/Datadog.Trace/Processors/NormalizerTraceProcessor.cs src/Datadog.Trace/Processors/ObfuscatorTagsProcessor.cs src/Datadog.Trace/Processors/ObfuscatorTraceProcessor.cs @@ -266,9 +265,6 @@ src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs src/Datadog.Trace/Ci/Agent/CIWriterHttpSender.cs src/Datadog.Trace/Ci/Agent/ICIVisibilityProtocolWriterSender.cs src/Datadog.Trace/Ci/Agent/IEventWriter.cs -src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs -src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs -src/Datadog.Trace/Ci/Sampling/CISampler.cs src/Datadog.Trace/Ci/Tagging/TestModuleSpanTags.cs src/Datadog.Trace/Ci/Tagging/TestSessionSpanTags.cs src/Datadog.Trace/Ci/Tagging/TestSpanTags.cs @@ -279,7 +275,6 @@ src/Datadog.Trace/Ci/Tags/CodeCoverageTags.cs src/Datadog.Trace/Ci/Tags/CommonTags.cs src/Datadog.Trace/Ci/Tags/IntelligentTestRunnerTags.cs src/Datadog.Trace/Ci/Tags/TestTags.cs -src/Datadog.Trace/Ci/Telemetry/TelemetryHelper.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs src/Datadog.Trace/ClrProfiler/Helpers/HttpBypassHelper.cs src/Datadog.Trace/ClrProfiler/Helpers/Interception.cs diff --git a/tracer/src/Datadog.Trace/Ci/CITracerManager.cs b/tracer/src/Datadog.Trace/Ci/CITracerManager.cs index 1fbd536e4ede..09ec29ecd9fd 100644 --- a/tracer/src/Datadog.Trace/Ci/CITracerManager.cs +++ b/tracer/src/Datadog.Trace/Ci/CITracerManager.cs @@ -87,8 +87,7 @@ private static Trace.Processors.ITraceProcessor[] GetProcessors(bool partialFlus ]; } - [return:NotNullIfNotNull(nameof(span))] - private Span? ProcessSpan(Span? span) + private Span? ProcessSpan(Span span) { if (span is null) { @@ -104,7 +103,14 @@ private static Trace.Processors.ITraceProcessor[] GetProcessors(bool partialFlus try { - span = processor.Process(span); + if (processor.Process(span) is { } nSpan) + { + span = nSpan; + } + else + { + return null; + } } catch (Exception e) { @@ -120,11 +126,17 @@ public void WriteEvent(IEvent @event) { if (@event is TestEvent { Content: { } test } testEvent) { - testEvent.Content = ProcessSpan(test); + if (ProcessSpan(test) is { } content) + { + testEvent.Content = content; + } } else if (@event is SpanEvent { Content: { } span } spanEvent) { - spanEvent.Content = ProcessSpan(span); + if (ProcessSpan(span) is { } content) + { + spanEvent.Content = content; + } } ((IEventWriter)AgentWriter).WriteEvent(@event); diff --git a/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs b/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs index aa88c651065c..276021c2c4ec 100644 --- a/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs +++ b/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Threading; @@ -97,7 +98,7 @@ public Span Process(Span span) return span; } - public ITagProcessor GetTagProcessor() + public ITagProcessor? GetTagProcessor() { return null; } diff --git a/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs b/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs index fa34a97f7354..78675bb82288 100644 --- a/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs +++ b/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs @@ -2,9 +2,11 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Datadog.Trace.Logging; using Datadog.Trace.Processors; @@ -27,7 +29,7 @@ public ArraySegment Process(ArraySegment trace) return trace; } - Span[] spans = null; + Span[]? spans = null; var spIdx = 0; for (var i = trace.Offset; i < trace.Count + trace.Offset; i++) { @@ -43,10 +45,10 @@ public ArraySegment Process(ArraySegment trace) } } - return spans is null ? new ArraySegment(Array.Empty()) : new ArraySegment(spans, 0, spIdx); + return spans is null ? new ArraySegment([]) : new ArraySegment(spans, 0, spIdx); } - public Span Process(Span span) + public Span? Process(Span span) { // If agentless is enabled we don't filter anything. if (span is null) @@ -59,7 +61,7 @@ public Span Process(Span span) return span.Type is SpanTypes.TestSuite or SpanTypes.TestModule or SpanTypes.TestSession ? null : span; } - public ITagProcessor GetTagProcessor() + public ITagProcessor? GetTagProcessor() { return null; } diff --git a/tracer/src/Datadog.Trace/Ci/Sampling/CISampler.cs b/tracer/src/Datadog.Trace/Ci/Sampling/CISampler.cs index a0e9a371665e..942ec694ca65 100644 --- a/tracer/src/Datadog.Trace/Ci/Sampling/CISampler.cs +++ b/tracer/src/Datadog.Trace/Ci/Sampling/CISampler.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Collections.Generic; using Datadog.Trace.Sampling; diff --git a/tracer/src/Datadog.Trace/Ci/Telemetry/TelemetryHelper.cs b/tracer/src/Datadog.Trace/Ci/Telemetry/TelemetryHelper.cs index 8b9fc1e4f425..6934ca4d2680 100644 --- a/tracer/src/Datadog.Trace/Ci/Telemetry/TelemetryHelper.cs +++ b/tracer/src/Datadog.Trace/Ci/Telemetry/TelemetryHelper.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.Ci.CiEnvironment; using Datadog.Trace.Ci.Tags; @@ -16,7 +17,7 @@ internal static class TelemetryHelper /// /// Testing framework string /// MetricTags.CIVisibilityTestFramework - public static MetricTags.CIVisibilityTestFramework GetTelemetryTestingFrameworkEnum(string testingFramework) + public static MetricTags.CIVisibilityTestFramework GetTelemetryTestingFrameworkEnum(string? testingFramework) { return testingFramework switch { diff --git a/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs b/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs index 02db033a3823..9fa5241bbc80 100644 --- a/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs +++ b/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; @@ -11,8 +12,8 @@ internal interface ITraceProcessor { ArraySegment Process(ArraySegment trace); - Span Process(Span span); + Span? Process(Span span); - ITagProcessor GetTagProcessor(); + ITagProcessor? GetTagProcessor(); } } From ed90ea778bc30ddcbd36743597b70cdfa783b140 Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 10:32:37 +0100 Subject: [PATCH 3/9] more files --- tracer/missing-nullability-files.csv | 25 - .../Datadog.Trace/Ci/Agent/ApmAgentWriter.cs | 100 +-- .../Ci/Agent/CIVisibilityProtocolWriter.cs | 582 +++++++------- .../Ci/Agent/CIWriterFileSender.cs | 124 +-- .../Ci/Agent/CIWriterHttpSender.cs | 345 ++++---- .../ICIVisibilityProtocolWriterSender.cs | 10 +- .../Datadog.Trace/Ci/Agent/IEventWriter.cs | 16 +- .../CIEventMessagePackFormatter.cs | 304 ++++---- .../MessagePack/EventMessagePackFormatter.cs | 39 +- .../MessagePack/SpanMessagePackFormatter.cs | 734 +++++++++--------- .../Agent/Payloads/CICodeCoveragePayload.cs | 116 +-- .../Ci/Agent/Payloads/CITestCyclePayload.cs | 43 +- .../Payloads/CIVisibilityProtocolPayload.cs | 65 +- .../Ci/Agent/Payloads/EventPlatformPayload.cs | 272 +++---- .../Ci/Agent/Payloads/EventsBuffer.cs | 213 +++-- .../Ci/Agent/Payloads/MultipartPayload.cs | 95 ++- .../Attributes/AvoidCoverageAttribute.cs | 16 +- .../Attributes/CoveredAssemblyAttribute.cs | 16 +- .../Exceptions/PdbNotFoundException.cs | 24 +- .../Ci/Tagging/TestModuleSpanTags.cs | 23 +- .../Ci/Tagging/TestSessionSpanTags.cs | 69 +- .../Datadog.Trace/Ci/Tagging/TestSpanTags.cs | 33 +- .../Ci/Tagging/TestSuiteSpanTags.cs | 3 +- .../Ci/Tags/BenchmarkTestTags.cs | 1 + tracer/src/Datadog.Trace/Ci/Tags/BuildTags.cs | 1 + .../Datadog.Trace/Ci/Tags/CodeCoverageTags.cs | 1 + .../src/Datadog.Trace/Ci/Tags/CommonTags.cs | 1 + .../Ci/Tags/IntelligentTestRunnerTags.cs | 1 + tracer/src/Datadog.Trace/Ci/Tags/TestTags.cs | 1 + 29 files changed, 1626 insertions(+), 1647 deletions(-) diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index db26e3d4b38c..6235de6b1211 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -259,22 +259,6 @@ src/Datadog.Trace/Agent/Transports/MimeTypes.cs src/Datadog.Trace/Agent/Transports/SocketHandlerRequestFactory.cs src/Datadog.Trace/AppSec/Waf/WafConstants.cs src/Datadog.Trace/AppSec/Waf/WafReturnCode.cs -src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs -src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs -src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs -src/Datadog.Trace/Ci/Agent/CIWriterHttpSender.cs -src/Datadog.Trace/Ci/Agent/ICIVisibilityProtocolWriterSender.cs -src/Datadog.Trace/Ci/Agent/IEventWriter.cs -src/Datadog.Trace/Ci/Tagging/TestModuleSpanTags.cs -src/Datadog.Trace/Ci/Tagging/TestSessionSpanTags.cs -src/Datadog.Trace/Ci/Tagging/TestSpanTags.cs -src/Datadog.Trace/Ci/Tagging/TestSuiteSpanTags.cs -src/Datadog.Trace/Ci/Tags/BenchmarkTestTags.cs -src/Datadog.Trace/Ci/Tags/BuildTags.cs -src/Datadog.Trace/Ci/Tags/CodeCoverageTags.cs -src/Datadog.Trace/Ci/Tags/CommonTags.cs -src/Datadog.Trace/Ci/Tags/IntelligentTestRunnerTags.cs -src/Datadog.Trace/Ci/Tags/TestTags.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs src/Datadog.Trace/ClrProfiler/Helpers/HttpBypassHelper.cs src/Datadog.Trace/ClrProfiler/Helpers/Interception.cs @@ -394,15 +378,6 @@ src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/Rule.cs src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/RuleMatch.cs src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/Tags.cs src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/WafMatch.cs -src/Datadog.Trace/Ci/Agent/Payloads/CICodeCoveragePayload.cs -src/Datadog.Trace/Ci/Agent/Payloads/CITestCyclePayload.cs -src/Datadog.Trace/Ci/Agent/Payloads/CIVisibilityProtocolPayload.cs -src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs -src/Datadog.Trace/Ci/Agent/Payloads/EventsBuffer.cs -src/Datadog.Trace/Ci/Agent/Payloads/MultipartPayload.cs -src/Datadog.Trace/Ci/Coverage/Attributes/AvoidCoverageAttribute.cs -src/Datadog.Trace/Ci/Coverage/Attributes/CoveredAssemblyAttribute.cs -src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/AdoNetClientInstrumentMethodsAttribute.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/AdoNetConstants.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/AdoNetDefinitions.cs diff --git a/tracer/src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs b/tracer/src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs index 086b88f237ce..a23907e712e1 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; @@ -11,70 +12,69 @@ using Datadog.Trace.Ci.EventModel; using Datadog.Trace.Configuration; -namespace Datadog.Trace.Ci.Agent +namespace Datadog.Trace.Ci.Agent; + +/// +/// APM Agent Writer for CI Visibility +/// +internal class ApmAgentWriter : IEventWriter { - /// - /// APM Agent Writer for CI Visibility - /// - internal class ApmAgentWriter : IEventWriter + private const int DefaultMaxBufferSize = 1024 * 1024 * 10; + + [ThreadStatic] + private static Span[]? _spanArray; + private readonly AgentWriter _agentWriter; + + public ApmAgentWriter(ImmutableTracerSettings settings, Action> updateSampleRates, IDiscoveryService discoveryService, int maxBufferSize = DefaultMaxBufferSize) { - private const int DefaultMaxBufferSize = 1024 * 1024 * 10; + var partialFlushEnabled = settings.ExporterInternal.PartialFlushEnabledInternal; + var apiRequestFactory = TracesTransportStrategy.Get(settings.ExporterInternal); + var api = new Api(apiRequestFactory, null, updateSampleRates, partialFlushEnabled); + var statsAggregator = StatsAggregator.Create(api, settings, discoveryService); - [ThreadStatic] - private static Span[] _spanArray; - private readonly AgentWriter _agentWriter; + _agentWriter = new AgentWriter(api, statsAggregator, null, maxBufferSize: maxBufferSize, appsecStandaloneEnabled: settings.AppsecStandaloneEnabledInternal); + } - public ApmAgentWriter(ImmutableTracerSettings settings, Action> updateSampleRates, IDiscoveryService discoveryService, int maxBufferSize = DefaultMaxBufferSize) - { - var partialFlushEnabled = settings.ExporterInternal.PartialFlushEnabledInternal; - var apiRequestFactory = TracesTransportStrategy.Get(settings.ExporterInternal); - var api = new Api(apiRequestFactory, null, updateSampleRates, partialFlushEnabled); - var statsAggregator = StatsAggregator.Create(api, settings, discoveryService); + public ApmAgentWriter(IApi api, int maxBufferSize = DefaultMaxBufferSize) + { + _agentWriter = new AgentWriter(api, null, null, maxBufferSize: maxBufferSize); + } - _agentWriter = new AgentWriter(api, statsAggregator, null, maxBufferSize: maxBufferSize, appsecStandaloneEnabled: settings.AppsecStandaloneEnabledInternal); - } + public void WriteEvent(IEvent @event) + { + // To keep compatibility with the agent version of the payload, any IEvent conversion to span + // goes here. - public ApmAgentWriter(IApi api, int maxBufferSize = DefaultMaxBufferSize) + if (_spanArray is not { } spanArray) { - _agentWriter = new AgentWriter(api, null, null, maxBufferSize: maxBufferSize); + spanArray = new Span[1]; + _spanArray = spanArray; } - public void WriteEvent(IEvent @event) + if (CIVisibilityEventsFactory.GetSpan(@event) is { } span) { - // To keep compatibility with the agent version of the payload, any IEvent conversion to span - // goes here. - - if (_spanArray is not { } spanArray) - { - spanArray = new Span[1]; - _spanArray = spanArray; - } - - if (CIVisibilityEventsFactory.GetSpan(@event) is { } span) - { - spanArray[0] = span; - WriteTrace(new ArraySegment(spanArray)); - } + spanArray[0] = span; + WriteTrace(new ArraySegment(spanArray)); } + } - public Task FlushAndCloseAsync() - { - return _agentWriter.FlushAndCloseAsync(); - } + public Task FlushAndCloseAsync() + { + return _agentWriter.FlushAndCloseAsync(); + } - public Task FlushTracesAsync() - { - return _agentWriter.FlushTracesAsync(); - } + public Task FlushTracesAsync() + { + return _agentWriter.FlushTracesAsync(); + } - public Task Ping() - { - return _agentWriter.Ping(); - } + public Task Ping() + { + return _agentWriter.Ping(); + } - public void WriteTrace(ArraySegment trace) - { - _agentWriter.WriteTrace(trace); - } + public void WriteTrace(ArraySegment trace) + { + _agentWriter.WriteTrace(trace); } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs b/tracer/src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs index 3e328210ab09..160f2f91abea 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/CIVisibilityProtocolWriter.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Concurrent; @@ -16,366 +17,365 @@ using Datadog.Trace.Util; using Datadog.Trace.Vendors.MessagePack; -namespace Datadog.Trace.Ci.Agent +namespace Datadog.Trace.Ci.Agent; + +/// +/// CI Visibility Protocol Writer +/// +/* + * Current Architecture of the writer: + * + * ┌────────────────────────────────────────────────────────────────┐ + * │ │ + * │ CIVisibilityProtocolWriter┌────────────────────────────────┐ │ + * │ │ Buffers │ │ + * │ ┌─┤ │ │ + * │ │ │ ┌────────────────────────────┐ │ │ + * │ │ │ │ CITestCyclePayload Buffer │ │ │ + * │ │ │ │ │ │ │ + * │ │ │ │ SpanEvent │ │ │ CITestCyclePayload Url + * │ │ │ │ TestEvent ├─┼───┼────────────────────────────► + * │ ┌───────────────┐ │ │ ├────────────────────────────┤ │ │ + * │ │ │ │ │ │ Items: inf Bytes: 5MB │ │ │ + * │ │ Event Queue │ │ │ └────────────────────────────┘ │ │ + * IEvent │ │ │ │ │ │ │ + * ───────┼──►│ ├─────┤►│ ┌────────────────────────────┐ │ │ + * │ │ Max: 25000 │ │ │ │ CICodeCoveragePayload Buf. │ │ │ + * │ │ │ │ │ │ │ │ │ + * │ └───────────────┘ │ │ │ CoveragePayload │ │ │ CICodeCoveragePayload Url + * │ │ │ │ ├─┼───┼────────────────────────────► + * │ │ │ ├────────────────────────────┤ │ │ + * │ │ │ │ Items: 100 Bytes: 50MB │ │ │ + * │ │ │ └────────────────────────────┘ │ │ + * │ │ │ │ │ + * │ │ │ Flush each sec or limit reach │ │ + * │ │ └──────────────────────────────┬─┘ │ + * │ │ │ │ + * │ └────────────────────────────────┘ │ + * │ 1 .. N Consumers │ + * │ │ + * │ Max N = 4 │ + * │ │ + * └────────────────────────────────────────────────────────────────┘ + */ +internal sealed class CIVisibilityProtocolWriter : IEventWriter { - /// - /// CI Visibility Protocol Writer - /// - /* - * Current Architecture of the writer: - * - * ┌────────────────────────────────────────────────────────────────┐ - * │ │ - * │ CIVisibilityProtocolWriter┌────────────────────────────────┐ │ - * │ │ Buffers │ │ - * │ ┌─┤ │ │ - * │ │ │ ┌────────────────────────────┐ │ │ - * │ │ │ │ CITestCyclePayload Buffer │ │ │ - * │ │ │ │ │ │ │ - * │ │ │ │ SpanEvent │ │ │ CITestCyclePayload Url - * │ │ │ │ TestEvent ├─┼───┼────────────────────────────► - * │ ┌───────────────┐ │ │ ├────────────────────────────┤ │ │ - * │ │ │ │ │ │ Items: inf Bytes: 5MB │ │ │ - * │ │ Event Queue │ │ │ └────────────────────────────┘ │ │ - * IEvent │ │ │ │ │ │ │ - * ───────┼──►│ ├─────┤►│ ┌────────────────────────────┐ │ │ - * │ │ Max: 25000 │ │ │ │ CICodeCoveragePayload Buf. │ │ │ - * │ │ │ │ │ │ │ │ │ - * │ └───────────────┘ │ │ │ CoveragePayload │ │ │ CICodeCoveragePayload Url - * │ │ │ │ ├─┼───┼────────────────────────────► - * │ │ │ ├────────────────────────────┤ │ │ - * │ │ │ │ Items: 100 Bytes: 50MB │ │ │ - * │ │ │ └────────────────────────────┘ │ │ - * │ │ │ │ │ - * │ │ │ Flush each sec or limit reach │ │ - * │ │ └──────────────────────────────┬─┘ │ - * │ │ │ │ - * │ └────────────────────────────────┘ │ - * │ 1 .. N Consumers │ - * │ │ - * │ Max N = 4 │ - * │ │ - * └────────────────────────────────────────────────────────────────┘ - */ - internal sealed class CIVisibilityProtocolWriter : IEventWriter + private const int DefaultBatchInterval = 2500; + private const int DefaultMaxItemsInQueue = 25000; + + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); + + private readonly BlockingCollection _eventQueue; + private readonly Buffers[] _buffersArray; + private readonly int _batchInterval; + + public CIVisibilityProtocolWriter( + CIVisibilitySettings settings, + ICIVisibilityProtocolWriterSender sender, + IFormatterResolver? formatterResolver = null, + int? concurrency = null, + int batchInterval = DefaultBatchInterval, + int maxItemsInQueue = DefaultMaxItemsInQueue) { - private const int DefaultBatchInterval = 2500; - private const int DefaultMaxItemsInQueue = 25000; - - private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); - - private readonly BlockingCollection _eventQueue; - private readonly Buffers[] _buffersArray; - private readonly int _batchInterval; - - public CIVisibilityProtocolWriter( - CIVisibilitySettings settings, - ICIVisibilityProtocolWriterSender sender, - IFormatterResolver formatterResolver = null, - int? concurrency = null, - int batchInterval = DefaultBatchInterval, - int maxItemsInQueue = DefaultMaxItemsInQueue) + _eventQueue = new BlockingCollection(maxItemsInQueue); + _batchInterval = batchInterval; + + // Concurrency Level is a simple algorithm where we select a number between 1 and 4 depending on the number of Logical Processor Count + // To scale the number of senders with a hard limit. + var concurrencyLevel = concurrency ?? Math.Min(Math.Max(Environment.ProcessorCount / 2, 1), 4); + _buffersArray = new Buffers[concurrencyLevel]; + for (var i = 0; i < _buffersArray.Length; i++) { - _eventQueue = new BlockingCollection(maxItemsInQueue); - _batchInterval = batchInterval; - - // Concurrency Level is a simple algorithm where we select a number between 1 and 4 depending on the number of Logical Processor Count - // To scale the number of senders with a hard limit. - var concurrencyLevel = concurrency ?? Math.Min(Math.Max(Environment.ProcessorCount / 2, 1), 4); - _buffersArray = new Buffers[concurrencyLevel]; - for (var i = 0; i < _buffersArray.Length; i++) - { - var buffers = new Buffers( - sender, - new CITestCyclePayload(settings, formatterResolver: formatterResolver), - new CICodeCoveragePayload(settings, formatterResolver: formatterResolver)); - _buffersArray[i] = buffers; - var tskFlush = Task.Run(() => InternalFlushEventsAsync(this, buffers)); - tskFlush.ContinueWith(t => Log.Error(t.Exception, "CIVisibilityProtocolWriter: Error in sending ci visibility events"), TaskContinuationOptions.OnlyOnFaulted); - _buffersArray[i].SetFlushTask(tskFlush); - } + var buffers = new Buffers( + sender, + new CITestCyclePayload(settings, formatterResolver: formatterResolver), + new CICodeCoveragePayload(settings, formatterResolver: formatterResolver)); + _buffersArray[i] = buffers; + var tskFlush = Task.Run(() => InternalFlushEventsAsync(this, buffers)); + tskFlush.ContinueWith(t => Log.Error(t.Exception, "CIVisibilityProtocolWriter: Error in sending ci visibility events"), TaskContinuationOptions.OnlyOnFaulted); + _buffersArray[i].SetFlushTask(tskFlush); + } - Log.Information("CIVisibilityProtocolWriter Initialized with concurrency level of: {ConcurrencyLevel}", concurrencyLevel); + Log.Information("CIVisibilityProtocolWriter Initialized with concurrency level of: {ConcurrencyLevel}", concurrencyLevel); + } + + public void WriteEvent(IEvent @event) + { + if (_eventQueue.IsAddingCompleted) + { + return; } - public void WriteEvent(IEvent @event) + try { - if (_eventQueue.IsAddingCompleted) - { - return; - } + _eventQueue.Add(@event); + TelemetryFactory.Metrics.RecordCountCIVisibilityEventsEnqueueForSerialization(); + } + catch (Exception ex) + { + Log.Error(ex, "CIVisibilityProtocolWriter: Error Writing event in a queue."); + } + } - try - { - _eventQueue.Add(@event); - TelemetryFactory.Metrics.RecordCountCIVisibilityEventsEnqueueForSerialization(); - } - catch (Exception ex) - { - Log.Error(ex, "CIVisibilityProtocolWriter: Error Writing event in a queue."); - } + public async Task FlushAndCloseAsync() + { + _eventQueue.CompleteAdding(); + foreach (var buffers in _buffersArray) + { + buffers.FlushDelayEvent.Set(); + await buffers.FlushTaskCompletionSource.Task.ConfigureAwait(false); } + } - public async Task FlushAndCloseAsync() + public Task FlushTracesAsync() + { + if (_eventQueue.IsAddingCompleted) { - _eventQueue.CompleteAdding(); - foreach (var buffers in _buffersArray) - { - buffers.FlushDelayEvent.Set(); - await buffers.FlushTaskCompletionSource.Task.ConfigureAwait(false); - } + return Task.CompletedTask; } - public Task FlushTracesAsync() + try { - if (_eventQueue.IsAddingCompleted) + var countdownEvent = new AsyncCountdownEvent(_buffersArray.Length); + foreach (var buffer in _buffersArray) { - return Task.CompletedTask; + _eventQueue.Add(new WatermarkEvent(countdownEvent)); + buffer.FlushDelayEvent.Set(); } - try - { - var countdownEvent = new AsyncCountdownEvent(_buffersArray.Length); - foreach (var buffer in _buffersArray) - { - _eventQueue.Add(new WatermarkEvent(countdownEvent)); - buffer.FlushDelayEvent.Set(); - } - - return countdownEvent.WaitAsync(); - } - catch (Exception ex) - { - Log.Error(ex, "CIVisibilityProtocolWriter: Error Writing event in a queue."); - return Task.FromException(ex); - } + return countdownEvent.WaitAsync(); } - - public Task Ping() + catch (Exception ex) { - return Task.FromResult(true); + Log.Error(ex, "CIVisibilityProtocolWriter: Error Writing event in a queue."); + return Task.FromException(ex); } + } - public void WriteTrace(ArraySegment trace) + public Task Ping() + { + return Task.FromResult(true); + } + + public void WriteTrace(ArraySegment trace) + { + // Transform spans to events + for (var i = trace.Offset; i < trace.Count; i++) { - // Transform spans to events - for (var i = trace.Offset; i < trace.Count; i++) + if (trace.Array is { } array) { - if (trace.Array is { } array) - { - WriteEvent(CIVisibilityEventsFactory.FromSpan(array[i])); - } + WriteEvent(CIVisibilityEventsFactory.FromSpan(array[i])); } } + } - private static async Task InternalFlushEventsAsync(CIVisibilityProtocolWriter writer, Buffers buffers) + private static async Task InternalFlushEventsAsync(CIVisibilityProtocolWriter writer, Buffers buffers) + { + var batchInterval = writer._batchInterval; + var eventQueue = writer._eventQueue; + var index = buffers.Index; + var flushDelayEvent = buffers.FlushDelayEvent; + var ciTestCycleBuffer = buffers.CiTestCycleBuffer; + var ciTestCycleBufferWatch = buffers.CiTestCycleBufferWatch; + var ciCodeCoverageBuffer = buffers.CiCodeCoverageBuffer; + var ciCodeCoverageBufferWatch = buffers.CiCodeCoverageBufferWatch; + var completionSource = buffers.FlushTaskCompletionSource; + + Log.Debug("CIVisibilityProtocolWriter: InternalFlushEventsAsync/ Starting FlushEventsAsync loop"); + + while (!eventQueue.IsCompleted) { - var batchInterval = writer._batchInterval; - var eventQueue = writer._eventQueue; - var index = buffers.Index; - var flushDelayEvent = buffers.FlushDelayEvent; - var ciTestCycleBuffer = buffers.CiTestCycleBuffer; - var ciTestCycleBufferWatch = buffers.CiTestCycleBufferWatch; - var ciCodeCoverageBuffer = buffers.CiCodeCoverageBuffer; - var ciCodeCoverageBufferWatch = buffers.CiCodeCoverageBufferWatch; - var completionSource = buffers.FlushTaskCompletionSource; - - Log.Debug("CIVisibilityProtocolWriter: InternalFlushEventsAsync/ Starting FlushEventsAsync loop"); - - while (!eventQueue.IsCompleted) - { - AsyncCountdownEvent watermarkCountDown = null; + AsyncCountdownEvent? watermarkCountDown = null; - try + try + { + // Retrieve events from the queue and add them to the respective buffer. + ciTestCycleBufferWatch.Restart(); + ciCodeCoverageBufferWatch.Restart(); + while (eventQueue.TryTake(out var item)) { - // Retrieve events from the queue and add them to the respective buffer. - ciTestCycleBufferWatch.Restart(); - ciCodeCoverageBufferWatch.Restart(); - while (eventQueue.TryTake(out var item)) + // *** + // Check if the item is a watermark + // *** + if (item is WatermarkEvent watermarkEvent) { - // *** - // Check if the item is a watermark - // *** - if (item is WatermarkEvent watermarkEvent) - { - // Flush operation. - // We get the countdown event and exit this loop - // to flush buffers (in case there's any event) - watermarkCountDown = watermarkEvent.Countdown; - Log.Debug("CIVisibilityProtocolWriter: Watermark detected on [Buffer: {BufferIndex}]", index); - break; - } + // Flush operation. + // We get the countdown event and exit this loop + // to flush buffers (in case there's any event) + watermarkCountDown = watermarkEvent.Countdown; + Log.Debug("CIVisibilityProtocolWriter: Watermark detected on [Buffer: {BufferIndex}]", index); + break; + } - // *** - // Force a flush by time (When the queue was never empty in a complete BatchInterval period) - // *** - var flushWithInterval1 = buffers.FlushCiTestCycleBufferWhenTimeElapsedAsync(batchInterval); - var flushWithInterval2 = buffers.FlushCiCodeCoverageBufferWhenTimeElapsedAsync(batchInterval); - await flushWithInterval1.ConfigureAwait(false); - await flushWithInterval2.ConfigureAwait(false); - - // *** - // Add the item to the right buffer, and flush the buffer in case is needed. - // *** - if (ciTestCycleBuffer.CanProcessEvent(item)) + // *** + // Force a flush by time (When the queue was never empty in a complete BatchInterval period) + // *** + var flushWithInterval1 = buffers.FlushCiTestCycleBufferWhenTimeElapsedAsync(batchInterval); + var flushWithInterval2 = buffers.FlushCiCodeCoverageBufferWhenTimeElapsedAsync(batchInterval); + await flushWithInterval1.ConfigureAwait(false); + await flushWithInterval2.ConfigureAwait(false); + + // *** + // Add the item to the right buffer, and flush the buffer in case is needed. + // *** + if (ciTestCycleBuffer.CanProcessEvent(item)) + { + // The CITestCycle endpoint can process this event, we try to add it to the buffer. + // If the item cannot be added to the buffer but the buffer has events + // we assume that is full and needs to be flushed. + while (!ciTestCycleBuffer.TryProcessEvent(item) && ciTestCycleBuffer.HasEvents) { - // The CITestCycle endpoint can process this event, we try to add it to the buffer. - // If the item cannot be added to the buffer but the buffer has events - // we assume that is full and needs to be flushed. - while (!ciTestCycleBuffer.TryProcessEvent(item) && ciTestCycleBuffer.HasEvents) - { - await buffers.FlushCiTestCycleBufferAsync().ConfigureAwait(false); - } - - continue; + await buffers.FlushCiTestCycleBufferAsync().ConfigureAwait(false); } - if (ciCodeCoverageBuffer.CanProcessEvent(item)) - { - // The CICodeCoverage track/endpoint can process this event, we try to add it to the buffer. - // If the item cannot be added to the buffer but the buffer has events - // we assume that is full and needs to be flushed. - while (!ciCodeCoverageBuffer.TryProcessEvent(item) && ciCodeCoverageBuffer.HasEvents) - { - await buffers.FlushCiCodeCoverageBufferAsync().ConfigureAwait(false); - } - - continue; - } + continue; } - // After removing all items from the queue, we check if buffers needs to flushed. - var flush1 = buffers.FlushCiTestCycleBufferAsync(); - var flush2 = buffers.FlushCiCodeCoverageBufferAsync(); - await flush1.ConfigureAwait(false); - await flush2.ConfigureAwait(false); - - // If there's a flush watermark we marked as resolved. - if (watermarkCountDown is not null) + if (ciCodeCoverageBuffer.CanProcessEvent(item)) { - watermarkCountDown.Signal(); - Log.Debug("CIVisibilityProtocolWriter: Waiting for signals from other buffers [Buffer: {BufferIndex}]", index); - await watermarkCountDown.WaitAsync().ConfigureAwait(false); - Log.Debug("CIVisibilityProtocolWriter: Signals received, continue processing.. [Buffer: {BufferIndex}]", index); + // The CICodeCoverage track/endpoint can process this event, we try to add it to the buffer. + // If the item cannot be added to the buffer but the buffer has events + // we assume that is full and needs to be flushed. + while (!ciCodeCoverageBuffer.TryProcessEvent(item) && ciCodeCoverageBuffer.HasEvents) + { + await buffers.FlushCiCodeCoverageBufferAsync().ConfigureAwait(false); + } + + continue; } } - catch (ThreadAbortException ex) + + // After removing all items from the queue, we check if buffers needs to flushed. + var flush1 = buffers.FlushCiTestCycleBufferAsync(); + var flush2 = buffers.FlushCiCodeCoverageBufferAsync(); + await flush1.ConfigureAwait(false); + await flush2.ConfigureAwait(false); + + // If there's a flush watermark we marked as resolved. + if (watermarkCountDown is not null) { - completionSource?.TrySetException(ex); - watermarkCountDown?.Signal(); - throw; + watermarkCountDown.Signal(); + Log.Debug("CIVisibilityProtocolWriter: Waiting for signals from other buffers [Buffer: {BufferIndex}]", index); + await watermarkCountDown.WaitAsync().ConfigureAwait(false); + Log.Debug("CIVisibilityProtocolWriter: Signals received, continue processing.. [Buffer: {BufferIndex}]", index); } - catch (Exception ex) - { - Log.Warning(ex, "CIVisibilityProtocolWriter: Error in InternalFlushEventsAsync"); + } + catch (ThreadAbortException ex) + { + completionSource.TrySetException(ex); + watermarkCountDown?.Signal(); + throw; + } + catch (Exception ex) + { + Log.Warning(ex, "CIVisibilityProtocolWriter: Error in InternalFlushEventsAsync"); - // If there's a flush watermark we marked as resolved. - if (watermarkCountDown is not null) - { - watermarkCountDown.Signal(); - Log.Debug("CIVisibilityProtocolWriter: Waiting for signals from other buffers [Buffer: {BufferIndex}]", index); - await watermarkCountDown.WaitAsync().ConfigureAwait(false); - Log.Debug("CIVisibilityProtocolWriter: Signals received, continue processing.. [Buffer: {BufferIndex}]", index); - } + // If there's a flush watermark we marked as resolved. + if (watermarkCountDown is not null) + { + watermarkCountDown.Signal(); + Log.Debug("CIVisibilityProtocolWriter: Waiting for signals from other buffers [Buffer: {BufferIndex}]", index); + await watermarkCountDown.WaitAsync().ConfigureAwait(false); + Log.Debug("CIVisibilityProtocolWriter: Signals received, continue processing.. [Buffer: {BufferIndex}]", index); } - finally + } + finally + { + if (watermarkCountDown is null) { - if (watermarkCountDown is null) - { - // In case there's no flush watermark, we wait before start processing new events. - await flushDelayEvent.WaitAsync(batchInterval).ConfigureAwait(false); - flushDelayEvent.Reset(); - } + // In case there's no flush watermark, we wait before start processing new events. + await flushDelayEvent.WaitAsync(batchInterval).ConfigureAwait(false); + flushDelayEvent.Reset(); } } - - completionSource?.TrySetResult(true); - Log.Debug("CIVisibilityProtocolWriter: InternalFlushEventsAsync/ Finishing FlushEventsAsync loop"); } - internal class WatermarkEvent : IEvent - { - public WatermarkEvent(AsyncCountdownEvent countdownEvent) - { - Countdown = countdownEvent; - } + completionSource.TrySetResult(true); + Log.Debug("CIVisibilityProtocolWriter: InternalFlushEventsAsync/ Finishing FlushEventsAsync loop"); + } - public AsyncCountdownEvent Countdown { get; } + internal class WatermarkEvent : IEvent + { + public WatermarkEvent(AsyncCountdownEvent countdownEvent) + { + Countdown = countdownEvent; } - private class Buffers - { - private static int _globalIndexes = 0; - private readonly ICIVisibilityProtocolWriterSender _sender; + public AsyncCountdownEvent Countdown { get; } + } - public Buffers(ICIVisibilityProtocolWriterSender sender, CIVisibilityProtocolPayload ciTestCycleBuffer, CICodeCoveragePayload ciCodeCoverageBuffer) - { - _sender = sender; - Index = Interlocked.Increment(ref _globalIndexes); - FlushDelayEvent = new AsyncManualResetEvent(false); - CiTestCycleBuffer = ciTestCycleBuffer; - CiTestCycleBufferWatch = Stopwatch.StartNew(); - CiCodeCoverageBuffer = ciCodeCoverageBuffer; - CiCodeCoverageBufferWatch = Stopwatch.StartNew(); - FlushTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - FlushTask = null; - } + private class Buffers + { + private static int _globalIndexes; + private readonly ICIVisibilityProtocolWriterSender _sender; - public int Index { get; } + public Buffers(ICIVisibilityProtocolWriterSender sender, CIVisibilityProtocolPayload ciTestCycleBuffer, CICodeCoveragePayload ciCodeCoverageBuffer) + { + _sender = sender; + Index = Interlocked.Increment(ref _globalIndexes); + FlushDelayEvent = new AsyncManualResetEvent(false); + CiTestCycleBuffer = ciTestCycleBuffer; + CiTestCycleBufferWatch = Stopwatch.StartNew(); + CiCodeCoverageBuffer = ciCodeCoverageBuffer; + CiCodeCoverageBufferWatch = Stopwatch.StartNew(); + FlushTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + FlushTask = null; + } - public AsyncManualResetEvent FlushDelayEvent { get; } + public int Index { get; } - public CIVisibilityProtocolPayload CiTestCycleBuffer { get; } + public AsyncManualResetEvent FlushDelayEvent { get; } - public Stopwatch CiTestCycleBufferWatch { get; } + public CIVisibilityProtocolPayload CiTestCycleBuffer { get; } - public CICodeCoveragePayload CiCodeCoverageBuffer { get; } + public Stopwatch CiTestCycleBufferWatch { get; } - public Stopwatch CiCodeCoverageBufferWatch { get; } + public CICodeCoveragePayload CiCodeCoverageBuffer { get; } - public TaskCompletionSource FlushTaskCompletionSource { get; } + public Stopwatch CiCodeCoverageBufferWatch { get; } - public Task FlushTask { get; private set; } + public TaskCompletionSource FlushTaskCompletionSource { get; } - internal void SetFlushTask(Task flushTask) - { - FlushTask = flushTask; - } + public Task? FlushTask { get; private set; } - public Task FlushCiTestCycleBufferWhenTimeElapsedAsync(int batchInterval) - { - return CiTestCycleBufferWatch.ElapsedMilliseconds >= batchInterval ? - FlushCiTestCycleBufferAsync() : Task.CompletedTask; - } + internal void SetFlushTask(Task flushTask) + { + FlushTask = flushTask; + } - public Task FlushCiTestCycleBufferAsync() - { - return CiTestCycleBuffer.HasEvents ? InternalFlushCiTestCycleBufferAsync() : Task.CompletedTask; + public Task FlushCiTestCycleBufferWhenTimeElapsedAsync(int batchInterval) + { + return CiTestCycleBufferWatch.ElapsedMilliseconds >= batchInterval ? + FlushCiTestCycleBufferAsync() : Task.CompletedTask; + } - async Task InternalFlushCiTestCycleBufferAsync() - { - await _sender.SendPayloadAsync(CiTestCycleBuffer).ConfigureAwait(false); - CiTestCycleBuffer.Reset(); - CiTestCycleBufferWatch.Restart(); - } - } + public Task FlushCiTestCycleBufferAsync() + { + return CiTestCycleBuffer.HasEvents ? InternalFlushCiTestCycleBufferAsync() : Task.CompletedTask; - public Task FlushCiCodeCoverageBufferWhenTimeElapsedAsync(int batchInterval) + async Task InternalFlushCiTestCycleBufferAsync() { - return CiCodeCoverageBufferWatch.ElapsedMilliseconds >= batchInterval ? - FlushCiCodeCoverageBufferAsync() : Task.CompletedTask; + await _sender.SendPayloadAsync(CiTestCycleBuffer).ConfigureAwait(false); + CiTestCycleBuffer.Reset(); + CiTestCycleBufferWatch.Restart(); } + } - public Task FlushCiCodeCoverageBufferAsync() - { - return CiCodeCoverageBuffer.HasEvents ? InternalFlushCiCodeCoverageBufferAsync() : Task.CompletedTask; + public Task FlushCiCodeCoverageBufferWhenTimeElapsedAsync(int batchInterval) + { + return CiCodeCoverageBufferWatch.ElapsedMilliseconds >= batchInterval ? + FlushCiCodeCoverageBufferAsync() : Task.CompletedTask; + } - async Task InternalFlushCiCodeCoverageBufferAsync() - { - await _sender.SendPayloadAsync(CiCodeCoverageBuffer).ConfigureAwait(false); - CiCodeCoverageBuffer.Reset(); - CiCodeCoverageBufferWatch.Restart(); - } + public Task FlushCiCodeCoverageBufferAsync() + { + return CiCodeCoverageBuffer.HasEvents ? InternalFlushCiCodeCoverageBufferAsync() : Task.CompletedTask; + + async Task InternalFlushCiCodeCoverageBufferAsync() + { + await _sender.SendPayloadAsync(CiCodeCoverageBuffer).ConfigureAwait(false); + CiCodeCoverageBuffer.Reset(); + CiCodeCoverageBufferWatch.Restart(); } } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs b/tracer/src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs index 2ac96ab358ac..a161ffc09ed5 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/CIWriterFileSender.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.IO; @@ -10,83 +11,82 @@ using Datadog.Trace.Ci.Agent.Payloads; using Datadog.Trace.Logging; -namespace Datadog.Trace.Ci.Agent +namespace Datadog.Trace.Ci.Agent; + +/// +/// This class is for debugging purposes only. +/// +internal sealed class CIWriterFileSender : ICIVisibilityProtocolWriterSender { - /// - /// This class is for debugging purposes only. - /// - internal sealed class CIWriterFileSender : ICIVisibilityProtocolWriterSender - { - private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); - public CIWriterFileSender() - { - Log.Information("CIWriterFileSender Initialized."); - } + public CIWriterFileSender() + { + Log.Information("CIWriterFileSender Initialized."); + } - public Task SendPayloadAsync(EventPlatformPayload payload) + public Task SendPayloadAsync(EventPlatformPayload payload) + { + switch (payload) { - switch (payload) - { - case CIVisibilityProtocolPayload ciVisibilityProtocolPayload: - return SendPayloadAsync(ciVisibilityProtocolPayload); - case MultipartPayload multipartPayload: - return SendPayloadAsync(multipartPayload); - default: - Util.ThrowHelper.ThrowNotSupportedException("Payload is not supported."); - return Task.FromException(new NotSupportedException("Payload is not supported.")); - } + case CIVisibilityProtocolPayload ciVisibilityProtocolPayload: + return SendPayloadAsync(ciVisibilityProtocolPayload); + case MultipartPayload multipartPayload: + return SendPayloadAsync(multipartPayload); + default: + ThrowHelper.ThrowNotSupportedException("Payload is not supported."); + return Task.FromException(new NotSupportedException("Payload is not supported.")); } + } - private Task SendPayloadAsync(CIVisibilityProtocolPayload payload) - { - var str = Path.Combine(Path.GetTempPath(), $"civiz-{Guid.NewGuid():n}"); + private Task SendPayloadAsync(CIVisibilityProtocolPayload payload) + { + var str = Path.Combine(Path.GetTempPath(), $"civiz-{Guid.NewGuid():n}"); - var msgPackBytes = payload.ToArray(); - var msgPackFile = str + ".mpack"; - File.WriteAllBytes(msgPackFile, msgPackBytes); - Log.Debug("File written: {File}", msgPackFile); + var msgPackBytes = payload.ToArray(); + var msgPackFile = str + ".mpack"; + File.WriteAllBytes(msgPackFile, msgPackBytes); + Log.Debug("File written: {File}", msgPackFile); - var json = Vendors.MessagePack.MessagePackSerializer.ToJson(msgPackBytes); - var jsonFile = str + ".json"; - File.WriteAllText(jsonFile, json); - Log.Debug("File written: {File}", jsonFile); + var json = Vendors.MessagePack.MessagePackSerializer.ToJson(msgPackBytes); + var jsonFile = str + ".json"; + File.WriteAllText(jsonFile, json); + Log.Debug("File written: {File}", jsonFile); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private Task SendPayloadAsync(MultipartPayload payload) + private Task SendPayloadAsync(MultipartPayload payload) + { + var str = Path.Combine(Path.GetTempPath(), $"multipart-{Guid.NewGuid():n}"); + foreach (var item in payload.ToArray()) { - var str = Path.Combine(Path.GetTempPath(), $"multipart-{Guid.NewGuid():n}"); - foreach (var item in payload.ToArray()) - { - byte[] bytes = null; + byte[]? bytes = null; - if (item.ContentInStream is { } stream) - { - using var ms = new MemoryStream(); - stream.CopyTo(ms); - bytes = ms.ToArray(); - } - else if (item.ContentInBytes is { } arraySegment) - { - bytes = arraySegment.ToArray(); - } + if (item.ContentInStream is { } stream) + { + using var ms = new MemoryStream(); + stream.CopyTo(ms); + bytes = ms.ToArray(); + } + else if (item.ContentInBytes is { } arraySegment) + { + bytes = arraySegment.ToArray(); + } - if (bytes is not null) - { - var msgPackFile = str + $"{item.Name}.mpack"; - File.WriteAllBytes(msgPackFile, bytes); - Log.Debug("File written: {File}", msgPackFile); + if (bytes is not null) + { + var msgPackFile = str + $"{item.Name}.mpack"; + File.WriteAllBytes(msgPackFile, bytes); + Log.Debug("File written: {File}", msgPackFile); - var json = Vendors.MessagePack.MessagePackSerializer.ToJson(bytes); - var jsonFile = str + $"{item.Name}.json"; - File.WriteAllText(jsonFile, json); - Log.Debug("File written: {File}", jsonFile); - } + var json = Vendors.MessagePack.MessagePackSerializer.ToJson(bytes); + var jsonFile = str + $"{item.Name}.json"; + File.WriteAllText(jsonFile, json); + Log.Debug("File written: {File}", jsonFile); } - - return Task.CompletedTask; } + + return Task.CompletedTask; } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/CIWriterHttpSender.cs b/tracer/src/Datadog.Trace/Ci/Agent/CIWriterHttpSender.cs index 068306338561..b48a2267a841 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/CIWriterHttpSender.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/CIWriterHttpSender.cs @@ -2,10 +2,10 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.Threading.Tasks; @@ -19,233 +19,232 @@ using Datadog.Trace.Util.Http; using Datadog.Trace.Vendors.Serilog.Events; -namespace Datadog.Trace.Ci.Agent +namespace Datadog.Trace.Ci.Agent; + +internal sealed class CIWriterHttpSender : ICIVisibilityProtocolWriterSender { - internal sealed class CIWriterHttpSender : ICIVisibilityProtocolWriterSender - { - private const string ApiKeyHeader = "dd-api-key"; - private const string EvpSubdomainHeader = "X-Datadog-EVP-Subdomain"; - private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); + private const string ApiKeyHeader = "dd-api-key"; + private const string EvpSubdomainHeader = "X-Datadog-EVP-Subdomain"; + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(); - private readonly IApiRequestFactory _apiRequestFactory; - private readonly bool _isDebugEnabled; + private readonly IApiRequestFactory _apiRequestFactory; + private readonly bool _isDebugEnabled; - public CIWriterHttpSender(IApiRequestFactory apiRequestFactory) + public CIWriterHttpSender(IApiRequestFactory apiRequestFactory) + { + _apiRequestFactory = apiRequestFactory; + _isDebugEnabled = GlobalSettings.Instance.DebugEnabledInternal; + Log.Information("CIWriterHttpSender Initialized."); + } + + public Task SendPayloadAsync(EventPlatformPayload payload) + { + switch (payload) { - _apiRequestFactory = apiRequestFactory; - _isDebugEnabled = GlobalSettings.Instance.DebugEnabledInternal; - Log.Information("CIWriterHttpSender Initialized."); + case CIVisibilityProtocolPayload ciVisibilityProtocolPayload: + return SendPayloadAsync(ciVisibilityProtocolPayload); + case MultipartPayload multipartPayload: + return SendPayloadAsync(multipartPayload); + default: + ThrowHelper.ThrowNotSupportedException("Payload is not supported."); + return Task.FromException(new NotSupportedException("Payload is not supported.")); } + } - public Task SendPayloadAsync(EventPlatformPayload payload) + private static bool IsStatusCodeError(int statusCode) => statusCode is < 200 or >= 300; + + private static async Task SendPayloadAsync(Func> senderFunc, IApiRequest request, EventPlatformPayload payload, T state, bool finalTry) + { + IApiResponse? response = null; + int statusCode; + + try { - switch (payload) + response = await senderFunc(request, payload, state).ConfigureAwait(false); + statusCode = response.StatusCode; + + // Attempt a retry if the status code is not SUCCESS + if (IsStatusCodeError(statusCode)) { - case CIVisibilityProtocolPayload ciVisibilityProtocolPayload: - return SendPayloadAsync(ciVisibilityProtocolPayload); - case MultipartPayload multipartPayload: - return SendPayloadAsync(multipartPayload); - default: - Util.ThrowHelper.ThrowNotSupportedException("Payload is not supported."); - return Task.FromException(new NotSupportedException("Payload is not supported.")); + if (finalTry) + { + try + { + var responseContent = await response.ReadAsStringAsync().ConfigureAwait(false); + Log.Error("Failed to submit events with status code {StatusCode} and message: {ResponseContent}", response.StatusCode, responseContent); + } + catch (Exception ex) + { + Log.Error(ex, "Unable to read response for failed request with status code {StatusCode}", response.StatusCode); + } + } } } + finally + { + response?.Dispose(); + } - private static bool IsStatusCodeError(int statusCode) => statusCode is < 200 or >= 300; + return statusCode; + } - private static async Task SendPayloadAsync(Func> senderFunc, IApiRequest request, EventPlatformPayload payload, T state, bool finalTry) + private async Task SendPayloadAsync(EventPlatformPayload payload, Func> senderFunc, T state) + { + // retry up to 5 times with exponential back-off + const int retryLimit = 5; + var retryCount = 1; + var sleepDuration = 100; // in milliseconds + var url = payload.Url; + var sw = Stopwatch.StartNew(); + + while (true) { - IApiResponse response = null; - int statusCode; + TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadRequests(payload.TelemetryEndpointAndCompression); + + IApiRequest request; try { - response = await senderFunc(request, payload, state).ConfigureAwait(false); - statusCode = response.StatusCode; - - // Attempt a retry if the status code is not SUCCESS - if (IsStatusCodeError(statusCode)) + request = _apiRequestFactory.Create(url); + if (payload.UseEvpProxy) { - if (finalTry) - { - try - { - var responseContent = await response.ReadAsStringAsync().ConfigureAwait(false); - Log.Error("Failed to submit events with status code {StatusCode} and message: {ResponseContent}", response.StatusCode, responseContent); - } - catch (Exception ex) - { - Log.Error(ex, "Unable to read response for failed request with status code {StatusCode}", response.StatusCode); - } - } + request.AddHeader(EvpSubdomainHeader, payload.EventPlatformSubdomain); + } + else + { + request.AddHeader(ApiKeyHeader, CIVisibility.Settings.ApiKey); } } - finally + catch (Exception ex) { - response?.Dispose(); + Log.Error(ex, "An error occurred while generating http request to send events to {AgentEndpoint}", _apiRequestFactory.Info(url)); + return; } - return statusCode; - } + var statusCode = -1; + var isFinalTry = retryCount >= retryLimit; + Exception? exception = null; - private async Task SendPayloadAsync(EventPlatformPayload payload, Func> senderFunc, T state) - { - // retry up to 5 times with exponential back-off - const int retryLimit = 5; - var retryCount = 1; - var sleepDuration = 100; // in milliseconds - var url = payload.Url; - var sw = Stopwatch.StartNew(); - - while (true) + sw.Restart(); + try { - TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadRequests(payload.TelemetryEndpointAndCompression); - - IApiRequest request; + statusCode = await SendPayloadAsync(senderFunc, request, payload, state, isFinalTry).ConfigureAwait(false); + } + catch (Exception ex) + { + exception = ex; - try + if (_isDebugEnabled) { - request = _apiRequestFactory.Create(url); - if (payload.UseEvpProxy) - { - request.AddHeader(EvpSubdomainHeader, payload.EventPlatformSubdomain); - } - else + if (ex.InnerException is InvalidOperationException ioe) { - request.AddHeader(ApiKeyHeader, CIVisibility.Settings.ApiKey); + Log.Error(ex, "An error occurred while sending events to {AgentEndpoint}", _apiRequestFactory.Info(url)); + TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadDropped(payload.TelemetryEndpoint); + return; } } - catch (Exception ex) + } + finally + { + TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadRequestsMs(payload.TelemetryEndpoint, sw.Elapsed.TotalMilliseconds); + if (TelemetryHelper.GetErrorTypeFromStatusCode(statusCode) is { } errorType) { - Log.Error(ex, "An error occurred while generating http request to send events to {AgentEndpoint}", _apiRequestFactory.Info(url)); - return; + TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadRequestsErrors(payload.TelemetryEndpoint, errorType); } + } - var statusCode = -1; - var isFinalTry = retryCount >= retryLimit; - Exception exception = null; - - sw.Restart(); - try + // Error handling block + if (IsStatusCodeError(statusCode)) + { + if (isFinalTry) { - statusCode = await SendPayloadAsync(senderFunc, request, payload, state, isFinalTry).ConfigureAwait(false); + // stop retrying + Log.Error(exception, "An error occurred while sending events after {Retries} retries to {AgentEndpoint} | StatusCode: {StatusCode}", retryCount, _apiRequestFactory.Info(url), statusCode); + TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadDropped(payload.TelemetryEndpoint); + return; } - catch (Exception ex) - { - exception = ex; - if (_isDebugEnabled) - { - if (ex.InnerException is InvalidOperationException ioe) - { - Log.Error(ex, "An error occurred while sending events to {AgentEndpoint}", _apiRequestFactory.Info(url)); - TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadDropped(payload.TelemetryEndpoint); - return; - } - } - } - finally + // Before retry delay + if (exception.IsSocketException()) { - TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadRequestsMs(payload.TelemetryEndpoint, sw.Elapsed.TotalMilliseconds); - if (TelemetryHelper.GetErrorTypeFromStatusCode(statusCode) is { } errorType) - { - TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadRequestsErrors(payload.TelemetryEndpoint, errorType); - } + Log.Debug(exception, "Unable to communicate with {AgentEndpoint}", _apiRequestFactory.Info(url)); } - // Error handling block - if (IsStatusCodeError(statusCode)) - { - if (isFinalTry) - { - // stop retrying - Log.Error(exception, "An error occurred while sending events after {Retries} retries to {AgentEndpoint} | StatusCode: {StatusCode}", retryCount, _apiRequestFactory.Info(url), statusCode); - TelemetryFactory.Metrics.RecordCountCIVisibilityEndpointPayloadDropped(payload.TelemetryEndpoint); - return; - } - - // Before retry delay - if (exception.IsSocketException()) - { - Log.Debug(exception, "Unable to communicate with {AgentEndpoint}", _apiRequestFactory.Info(url)); - } + // Execute retry delay + Log.Debug(exception, "An error occurred while sending events to {AgentEndpoint} | StatusCode: {StatusCode}", _apiRequestFactory.Info(url), statusCode); + await Task.Delay(sleepDuration).ConfigureAwait(false); + retryCount++; + sleepDuration *= 2; - // Execute retry delay - Log.Debug(exception, "An error occurred while sending events to {AgentEndpoint} | StatusCode: {StatusCode}", _apiRequestFactory.Info(url), statusCode); - await Task.Delay(sleepDuration).ConfigureAwait(false); - retryCount++; - sleepDuration *= 2; - - continue; - } - - Log.Debug("Successfully sent events to {AgentEndpoint}", _apiRequestFactory.Info(url)); - return; + continue; } + + Log.Debug("Successfully sent events to {AgentEndpoint}", _apiRequestFactory.Info(url)); + return; } + } - private async Task SendPayloadAsync(CIVisibilityProtocolPayload payload) + private async Task SendPayloadAsync(CIVisibilityProtocolPayload payload) + { + ArraySegment payloadArraySegment; + MemoryStream? agentlessMemoryStream = null; + try { - ArraySegment payloadArraySegment; - MemoryStream agentlessMemoryStream = null; - try + if (payload.UseGZip) { - if (payload.UseGZip) + agentlessMemoryStream = new MemoryStream(); + int uncompressedSize; + using (var gzipStream = new GZipStream(agentlessMemoryStream, CompressionLevel.Fastest, true)) { - agentlessMemoryStream = new MemoryStream(); - int uncompressedSize; - using (var gzipStream = new GZipStream(agentlessMemoryStream, CompressionLevel.Fastest, true)) - { - uncompressedSize = payload.WriteTo(gzipStream); - } - - agentlessMemoryStream.TryGetBuffer(out payloadArraySegment); - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("Sending ({NumberOfTraces} events) {BytesValue} bytes... ({Uncompressed} bytes uncompressed)", payload.Count, payloadArraySegment.Count.ToString("N0"), uncompressedSize.ToString("N0")); - } + uncompressedSize = payload.WriteTo(gzipStream); } - else + + agentlessMemoryStream.TryGetBuffer(out payloadArraySegment); + if (Log.IsEnabled(LogEventLevel.Debug)) { - payloadArraySegment = new ArraySegment(payload.ToArray()); - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("Sending ({NumberOfTraces} events) {BytesValue} bytes...", payload.Count, payloadArraySegment.Count.ToString("N0")); - } + Log.Debug("Sending ({NumberOfTraces} events) {BytesValue} bytes... ({Uncompressed} bytes uncompressed)", payload.Count, payloadArraySegment.Count.ToString("N0"), uncompressedSize.ToString("N0")); } - - TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadEventsCount(payload.TelemetryEndpoint, payload.Count); - TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadBytes(payload.TelemetryEndpoint, payloadArraySegment.Count); - - await SendPayloadAsync( - payload, - static (request, payload, payloadBytes) => request.PostAsync(payloadBytes, MimeTypes.MsgPack, payload.UseGZip ? "gzip" : null), - payloadArraySegment) - .ConfigureAwait(false); - } - finally - { - agentlessMemoryStream?.Dispose(); } - } - - private async Task SendPayloadAsync(MultipartPayload payload) - { - var payloadArray = payload.ToArray(); - var payloadBytes = 0; - foreach (var multipartFormItem in payloadArray) + else { - payloadBytes += multipartFormItem.ContentInBytes?.Count ?? 0; + payloadArraySegment = new ArraySegment(payload.ToArray()); + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug("Sending ({NumberOfTraces} events) {BytesValue} bytes...", payload.Count, payloadArraySegment.Count.ToString("N0")); + } } - TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadBytes(payload.TelemetryEndpoint, payloadBytes); TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadEventsCount(payload.TelemetryEndpoint, payload.Count); + TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadBytes(payload.TelemetryEndpoint, payloadArraySegment.Count); - Log.Debug("Sending {Count} multipart items...", payload.Count); await SendPayloadAsync( - payload, - static (request, payload, payloadArray) => request.PostAsync(payloadArray, payload.UseGZip ? MultipartCompression.GZip : MultipartCompression.None), - payloadArray).ConfigureAwait(false); + payload, + static (request, payload, payloadBytes) => request.PostAsync(payloadBytes, MimeTypes.MsgPack, payload.UseGZip ? "gzip" : null), + payloadArraySegment) + .ConfigureAwait(false); + } + finally + { + agentlessMemoryStream?.Dispose(); + } + } + + private async Task SendPayloadAsync(MultipartPayload payload) + { + var payloadArray = payload.ToArray(); + var payloadBytes = 0; + foreach (var multipartFormItem in payloadArray) + { + payloadBytes += multipartFormItem.ContentInBytes?.Count ?? 0; } + + TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadBytes(payload.TelemetryEndpoint, payloadBytes); + TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointPayloadEventsCount(payload.TelemetryEndpoint, payload.Count); + + Log.Debug("Sending {Count} multipart items...", payload.Count); + await SendPayloadAsync( + payload, + static (request, payload, payloadArray) => request.PostAsync(payloadArray, payload.UseGZip ? MultipartCompression.GZip : MultipartCompression.None), + payloadArray).ConfigureAwait(false); } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/ICIVisibilityProtocolWriterSender.cs b/tracer/src/Datadog.Trace/Ci/Agent/ICIVisibilityProtocolWriterSender.cs index f3612368ee5f..5b1cc2500c32 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/ICIVisibilityProtocolWriterSender.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/ICIVisibilityProtocolWriterSender.cs @@ -2,14 +2,14 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Threading.Tasks; using Datadog.Trace.Ci.Agent.Payloads; -namespace Datadog.Trace.Ci.Agent +namespace Datadog.Trace.Ci.Agent; + +internal interface ICIVisibilityProtocolWriterSender { - internal interface ICIVisibilityProtocolWriterSender - { - Task SendPayloadAsync(EventPlatformPayload payload); - } + Task SendPayloadAsync(EventPlatformPayload payload); } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/IEventWriter.cs b/tracer/src/Datadog.Trace/Ci/Agent/IEventWriter.cs index ee50d5add3a2..eabdd8edd104 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/IEventWriter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/IEventWriter.cs @@ -2,16 +2,16 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.Agent; -namespace Datadog.Trace.Ci.Agent +namespace Datadog.Trace.Ci.Agent; + +/// +/// Event Writer interface +/// +internal interface IEventWriter : IAgentWriter { - /// - /// Event Writer interface - /// - internal interface IEventWriter : IAgentWriter - { - void WriteEvent(IEvent @event); - } + void WriteEvent(IEvent @event); } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIEventMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIEventMessagePackFormatter.cs index c9551feeb83a..392cf31d9277 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIEventMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/CIEventMessagePackFormatter.cs @@ -2,7 +2,6 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // - #nullable enable using System; @@ -11,169 +10,168 @@ using Datadog.Trace.Configuration; using Datadog.Trace.Vendors.MessagePack; -namespace Datadog.Trace.Ci.Agent.MessagePack +namespace Datadog.Trace.Ci.Agent.MessagePack; + +internal class CIEventMessagePackFormatter : EventMessagePackFormatter { - internal class CIEventMessagePackFormatter : EventMessagePackFormatter + private readonly byte[] _metadataBytes = StringEncoding.UTF8.GetBytes("metadata"); + + private readonly byte[] _asteriskBytes = StringEncoding.UTF8.GetBytes("*"); + private readonly byte[] _runtimeIdBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.RuntimeId); + private readonly byte[] _runtimeIdValueBytes = StringEncoding.UTF8.GetBytes(Tracer.RuntimeId); + private readonly byte[] _languageNameBytes = StringEncoding.UTF8.GetBytes("language"); + private readonly byte[] _languageNameValueBytes = StringEncoding.UTF8.GetBytes("dotnet"); + private readonly byte[] _libraryVersionBytes = StringEncoding.UTF8.GetBytes(CommonTags.LibraryVersion); + private readonly byte[] _libraryVersionValueBytes = StringEncoding.UTF8.GetBytes(TracerConstants.AssemblyVersion); + private readonly byte[] _environmentBytes = StringEncoding.UTF8.GetBytes("env"); + private readonly byte[]? _environmentValueBytes; + + private readonly byte[] _testBytes = StringEncoding.UTF8.GetBytes(SpanTypes.Test); + private readonly byte[] _testSuiteEndBytes = StringEncoding.UTF8.GetBytes(SpanTypes.TestSuite); + private readonly byte[] _testModuleEndBytes = StringEncoding.UTF8.GetBytes(SpanTypes.TestModule); + private readonly byte[] _testSessionEndBytes = StringEncoding.UTF8.GetBytes(SpanTypes.TestSession); + private readonly byte[] _testSessionNameBytes = StringEncoding.UTF8.GetBytes("test_session.name"); + private readonly byte[]? _testSessionNameValueBytes; + + private readonly byte[] _eventsBytes = StringEncoding.UTF8.GetBytes("events"); + + private readonly ArraySegment _envelopBytes; + + public CIEventMessagePackFormatter(TracerSettings tracerSettings) { - private readonly byte[] _metadataBytes = StringEncoding.UTF8.GetBytes("metadata"); - - private readonly byte[] _asteriskBytes = StringEncoding.UTF8.GetBytes("*"); - private readonly byte[] _runtimeIdBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.RuntimeId); - private readonly byte[] _runtimeIdValueBytes = StringEncoding.UTF8.GetBytes(Tracer.RuntimeId); - private readonly byte[] _languageNameBytes = StringEncoding.UTF8.GetBytes("language"); - private readonly byte[] _languageNameValueBytes = StringEncoding.UTF8.GetBytes("dotnet"); - private readonly byte[] _libraryVersionBytes = StringEncoding.UTF8.GetBytes(CommonTags.LibraryVersion); - private readonly byte[] _libraryVersionValueBytes = StringEncoding.UTF8.GetBytes(TracerConstants.AssemblyVersion); - private readonly byte[] _environmentBytes = StringEncoding.UTF8.GetBytes("env"); - private readonly byte[]? _environmentValueBytes; - - private readonly byte[] _testBytes = StringEncoding.UTF8.GetBytes(SpanTypes.Test); - private readonly byte[] _testSuiteEndBytes = StringEncoding.UTF8.GetBytes(SpanTypes.TestSuite); - private readonly byte[] _testModuleEndBytes = StringEncoding.UTF8.GetBytes(SpanTypes.TestModule); - private readonly byte[] _testSessionEndBytes = StringEncoding.UTF8.GetBytes(SpanTypes.TestSession); - private readonly byte[] _testSessionNameBytes = StringEncoding.UTF8.GetBytes("test_session.name"); - private readonly byte[]? _testSessionNameValueBytes; - - private readonly byte[] _eventsBytes = StringEncoding.UTF8.GetBytes("events"); - - private readonly ArraySegment _envelopBytes; - - public CIEventMessagePackFormatter(TracerSettings tracerSettings) + if (!string.IsNullOrEmpty(tracerSettings.EnvironmentInternal)) + { + _environmentValueBytes = StringEncoding.UTF8.GetBytes(tracerSettings.EnvironmentInternal); + } + + if (!string.IsNullOrWhiteSpace(Ci.CIVisibility.Settings.TestSessionName)) { - if (!string.IsNullOrEmpty(tracerSettings.EnvironmentInternal)) - { - _environmentValueBytes = StringEncoding.UTF8.GetBytes(tracerSettings.EnvironmentInternal); - } + _testSessionNameValueBytes = StringEncoding.UTF8.GetBytes(Ci.CIVisibility.Settings.TestSessionName); + } + + _envelopBytes = GetEnvelopeArraySegment(); + } + + public override int Serialize(ref byte[] bytes, int offset, CIVisibilityProtocolPayload? value, IFormatterResolver formatterResolver) + { + if (value is null) + { + return 0; + } + + var originalOffset = offset; + + // Write envelope + MessagePackBinary.EnsureCapacity(ref bytes, offset, _envelopBytes.Count); + Buffer.BlockCopy(_envelopBytes.Array!, _envelopBytes.Offset, bytes, offset, _envelopBytes.Count); + offset += _envelopBytes.Count; + + // Write events + if (value.Events.Lock()) + { + var data = value.Events.Data; + MessagePackBinary.EnsureCapacity(ref bytes, offset, data.Count); + Buffer.BlockCopy(data.Array!, data.Offset, bytes, offset, data.Count); + offset += data.Count; + } + else + { + Log.Error("Error while locking the events buffer with {Count} events.", value.Events.Count); + offset += MessagePackBinary.WriteNil(ref bytes, offset); + } - if (!string.IsNullOrWhiteSpace(Ci.CIVisibility.Settings.TestSessionName)) - { - _testSessionNameValueBytes = StringEncoding.UTF8.GetBytes(Ci.CIVisibility.Settings.TestSessionName); - } + return offset - originalOffset; + } + + private ArraySegment GetEnvelopeArraySegment() + { + var offset = 0; + // Default size o the array, in case we don't have enough space MessagePackBinary will resize it + var bytes = new byte[2048]; + + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 3); + + // # Version + + // Key + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, VersionBytes); + // Value + offset += MessagePackBinary.WriteInt32(ref bytes, offset, 1); + + // # Metadata + + // Key + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _metadataBytes); + + // Value + var metadataValuesCount = _testSessionNameValueBytes is not null ? 5 : 1; + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, metadataValuesCount); + + // -> * : {} - _envelopBytes = GetEnvelopeArraySegment(); + // Key + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _asteriskBytes); + + // Value (RuntimeId, Language, library_version, Env?) + int valuesCount = 3; + if (_environmentValueBytes is not null) + { + valuesCount++; } - public override int Serialize(ref byte[] bytes, int offset, CIVisibilityProtocolPayload? value, IFormatterResolver formatterResolver) + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, valuesCount); + + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _runtimeIdBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _runtimeIdValueBytes); + + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageNameValueBytes); + + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _libraryVersionBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _libraryVersionValueBytes); + + if (_environmentValueBytes is not null) { - if (value is null) - { - return 0; - } - - var originalOffset = offset; - - // Write envelope - MessagePackBinary.EnsureCapacity(ref bytes, offset, _envelopBytes.Count); - Buffer.BlockCopy(_envelopBytes.Array!, _envelopBytes.Offset, bytes, offset, _envelopBytes.Count); - offset += _envelopBytes.Count; - - // Write events - if (value.Events.Lock()) - { - var data = value.Events.Data; - MessagePackBinary.EnsureCapacity(ref bytes, offset, data.Count); - Buffer.BlockCopy(data.Array!, data.Offset, bytes, offset, data.Count); - offset += data.Count; - } - else - { - Log.Error("Error while locking the events buffer with {Count} events.", value.Events.Count); - offset += MessagePackBinary.WriteNil(ref bytes, offset); - } - - return offset - originalOffset; + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _environmentBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _environmentValueBytes); } - private ArraySegment GetEnvelopeArraySegment() + if (_testSessionNameValueBytes is not null) { - var offset = 0; - // Default size o the array, in case we don't have enough space MessagePackBinary will resize it - var bytes = new byte[2048]; - - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 3); - - // # Version - - // Key - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, VersionBytes); - // Value - offset += MessagePackBinary.WriteInt32(ref bytes, offset, 1); - - // # Metadata - - // Key - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _metadataBytes); - - // Value - var metadataValuesCount = _testSessionNameValueBytes is not null ? 5 : 1; - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, metadataValuesCount); - - // -> * : {} - - // Key - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _asteriskBytes); - - // Value (RuntimeId, Language, library_version, Env?) - int valuesCount = 3; - if (_environmentValueBytes is not null) - { - valuesCount++; - } - - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, valuesCount); - - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _runtimeIdBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _runtimeIdValueBytes); - - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageNameValueBytes); - - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _libraryVersionBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _libraryVersionValueBytes); - - if (_environmentValueBytes is not null) - { - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _environmentBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _environmentValueBytes); - } - - if (_testSessionNameValueBytes is not null) - { - // -> test : {} - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testBytes); - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); - // -> test_session.name : "value" - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); - - // -> test_suite_end : {} - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSuiteEndBytes); - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); - // -> test_session.name : "value" - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); - - // -> test_module_end : {} - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testModuleEndBytes); - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); - // -> test_session.name : "value" - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); - - // -> test_session_end : {} - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionEndBytes); - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); - // -> test_session.name : "value" - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); - } - - // # Events - - // Key - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _eventsBytes); - - return new ArraySegment(bytes, 0, offset); + // -> test : {} + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testBytes); + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); + // -> test_session.name : "value" + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); + + // -> test_suite_end : {} + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSuiteEndBytes); + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); + // -> test_session.name : "value" + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); + + // -> test_module_end : {} + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testModuleEndBytes); + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); + // -> test_session.name : "value" + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); + + // -> test_session_end : {} + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionEndBytes); + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, 1); + // -> test_session.name : "value" + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionNameValueBytes); } + + // # Events + + // Key + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _eventsBytes); + + return new ArraySegment(bytes, 0, offset); } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs index 1259e16f6da1..94ec26477fa8 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs @@ -3,34 +3,37 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // #nullable enable +#pragma warning disable SA1402 using System; using Datadog.Trace.Logging; using Datadog.Trace.Vendors.MessagePack; using Datadog.Trace.Vendors.MessagePack.Formatters; -namespace Datadog.Trace.Ci.Agent.MessagePack -{ - internal abstract class EventMessagePackFormatter : IMessagePackFormatter - { - private readonly IDatadogLogger _log; +namespace Datadog.Trace.Ci.Agent.MessagePack; - protected static readonly byte[] TypeBytes = StringEncoding.UTF8.GetBytes("type"); - protected static readonly byte[] VersionBytes = StringEncoding.UTF8.GetBytes("version"); - protected static readonly byte[] ContentBytes = StringEncoding.UTF8.GetBytes("content"); +internal abstract class EventMessagePackFormatter +{ + private readonly IDatadogLogger _log; - protected EventMessagePackFormatter() - { - _log = DatadogLogging.GetLoggerFor(GetType()); - } + protected static readonly byte[] TypeBytes = StringEncoding.UTF8.GetBytes("type"); + protected static readonly byte[] VersionBytes = StringEncoding.UTF8.GetBytes("version"); + protected static readonly byte[] ContentBytes = StringEncoding.UTF8.GetBytes("content"); - protected IDatadogLogger Log => _log; + protected EventMessagePackFormatter() + { + _log = DatadogLogging.GetLoggerFor(GetType()); + } - public virtual T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) - { - throw new NotImplementedException(); - } + protected IDatadogLogger Log => _log; +} - public abstract int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver); +internal abstract class EventMessagePackFormatter : EventMessagePackFormatter, IMessagePackFormatter +{ + public virtual T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new NotImplementedException(); } + + public abstract int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver); } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs index d365af1d7bcd..c04785afefc0 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/SpanMessagePackFormatter.cs @@ -9,494 +9,492 @@ using System.Runtime.CompilerServices; using Datadog.Trace.Ci.Tagging; using Datadog.Trace.Ci.Tags; -using Datadog.Trace.ExtensionMethods; using Datadog.Trace.Processors; using Datadog.Trace.Tagging; using Datadog.Trace.Util; using Datadog.Trace.Vendors.MessagePack; using Datadog.Trace.Vendors.MessagePack.Formatters; -namespace Datadog.Trace.Ci.Agent.MessagePack +namespace Datadog.Trace.Ci.Agent.MessagePack; + +internal class SpanMessagePackFormatter : IMessagePackFormatter { - internal class SpanMessagePackFormatter : IMessagePackFormatter - { - public static readonly IMessagePackFormatter Instance = new SpanMessagePackFormatter(); + public static readonly IMessagePackFormatter Instance = new SpanMessagePackFormatter(); + + private readonly byte[] _traceIdBytes = StringEncoding.UTF8.GetBytes("trace_id"); + private readonly byte[] _spanIdBytes = StringEncoding.UTF8.GetBytes("span_id"); + private readonly byte[] _nameBytes = StringEncoding.UTF8.GetBytes("name"); + private readonly byte[] _resourceBytes = StringEncoding.UTF8.GetBytes("resource"); + private readonly byte[] _serviceBytes = StringEncoding.UTF8.GetBytes("service"); + private readonly byte[] _typeBytes = StringEncoding.UTF8.GetBytes("type"); + private readonly byte[] _startBytes = StringEncoding.UTF8.GetBytes("start"); + private readonly byte[] _durationBytes = StringEncoding.UTF8.GetBytes("duration"); + private readonly byte[] _parentIdBytes = StringEncoding.UTF8.GetBytes("parent_id"); + private readonly byte[] _errorBytes = StringEncoding.UTF8.GetBytes("error"); + private readonly byte[] _itrCorrelationId = StringEncoding.UTF8.GetBytes("itr_correlation_id"); - private readonly byte[] _traceIdBytes = StringEncoding.UTF8.GetBytes("trace_id"); - private readonly byte[] _spanIdBytes = StringEncoding.UTF8.GetBytes("span_id"); - private readonly byte[] _nameBytes = StringEncoding.UTF8.GetBytes("name"); - private readonly byte[] _resourceBytes = StringEncoding.UTF8.GetBytes("resource"); - private readonly byte[] _serviceBytes = StringEncoding.UTF8.GetBytes("service"); - private readonly byte[] _typeBytes = StringEncoding.UTF8.GetBytes("type"); - private readonly byte[] _startBytes = StringEncoding.UTF8.GetBytes("start"); - private readonly byte[] _durationBytes = StringEncoding.UTF8.GetBytes("duration"); - private readonly byte[] _parentIdBytes = StringEncoding.UTF8.GetBytes("parent_id"); - private readonly byte[] _errorBytes = StringEncoding.UTF8.GetBytes("error"); - private readonly byte[] _itrCorrelationId = StringEncoding.UTF8.GetBytes("itr_correlation_id"); + // SuiteId, ModuleId, SessionId tags + private readonly byte[] _testSuiteIdBytes = StringEncoding.UTF8.GetBytes(TestSuiteVisibilityTags.TestSuiteId); + private readonly byte[] _testModuleIdBytes = StringEncoding.UTF8.GetBytes(TestSuiteVisibilityTags.TestModuleId); + private readonly byte[] _testSessionIdBytes = StringEncoding.UTF8.GetBytes(TestSuiteVisibilityTags.TestSessionId); - // SuiteId, ModuleId, SessionId tags - private readonly byte[] _testSuiteIdBytes = StringEncoding.UTF8.GetBytes(TestSuiteVisibilityTags.TestSuiteId); - private readonly byte[] _testModuleIdBytes = StringEncoding.UTF8.GetBytes(TestSuiteVisibilityTags.TestModuleId); - private readonly byte[] _testSessionIdBytes = StringEncoding.UTF8.GetBytes(TestSuiteVisibilityTags.TestSessionId); + // string tags + private readonly byte[] _metaBytes = StringEncoding.UTF8.GetBytes("meta"); - // string tags - private readonly byte[] _metaBytes = StringEncoding.UTF8.GetBytes("meta"); + private readonly byte[] _languageNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Language); + private readonly byte[] _languageValueBytes = StringEncoding.UTF8.GetBytes(TracerConstants.Language); - private readonly byte[] _languageNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Language); - private readonly byte[] _languageValueBytes = StringEncoding.UTF8.GetBytes(TracerConstants.Language); + private readonly byte[] _environmentNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Env); - private readonly byte[] _environmentNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Env); + private readonly byte[] _versionNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Version); - private readonly byte[] _versionNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Version); + private readonly byte[] _originNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Origin); + private readonly byte[] _originValueBytes = StringEncoding.UTF8.GetBytes(TestTags.CIAppTestOriginName); - private readonly byte[] _originNameBytes = StringEncoding.UTF8.GetBytes(Trace.Tags.Origin); - private readonly byte[] _originValueBytes = StringEncoding.UTF8.GetBytes(TestTags.CIAppTestOriginName); + // numeric tags + private readonly byte[] _metricsBytes = StringEncoding.UTF8.GetBytes("metrics"); - // numeric tags - private readonly byte[] _metricsBytes = StringEncoding.UTF8.GetBytes("metrics"); + private readonly byte[] _samplingPriorityNameBytes = StringEncoding.UTF8.GetBytes(Metrics.SamplingPriority); + private readonly byte[][] _samplingPriorityValueBytes; + + private readonly byte[] _processIdNameBytes = StringEncoding.UTF8.GetBytes(Trace.Metrics.ProcessId); + private readonly byte[]? _processIdValueBytes; + + private SpanMessagePackFormatter() + { + double processId = DomainMetadata.Instance.ProcessId; + _processIdValueBytes = processId > 0 ? MessagePackSerializer.Serialize(processId) : null; + + // values begin at -1, so they are shifted by 1 from their array index: [-1, 0, 1, 2] + // these must serialized as msgpack float64 (Double in .NET). + _samplingPriorityValueBytes = + [ + MessagePackSerializer.Serialize((double)SamplingPriorityValues.UserReject), + MessagePackSerializer.Serialize((double)SamplingPriorityValues.AutoReject), + MessagePackSerializer.Serialize((double)SamplingPriorityValues.AutoKeep), + MessagePackSerializer.Serialize((double)SamplingPriorityValues.UserKeep) + ]; + } - private readonly byte[] _samplingPriorityNameBytes = StringEncoding.UTF8.GetBytes(Metrics.SamplingPriority); - private readonly byte[][] _samplingPriorityValueBytes; + public int Serialize(ref byte[] bytes, int offset, Span value, IFormatterResolver formatterResolver) + { + var context = value.Context; + var testSessionTags = value.Tags as TestSessionSpanTags; + var testModuleTags = value.Tags as TestModuleSpanTags; + var testSuiteTags = value.Tags as TestSuiteSpanTags; - private readonly byte[] _processIdNameBytes = StringEncoding.UTF8.GetBytes(Trace.Metrics.ProcessId); - private readonly byte[]? _processIdValueBytes; + // First, pack array length (or map length). + // It should be the number of members of the object to be serialized. + var len = 9; - private SpanMessagePackFormatter() + if (context.ParentIdInternal is not null) { - double processId = DomainMetadata.Instance.ProcessId; - _processIdValueBytes = processId > 0 ? MessagePackSerializer.Serialize(processId) : null; - - // values begin at -1, so they are shifted by 1 from their array index: [-1, 0, 1, 2] - // these must serialized as msgpack float64 (Double in .NET). - _samplingPriorityValueBytes = - [ - MessagePackSerializer.Serialize((double)SamplingPriorityValues.UserReject), - MessagePackSerializer.Serialize((double)SamplingPriorityValues.AutoReject), - MessagePackSerializer.Serialize((double)SamplingPriorityValues.AutoKeep), - MessagePackSerializer.Serialize((double)SamplingPriorityValues.UserKeep) - ]; + len++; } - public int Serialize(ref byte[] bytes, int offset, Span value, IFormatterResolver formatterResolver) + if (testSessionTags is not null && testSessionTags.SessionId != 0) { - var context = value.Context; - var testSessionTags = value.Tags as TestSessionSpanTags; - var testModuleTags = value.Tags as TestModuleSpanTags; - var testSuiteTags = value.Tags as TestSuiteSpanTags; - - // First, pack array length (or map length). - // It should be the number of members of the object to be serialized. - var len = 9; + // we need to add TestSessionId value to the root + len++; + } - if (context.ParentIdInternal is not null) - { - len++; - } + if (testModuleTags is not null) + { + // we need to add ModuleId value to the root + len++; + } - if (testSessionTags is not null && testSessionTags.SessionId != 0) - { - // we need to add TestSessionId value to the root - len++; - } + if (testSuiteTags is not null) + { + // we need to add SuiteId value to the root + len++; + } - if (testModuleTags is not null) - { - // we need to add ModuleId value to the root - len++; - } + var isSpan = false; + if (value.Type is not (SpanTypes.TestSuite or SpanTypes.TestModule or SpanTypes.TestSession)) + { + // we need to add TraceId and SpanId + len++; + len++; + isSpan = true; + } - if (testSuiteTags is not null) - { - // we need to add SuiteId value to the root - len++; - } + var correlationId = value.Type is SpanTypes.Test or SpanTypes.Browser ? CIVisibility.GetSkippableTestsCorrelationId() : null; + if (correlationId is not null) + { + len++; + } - var isSpan = false; - if (value.Type is not (SpanTypes.TestSuite or SpanTypes.TestModule or SpanTypes.TestSession)) - { - // we need to add TraceId and SpanId - len++; - len++; - isSpan = true; - } + var originalOffset = offset; - var correlationId = value.Type is SpanTypes.Test or SpanTypes.Browser ? CIVisibility.GetSkippableTestsCorrelationId() : null; - if (correlationId is not null) - { - len++; - } + offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, len); - var originalOffset = offset; + if (isSpan) + { + // trace_id field is 64-bits, truncate by using TraceId128.Lower + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _traceIdBytes); + offset += MessagePackBinary.WriteUInt64(ref bytes, offset, context.TraceId128.Lower); - offset += MessagePackBinary.WriteMapHeader(ref bytes, offset, len); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _spanIdBytes); + offset += MessagePackBinary.WriteUInt64(ref bytes, offset, context.SpanId); + } - if (isSpan) - { - // trace_id field is 64-bits, truncate by using TraceId128.Lower - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _traceIdBytes); - offset += MessagePackBinary.WriteUInt64(ref bytes, offset, context.TraceId128.Lower); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _nameBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, value.OperationName); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _spanIdBytes); - offset += MessagePackBinary.WriteUInt64(ref bytes, offset, context.SpanId); - } + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _resourceBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, value.ResourceName); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _nameBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, value.OperationName); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _serviceBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, value.ServiceName); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _resourceBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, value.ResourceName); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _typeBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, value.Type); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _serviceBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, value.ServiceName); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _startBytes); + offset += MessagePackBinary.WriteInt64(ref bytes, offset, value.StartTime.ToUnixTimeNanoseconds()); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _typeBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, value.Type); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _durationBytes); + offset += MessagePackBinary.WriteInt64(ref bytes, offset, value.Duration.ToNanoseconds()); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _startBytes); - offset += MessagePackBinary.WriteInt64(ref bytes, offset, value.StartTime.ToUnixTimeNanoseconds()); + if (context.ParentIdInternal is not null) + { + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _parentIdBytes); + offset += MessagePackBinary.WriteUInt64(ref bytes, offset, context.ParentIdInternal.Value); + } - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _durationBytes); - offset += MessagePackBinary.WriteInt64(ref bytes, offset, value.Duration.ToNanoseconds()); + if (testSuiteTags is not null) + { + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSuiteIdBytes); + offset += MessagePackBinary.WriteUInt64(ref bytes, offset, testSuiteTags.SuiteId); + } - if (context.ParentIdInternal is not null) - { - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _parentIdBytes); - offset += MessagePackBinary.WriteUInt64(ref bytes, offset, context.ParentIdInternal.Value); - } + if (testModuleTags is not null) + { + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testModuleIdBytes); + offset += MessagePackBinary.WriteUInt64(ref bytes, offset, testModuleTags.ModuleId); + } - if (testSuiteTags is not null) - { - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSuiteIdBytes); - offset += MessagePackBinary.WriteUInt64(ref bytes, offset, testSuiteTags.SuiteId); - } + if (testSessionTags is not null && testSessionTags.SessionId != 0) + { + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionIdBytes); + offset += MessagePackBinary.WriteUInt64(ref bytes, offset, testSessionTags.SessionId); + } - if (testModuleTags is not null) - { - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testModuleIdBytes); - offset += MessagePackBinary.WriteUInt64(ref bytes, offset, testModuleTags.ModuleId); - } + if (correlationId is not null) + { + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _itrCorrelationId); + offset += MessagePackBinary.WriteString(ref bytes, offset, correlationId); + } - if (testSessionTags is not null && testSessionTags.SessionId != 0) - { - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _testSessionIdBytes); - offset += MessagePackBinary.WriteUInt64(ref bytes, offset, testSessionTags.SessionId); - } + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _errorBytes); + offset += MessagePackBinary.WriteByte(ref bytes, offset, (byte)(value.Error ? 1 : 0)); - if (correlationId is not null) - { - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _itrCorrelationId); - offset += MessagePackBinary.WriteString(ref bytes, offset, correlationId); - } + ITagProcessor[]? tagProcessors = null; + if (context.TraceContext?.Tracer is Tracer tracer) + { + tagProcessors = tracer.TracerManager.TagProcessors; + } - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _errorBytes); - offset += MessagePackBinary.WriteByte(ref bytes, offset, (byte)(value.Error ? 1 : 0)); + offset += SerializeTags(ref bytes, offset, value, value.Tags, tagProcessors); - ITagProcessor[]? tagProcessors = null; - if (context.TraceContext?.Tracer is Tracer tracer) - { - tagProcessors = tracer.TracerManager.TagProcessors; - } + return offset - originalOffset; + } - offset += SerializeTags(ref bytes, offset, value, value.Tags, tagProcessors); + private int SerializeTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) + { + int originalOffset = offset; - return offset - originalOffset; - } + offset += WriteTags(ref bytes, offset, span, tags, tagProcessors); + offset += WriteMetrics(ref bytes, offset, span, tags, tagProcessors); - private int SerializeTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) - { - int originalOffset = offset; + return offset - originalOffset; + } - offset += WriteTags(ref bytes, offset, span, tags, tagProcessors); - offset += WriteMetrics(ref bytes, offset, span, tags, tagProcessors); + // TAGS - return offset - originalOffset; - } + private int WriteTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) + { + int originalOffset = offset; + var traceContext = span.Context.TraceContext; - // TAGS + // Start of "meta" dictionary. Do not add any string tags before this line. + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _metaBytes); - private int WriteTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) - { - int originalOffset = offset; - var traceContext = span.Context.TraceContext; + int count = 0; - // Start of "meta" dictionary. Do not add any string tags before this line. - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _metaBytes); + // We don't know the final count yet, write a fixed-size header and note the offset + var countOffset = offset; + offset += MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, offset, 0); - int count = 0; + // Write span tags + var tagWriter = new TagWriter(this, tagProcessors, bytes, offset); + tags.EnumerateTags(ref tagWriter); + bytes = tagWriter.Bytes; + offset = tagWriter.Offset; + count += tagWriter.Count; - // We don't know the final count yet, write a fixed-size header and note the offset - var countOffset = offset; - offset += MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, offset, 0); + // Write trace tags + // NOTE: this formatter for CI Visibility doesn't know if the span is "first in chunk" or "chunk orphan", + // so we add the trace tags to the local root span only. + if (span.IsRootSpan && traceContext?.Tags is { Count: > 0 } traceTags) + { + var traceTagWriter = new TraceTagWriter(this, tagProcessors, bytes, offset); + traceTags.Enumerate(ref traceTagWriter); + bytes = traceTagWriter.Bytes; + offset = traceTagWriter.Offset; + count += traceTagWriter.Count; + } - // Write span tags - var tagWriter = new TagWriter(this, tagProcessors, bytes, offset); - tags.EnumerateTags(ref tagWriter); - bytes = tagWriter.Bytes; - offset = tagWriter.Offset; - count += tagWriter.Count; + // add "_dd.origin" tag to all spans + count++; + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _originNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _originValueBytes); - // Write trace tags - // NOTE: this formatter for CI Visibility doesn't know if the span is "first in chunk" or "chunk orphan", - // so we add the trace tags to the local root span only. - if (span.IsRootSpan && traceContext?.Tags is { Count: > 0 } traceTags) - { - var traceTagWriter = new TraceTagWriter(this, tagProcessors, bytes, offset); - traceTags.Enumerate(ref traceTagWriter); - bytes = traceTagWriter.Bytes; - offset = traceTagWriter.Offset; - count += traceTagWriter.Count; - } + // add "env" to all spans + var env = traceContext?.Environment; - // add "_dd.origin" tag to all spans + if (!string.IsNullOrWhiteSpace(env)) + { count++; - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _originNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _originValueBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _environmentNameBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, env); + } - // add "env" to all spans - var env = traceContext?.Environment; + // add "language=dotnet" tag to all spans, except those that + // represents a downstream service or external dependency + if (span.Tags is not InstrumentationTags { SpanKind: SpanKinds.Client or SpanKinds.Producer }) + { + count++; + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageNameBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageValueBytes); + } - if (!string.IsNullOrWhiteSpace(env)) - { - count++; - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _environmentNameBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, env); - } + // add "version" tags to all spans whose service name is the default service name + if (string.Equals(span.Context.ServiceNameInternal, traceContext?.Tracer.DefaultServiceName, StringComparison.OrdinalIgnoreCase)) + { + var version = traceContext?.ServiceVersion; - // add "language=dotnet" tag to all spans, except those that - // represents a downstream service or external dependency - if (span.Tags is not InstrumentationTags { SpanKind: SpanKinds.Client or SpanKinds.Producer }) + if (!string.IsNullOrWhiteSpace(version)) { count++; - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageNameBytes); - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _languageValueBytes); + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _versionNameBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, version); } + } - // add "version" tags to all spans whose service name is the default service name - if (string.Equals(span.Context.ServiceNameInternal, traceContext?.Tracer.DefaultServiceName, StringComparison.OrdinalIgnoreCase)) - { - var version = traceContext?.ServiceVersion; + if (count > 0) + { + // Back-patch the count. End of "meta" dictionary. Do not add any string tags after this line. + MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, countOffset, (uint)count); + } - if (!string.IsNullOrWhiteSpace(version)) - { - count++; - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _versionNameBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, version); - } - } + return offset - originalOffset; + } - if (count > 0) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteTag(ref byte[] bytes, ref int offset, string key, string value, ITagProcessor[]? tagProcessors) + { + if (tagProcessors is not null) + { + for (var i = 0; i < tagProcessors.Length; i++) { - // Back-patch the count. End of "meta" dictionary. Do not add any string tags after this line. - MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, countOffset, (uint)count); + tagProcessors[i]?.ProcessMeta(ref key, ref value); } - - return offset - originalOffset; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteTag(ref byte[] bytes, ref int offset, string key, string value, ITagProcessor[]? tagProcessors) + offset += MessagePackBinary.WriteString(ref bytes, offset, key); + offset += MessagePackBinary.WriteString(ref bytes, offset, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteTag(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, string value, ITagProcessor[]? tagProcessors) + { + if (tagProcessors is not null) { - if (tagProcessors is not null) + string? key = null; + for (var i = 0; i < tagProcessors.Length; i++) { - for (var i = 0; i < tagProcessors.Length; i++) - { - tagProcessors[i]?.ProcessMeta(ref key, ref value); - } + tagProcessors[i]?.ProcessMeta(ref key, ref value); } - - offset += MessagePackBinary.WriteString(ref bytes, offset, key); - offset += MessagePackBinary.WriteString(ref bytes, offset, value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteTag(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, string value, ITagProcessor[]? tagProcessors) - { - if (tagProcessors is not null) - { - string? key = null; - for (var i = 0; i < tagProcessors.Length; i++) - { - tagProcessors[i]?.ProcessMeta(ref key, ref value); - } - } + MessagePackBinary.EnsureCapacity(ref bytes, offset, keyBytes.Length + StringEncoding.UTF8.GetMaxByteCount(value.Length) + 5); + offset += MessagePackBinary.WriteRaw(ref bytes, offset, keyBytes); + offset += MessagePackBinary.WriteString(ref bytes, offset, value); + } - MessagePackBinary.EnsureCapacity(ref bytes, offset, keyBytes.Length + StringEncoding.UTF8.GetMaxByteCount(value.Length) + 5); - offset += MessagePackBinary.WriteRaw(ref bytes, offset, keyBytes); - offset += MessagePackBinary.WriteString(ref bytes, offset, value); - } + // METRICS - // METRICS + private int WriteMetrics(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) + { + int originalOffset = offset; - private int WriteMetrics(ref byte[] bytes, int offset, Span span, ITags tags, ITagProcessor[]? tagProcessors) - { - int originalOffset = offset; + // Start of "metrics" dictionary. Do not add any numeric tags before this line. + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _metricsBytes); - // Start of "metrics" dictionary. Do not add any numeric tags before this line. - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _metricsBytes); + int count = 0; - int count = 0; + // We don't know the final count yet, write a fixed-size header and note the offset + var countOffset = offset; + offset += MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, offset, 0); - // We don't know the final count yet, write a fixed-size header and note the offset - var countOffset = offset; - offset += MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, offset, 0); + // Write span metrics + var tagWriter = new TagWriter(this, tagProcessors, bytes, offset); + tags.EnumerateMetrics(ref tagWriter); + bytes = tagWriter.Bytes; + offset = tagWriter.Offset; + count += tagWriter.Count; - // Write span metrics - var tagWriter = new TagWriter(this, tagProcessors, bytes, offset); - tags.EnumerateMetrics(ref tagWriter); - bytes = tagWriter.Bytes; - offset = tagWriter.Offset; - count += tagWriter.Count; + if (span.IsRootSpan) + { + if (_processIdValueBytes is not null) + { + // add "process_id" tag + count++; + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _processIdNameBytes); + offset += MessagePackBinary.WriteRaw(ref bytes, offset, _processIdValueBytes); + } - if (span.IsRootSpan) + // add "_sampling_priority_v1" tag + if (span.Context.TraceContext.SamplingPriority is { } samplingPriority) { - if (_processIdValueBytes is not null) + count++; + offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _samplingPriorityNameBytes); + + if (samplingPriority is >= -1 and <= 2) { - // add "process_id" tag - count++; - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _processIdNameBytes); - offset += MessagePackBinary.WriteRaw(ref bytes, offset, _processIdValueBytes); + // values begin at -1, so they are shifted by 1 from their array index: [-1, 0, 1, 2] + offset += MessagePackBinary.WriteRaw(ref bytes, offset, _samplingPriorityValueBytes[samplingPriority + 1]); } - - // add "_sampling_priority_v1" tag - if (span.Context.TraceContext.SamplingPriority is { } samplingPriority) + else { - count++; - offset += MessagePackBinary.WriteStringBytes(ref bytes, offset, _samplingPriorityNameBytes); - - if (samplingPriority is >= -1 and <= 2) - { - // values begin at -1, so they are shifted by 1 from their array index: [-1, 0, 1, 2] - offset += MessagePackBinary.WriteRaw(ref bytes, offset, _samplingPriorityValueBytes[samplingPriority + 1]); - } - else - { - // fallback to support unknown future values that are not cached - offset += MessagePackBinary.WriteDouble(ref bytes, offset, samplingPriority); - } + // fallback to support unknown future values that are not cached + offset += MessagePackBinary.WriteDouble(ref bytes, offset, samplingPriority); } } + } - if (count > 0) - { - // Back-patch the count. End of "metrics" dictionary. Do not add any numeric tags after this line. - MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, countOffset, (uint)count); - } - - return offset - originalOffset; + if (count > 0) + { + // Back-patch the count. End of "metrics" dictionary. Do not add any numeric tags after this line. + MessagePackBinary.WriteMapHeaderForceMap32Block(ref bytes, countOffset, (uint)count); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMetric(ref byte[] bytes, ref int offset, string key, double value, ITagProcessor[]? tagProcessors) + return offset - originalOffset; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteMetric(ref byte[] bytes, ref int offset, string key, double value, ITagProcessor[]? tagProcessors) + { + if (tagProcessors is not null) { - if (tagProcessors is not null) + for (var i = 0; i < tagProcessors.Length; i++) { - for (var i = 0; i < tagProcessors.Length; i++) - { - tagProcessors[i]?.ProcessMetric(ref key, ref value); - } + tagProcessors[i]?.ProcessMetric(ref key, ref value); } - - offset += MessagePackBinary.WriteString(ref bytes, offset, key); - offset += MessagePackBinary.WriteDouble(ref bytes, offset, value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMetric(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, double value, ITagProcessor[]? tagProcessors) + offset += MessagePackBinary.WriteString(ref bytes, offset, key); + offset += MessagePackBinary.WriteDouble(ref bytes, offset, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteMetric(ref byte[] bytes, ref int offset, ReadOnlySpan keyBytes, double value, ITagProcessor[]? tagProcessors) + { + if (tagProcessors is not null) { - if (tagProcessors is not null) + string? key = null; + for (var i = 0; i < tagProcessors.Length; i++) { - string? key = null; - for (var i = 0; i < tagProcessors.Length; i++) - { - tagProcessors[i]?.ProcessMetric(ref key, ref value); - } + tagProcessors[i]?.ProcessMetric(ref key, ref value); } - - MessagePackBinary.EnsureCapacity(ref bytes, offset, keyBytes.Length + 9); - offset += MessagePackBinary.WriteRaw(ref bytes, offset, keyBytes); - offset += MessagePackBinary.WriteDouble(ref bytes, offset, value); } - public Span Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + MessagePackBinary.EnsureCapacity(ref bytes, offset, keyBytes.Length + 9); + offset += MessagePackBinary.WriteRaw(ref bytes, offset, keyBytes); + offset += MessagePackBinary.WriteDouble(ref bytes, offset, value); + } + + public Span Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new NotImplementedException(); + } + + internal struct TagWriter : IItemProcessor, IItemProcessor + { + private readonly SpanMessagePackFormatter _formatter; + private readonly ITagProcessor[]? _tagProcessors; + + public byte[] Bytes; + public int Offset; + public int Count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal TagWriter(SpanMessagePackFormatter formatter, ITagProcessor[]? tagProcessors, byte[] bytes, int offset) { - throw new NotImplementedException(); + _formatter = formatter; + _tagProcessors = tagProcessors; + Bytes = bytes; + Offset = offset; + Count = 0; } - internal struct TagWriter : IItemProcessor, IItemProcessor + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Process(TagItem item) { - private readonly SpanMessagePackFormatter _formatter; - private readonly ITagProcessor[]? _tagProcessors; - - public byte[] Bytes; - public int Offset; - public int Count; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal TagWriter(SpanMessagePackFormatter formatter, ITagProcessor[]? tagProcessors, byte[] bytes, int offset) + if (item.SerializedKey.IsEmpty) { - _formatter = formatter; - _tagProcessors = tagProcessors; - Bytes = bytes; - Offset = offset; - Count = 0; + _formatter.WriteTag(ref Bytes, ref Offset, item.Key, item.Value, _tagProcessors); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Process(TagItem item) + else { - if (item.SerializedKey.IsEmpty) - { - _formatter.WriteTag(ref Bytes, ref Offset, item.Key, item.Value, _tagProcessors); - } - else - { - _formatter.WriteTag(ref Bytes, ref Offset, item.SerializedKey, item.Value, _tagProcessors); - } - - Count++; + _formatter.WriteTag(ref Bytes, ref Offset, item.SerializedKey, item.Value, _tagProcessors); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Process(TagItem item) - { - if (item.SerializedKey.IsEmpty) - { - _formatter.WriteMetric(ref Bytes, ref Offset, item.Key, item.Value, _tagProcessors); - } - else - { - _formatter.WriteMetric(ref Bytes, ref Offset, item.SerializedKey, item.Value, _tagProcessors); - } - - Count++; - } + Count++; } - internal struct TraceTagWriter : TraceTagCollection.ITagEnumerator + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Process(TagItem item) { - private readonly SpanMessagePackFormatter _formatter; - private readonly ITagProcessor[]? _tagProcessors; - - public byte[] Bytes; - public int Offset; - public int Count; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal TraceTagWriter(SpanMessagePackFormatter formatter, ITagProcessor[]? tagProcessors, byte[] bytes, int offset) + if (item.SerializedKey.IsEmpty) { - _formatter = formatter; - _tagProcessors = tagProcessors; - Bytes = bytes; - Offset = offset; - Count = 0; + _formatter.WriteMetric(ref Bytes, ref Offset, item.Key, item.Value, _tagProcessors); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Next(KeyValuePair item) + else { - _formatter.WriteTag(ref Bytes, ref Offset, item.Key, item.Value, _tagProcessors); - Count++; + _formatter.WriteMetric(ref Bytes, ref Offset, item.SerializedKey, item.Value, _tagProcessors); } + + Count++; + } + } + + internal struct TraceTagWriter : TraceTagCollection.ITagEnumerator + { + private readonly SpanMessagePackFormatter _formatter; + private readonly ITagProcessor[]? _tagProcessors; + + public byte[] Bytes; + public int Offset; + public int Count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal TraceTagWriter(SpanMessagePackFormatter formatter, ITagProcessor[]? tagProcessors, byte[] bytes, int offset) + { + _formatter = formatter; + _tagProcessors = tagProcessors; + Bytes = bytes; + Offset = offset; + Count = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Next(KeyValuePair item) + { + _formatter.WriteTag(ref Bytes, ref Offset, item.Key, item.Value, _tagProcessors); + Count++; } } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CICodeCoveragePayload.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CICodeCoveragePayload.cs index e4614893c872..0f20029a0580 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CICodeCoveragePayload.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CICodeCoveragePayload.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Diagnostics; @@ -15,78 +16,77 @@ using Datadog.Trace.Telemetry.Metrics; using Datadog.Trace.Vendors.MessagePack; -namespace Datadog.Trace.Ci.Agent.Payloads +namespace Datadog.Trace.Ci.Agent.Payloads; + +internal class CICodeCoveragePayload : MultipartPayload { - internal class CICodeCoveragePayload : MultipartPayload + private readonly IFormatterResolver _formatterResolver; + private readonly Stopwatch _serializationWatch; + + public CICodeCoveragePayload(CIVisibilitySettings settings, int maxItemsPerPayload = DefaultMaxItemsPerPayload, int maxBytesPerPayload = DefaultMaxBytesPerPayload, IFormatterResolver? formatterResolver = null) + : base(settings, maxItemsPerPayload, maxBytesPerPayload, formatterResolver) { - private readonly IFormatterResolver _formatterResolver; - private readonly Stopwatch _serializationWatch; + _formatterResolver = formatterResolver ?? CIFormatterResolver.Instance; + _serializationWatch = new Stopwatch(); - public CICodeCoveragePayload(CIVisibilitySettings settings, int maxItemsPerPayload = DefaultMaxItemsPerPayload, int maxBytesPerPayload = DefaultMaxBytesPerPayload, IFormatterResolver formatterResolver = null) - : base(settings, maxItemsPerPayload, maxBytesPerPayload, formatterResolver) - { - _formatterResolver = formatterResolver ?? CIFormatterResolver.Instance; - _serializationWatch = new Stopwatch(); + // We call reset here to add the dummy event + Reset(); + } - // We call reset here to add the dummy event - Reset(); - } + public override string EventPlatformSubdomain => "citestcov-intake"; - public override string EventPlatformSubdomain => "citestcov-intake"; + public override string EventPlatformPath => "api/v2/citestcov"; - public override string EventPlatformPath => "api/v2/citestcov"; + public override MetricTags.CIVisibilityEndpoints TelemetryEndpoint => MetricTags.CIVisibilityEndpoints.CodeCoverage; - public override MetricTags.CIVisibilityEndpoints TelemetryEndpoint => MetricTags.CIVisibilityEndpoints.CodeCoverage; + public override MetricTags.CIVisibilityEndpointAndCompression TelemetryEndpointAndCompression + => UseGZip + ? MetricTags.CIVisibilityEndpointAndCompression.CodeCoverageRequestCompressed + : MetricTags.CIVisibilityEndpointAndCompression.CodeCoverageUncompressed; - public override MetricTags.CIVisibilityEndpointAndCompression TelemetryEndpointAndCompression - => UseGZip - ? MetricTags.CIVisibilityEndpointAndCompression.CodeCoverageRequestCompressed - : MetricTags.CIVisibilityEndpointAndCompression.CodeCoverageUncompressed; + public override bool CanProcessEvent(IEvent @event) + { + return @event is TestCoverage; + } - public override bool CanProcessEvent(IEvent @event) - { - return @event is TestCoverage; - } + protected override MultipartFormItem CreateMultipartFormItem(EventsBuffer eventsBuffer) + { + var index = Count; + var eventInBytes = MessagePackSerializer.Serialize(new CoveragePayload(eventsBuffer), _formatterResolver); + CIVisibility.Log.Debug("CICodeCoveragePayload: Serialized {Count} test code coverage as a single multipart item with {Size} bytes.", eventsBuffer.Count, eventInBytes.Length); + return new MultipartFormItem($"coverage{index}", MimeTypes.MsgPack, $"filecoverage{index}.msgpack", new ArraySegment(eventInBytes)); + } - protected override MultipartFormItem CreateMultipartFormItem(EventsBuffer eventsBuffer) - { - var index = Count; - var eventInBytes = MessagePackSerializer.Serialize(new CoveragePayload(eventsBuffer), _formatterResolver); - CIVisibility.Log.Debug("CICodeCoveragePayload: Serialized {Count} test code coverage as a single multipart item with {Size} bytes.", eventsBuffer.Count, eventInBytes.Length); - return new MultipartFormItem($"coverage{index}", MimeTypes.MsgPack, $"filecoverage{index}.msgpack", new ArraySegment(eventInBytes)); - } + public override bool TryProcessEvent(IEvent @event) + { + _serializationWatch.Restart(); + var success = base.TryProcessEvent(@event); + TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointEventsSerializationMs(TelemetryEndpoint, _serializationWatch.Elapsed.TotalMilliseconds); + return success; + } - public override bool TryProcessEvent(IEvent @event) - { - _serializationWatch.Restart(); - var success = base.TryProcessEvent(@event); - TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointEventsSerializationMs(TelemetryEndpoint, _serializationWatch.Elapsed.TotalMilliseconds); - return success; - } + public override void Reset() + { + base.Reset(); - public override void Reset() - { - base.Reset(); - - // This is a current limitation in the backend side - // an "event" item must be included in each payload. - // So here we are adding a dummy one. - AddMultipartFormItem( - new MultipartFormItem( - "event", - MimeTypes.Json, - "fileevent.json", - new ArraySegment(Encoding.UTF8.GetBytes("{\"dummy\": true}")))); - } + // This is a current limitation in the backend side + // an "event" item must be included in each payload. + // So here we are adding a dummy one. + AddMultipartFormItem( + new MultipartFormItem( + "event", + MimeTypes.Json, + "fileevent.json", + new ArraySegment(Encoding.UTF8.GetBytes("{\"dummy\": true}")))); + } - internal readonly struct CoveragePayload - { - public readonly EventsBuffer TestCoverageData; + internal readonly struct CoveragePayload + { + public readonly EventsBuffer TestCoverageData; - public CoveragePayload(EventsBuffer testCoverageData) - { - TestCoverageData = testCoverageData; - } + public CoveragePayload(EventsBuffer testCoverageData) + { + TestCoverageData = testCoverageData; } } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CITestCyclePayload.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CITestCyclePayload.cs index b9b7ba464b61..251277b1506d 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CITestCyclePayload.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CITestCyclePayload.cs @@ -2,42 +2,41 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable -using System; using Datadog.Trace.Ci.Configuration; using Datadog.Trace.Ci.EventModel; using Datadog.Trace.Telemetry.Metrics; using Datadog.Trace.Vendors.MessagePack; -namespace Datadog.Trace.Ci.Agent.Payloads +namespace Datadog.Trace.Ci.Agent.Payloads; + +internal class CITestCyclePayload : CIVisibilityProtocolPayload { - internal class CITestCyclePayload : CIVisibilityProtocolPayload + public CITestCyclePayload(CIVisibilitySettings settings, IFormatterResolver? formatterResolver = null) + : base(settings, formatterResolver) { - public CITestCyclePayload(CIVisibilitySettings settings, IFormatterResolver formatterResolver = null) - : base(settings, formatterResolver) - { - } + } - public override string EventPlatformSubdomain => "citestcycle-intake"; + public override string EventPlatformSubdomain => "citestcycle-intake"; - public override string EventPlatformPath => "api/v2/citestcycle"; + public override string EventPlatformPath => "api/v2/citestcycle"; - public override MetricTags.CIVisibilityEndpoints TelemetryEndpoint => MetricTags.CIVisibilityEndpoints.TestCycle; + public override MetricTags.CIVisibilityEndpoints TelemetryEndpoint => MetricTags.CIVisibilityEndpoints.TestCycle; - public override MetricTags.CIVisibilityEndpointAndCompression TelemetryEndpointAndCompression - => UseGZip - ? MetricTags.CIVisibilityEndpointAndCompression.TestCycleRequestCompressed - : MetricTags.CIVisibilityEndpointAndCompression.TestCycleUncompressed; + public override MetricTags.CIVisibilityEndpointAndCompression TelemetryEndpointAndCompression + => UseGZip + ? MetricTags.CIVisibilityEndpointAndCompression.TestCycleRequestCompressed + : MetricTags.CIVisibilityEndpointAndCompression.TestCycleUncompressed; - public override bool CanProcessEvent(IEvent @event) + public override bool CanProcessEvent(IEvent @event) + { + // This intake accepts both Span and Test events + if (@event is CIVisibilityEvent) { - // This intake accepts both Span and Test events - if (@event is CIVisibilityEvent) - { - return true; - } - - return false; + return true; } + + return false; } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CIVisibilityProtocolPayload.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CIVisibilityProtocolPayload.cs index 5b77c52ca944..ca207f5b7d76 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CIVisibilityProtocolPayload.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/CIVisibilityProtocolPayload.cs @@ -2,8 +2,8 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable -using System; using System.Diagnostics; using System.IO; using Datadog.Trace.Ci.Agent.MessagePack; @@ -11,47 +11,46 @@ using Datadog.Trace.Telemetry; using Datadog.Trace.Vendors.MessagePack; -namespace Datadog.Trace.Ci.Agent.Payloads +namespace Datadog.Trace.Ci.Agent.Payloads; + +internal abstract class CIVisibilityProtocolPayload : EventPlatformPayload { - internal abstract class CIVisibilityProtocolPayload : EventPlatformPayload - { - private readonly EventsBuffer _events; - private readonly IFormatterResolver _formatterResolver; - private readonly Stopwatch _serializationWatch; + private readonly EventsBuffer _events; + private readonly IFormatterResolver _formatterResolver; + private readonly Stopwatch _serializationWatch; - public CIVisibilityProtocolPayload(CIVisibilitySettings settings, IFormatterResolver formatterResolver = null) - : base(settings) - { - _formatterResolver = formatterResolver ?? CIFormatterResolver.Instance; - _serializationWatch = new Stopwatch(); + public CIVisibilityProtocolPayload(CIVisibilitySettings settings, IFormatterResolver? formatterResolver = null) + : base(settings) + { + _formatterResolver = formatterResolver ?? CIFormatterResolver.Instance; + _serializationWatch = new Stopwatch(); - // Because we don't know the size of the events array envelope we left 500kb for that. - _events = new EventsBuffer(settings.MaximumAgentlessPayloadSize - (500 * 1024), _formatterResolver); - } + // Because we don't know the size of the events array envelope we left 500kb for that. + _events = new EventsBuffer(settings.MaximumAgentlessPayloadSize - (500 * 1024), _formatterResolver); + } - public override bool HasEvents => _events.Count > 0; + public override bool HasEvents => _events.Count > 0; - public override int Count => _events.Count; + public override int Count => _events.Count; - internal EventsBuffer Events => _events; + internal EventsBuffer Events => _events; - public override bool TryProcessEvent(IEvent @event) - { - _serializationWatch.Restart(); - var success = _events.TryWrite(@event); - TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointEventsSerializationMs(TelemetryEndpoint, _serializationWatch.Elapsed.TotalMilliseconds); - return success; - } + public override bool TryProcessEvent(IEvent @event) + { + _serializationWatch.Restart(); + var success = _events.TryWrite(@event); + TelemetryFactory.Metrics.RecordDistributionCIVisibilityEndpointEventsSerializationMs(TelemetryEndpoint, _serializationWatch.Elapsed.TotalMilliseconds); + return success; + } - public override void Reset() => _events.Clear(); + public override void Reset() => _events.Clear(); - public byte[] ToArray() => MessagePackSerializer.Serialize(this, _formatterResolver); + public byte[] ToArray() => MessagePackSerializer.Serialize(this, _formatterResolver); - public int WriteTo(Stream stream) - { - var buffer = MessagePackSerializer.SerializeUnsafe(this, _formatterResolver); - stream.Write(buffer.Array, buffer.Offset, buffer.Count); - return buffer.Count; - } + public int WriteTo(Stream stream) + { + var buffer = MessagePackSerializer.SerializeUnsafe(this, _formatterResolver); + stream.Write(buffer.Array!, buffer.Offset, buffer.Count); + return buffer.Count; } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs index c2181cccdcc0..84b343e439c1 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs @@ -2,177 +2,177 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using Datadog.Trace.Agent; using Datadog.Trace.Ci.Configuration; using Datadog.Trace.Telemetry.Metrics; -using Datadog.Trace.Util; -namespace Datadog.Trace.Ci.Agent.Payloads +namespace Datadog.Trace.Ci.Agent.Payloads; + +/// +/// Event-platform payload +/// +internal abstract class EventPlatformPayload { + private readonly CIVisibilitySettings _settings; + private bool? _useGzip; + private Uri? _url; + + protected EventPlatformPayload(CIVisibilitySettings settings) + { + if (settings is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(settings)); + } + + _settings = settings; + _useGzip = null; + UseEvpProxy = !settings.Agentless; + } + /// - /// Event-platform payload + /// Gets Event-Platform subdomain/track /// - internal abstract class EventPlatformPayload - { - private readonly CIVisibilitySettings _settings; - private bool? _useGzip; - private Uri _url; + public abstract string EventPlatformSubdomain { get; } + + /// + /// Gets Event-Platform path + /// + public abstract string EventPlatformPath { get; } + + /// + /// Gets the Telemetry endpoint enum + /// + public abstract MetricTags.CIVisibilityEndpoints TelemetryEndpoint { get; } - protected EventPlatformPayload(CIVisibilitySettings settings) + /// + /// Gets the Telemetry endpoint and compression enum + /// + public abstract MetricTags.CIVisibilityEndpointAndCompression TelemetryEndpointAndCompression { get; } + + /// + /// Gets or sets the Payload url + /// + public Uri Url + { + get { - if (settings is null) + if (_url is null) { - ThrowHelper.ThrowArgumentNullException(nameof(settings)); + return EnsureUrl(); } - _settings = settings; - _useGzip = null; - UseEvpProxy = !settings.Agentless; + return _url; } - /// - /// Gets Event-Platform subdomain/track - /// - public abstract string EventPlatformSubdomain { get; } - - /// - /// Gets Event-Platform path - /// - public abstract string EventPlatformPath { get; } - - /// - /// Gets the Telemetry endpoint enum - /// - public abstract MetricTags.CIVisibilityEndpoints TelemetryEndpoint { get; } - - /// - /// Gets the Telemetry endpoint and compression enum - /// - public abstract MetricTags.CIVisibilityEndpointAndCompression TelemetryEndpointAndCompression { get; } - - /// - /// Gets or sets the Payload url - /// - public Uri Url + set { - get - { - if (_url is null) - { - EnsureUrl(); - } + _url = value; + } + } - return _url; - } + /// + /// Gets a value indicating whether the payload is configured to use the agent proxy + /// + public virtual bool UseEvpProxy { get; } - set + /// + /// Gets a value indicating whether the payload need to be gzipped + /// + public virtual bool UseGZip + { + get + { + if (_useGzip is null) { - _url = value; + EnsureUrl(); } + + return _useGzip ?? false; } + } + + /// + /// Gets a value indicating whether the payload contains events or not + /// + public abstract bool HasEvents { get; } - /// - /// Gets a value indicating whether the payload is configured to use the agent proxy - /// - public virtual bool UseEvpProxy { get; } + /// + /// Gets the number of events in the payload + /// + public abstract int Count { get; } - /// - /// Gets a value indicating whether the payload need to be gzipped - /// - public virtual bool UseGZip + /// + /// Checks if the payload can process a given event + /// + /// Event to be processed + /// True if the event can be processed by the payload instance; otherwise false. + public abstract bool CanProcessEvent(IEvent @event); + + /// + /// Try to process an event + /// + /// Event to be processed + /// True if the event has been processed by the payload instance; otherwise false. + public abstract bool TryProcessEvent(IEvent @event); + + /// + /// Resets payload buffer + /// + public abstract void Reset(); + + private Uri EnsureUrl() + { + UriBuilder builder; + if (_settings.Agentless) { - get + var agentlessUrl = _settings.AgentlessUrl; + if (!string.IsNullOrWhiteSpace(agentlessUrl)) { - if (_useGzip is null) - { - EnsureUrl(); - } - - return _useGzip ?? false; + builder = new UriBuilder(agentlessUrl); + builder.Path = EventPlatformPath; + } + else + { + builder = new UriBuilder( + scheme: "https", + host: $"{EventPlatformSubdomain}.{_settings.Site}", + port: 443, + pathValue: EventPlatformPath); } - } - /// - /// Gets a value indicating whether the payload contains events or not - /// - public abstract bool HasEvents { get; } - - /// - /// Gets the number of events in the payload - /// - public abstract int Count { get; } - - /// - /// Checks if the payload can process a given event - /// - /// Event to be processed - /// True if the event can be processed by the payload instance; otherwise false. - public abstract bool CanProcessEvent(IEvent @event); - - /// - /// Try to process an event - /// - /// Event to be processed - /// True if the event has been processed by the payload instance; otherwise false. - public abstract bool TryProcessEvent(IEvent @event); - - /// - /// Resets payload buffer - /// - public abstract void Reset(); - - private void EnsureUrl() + _useGzip = true; + } + else { - UriBuilder builder; - if (_settings.Agentless) + // Use Agent EVP Proxy + switch (_settings.TracerSettings.ExporterInternal.TracesTransport) { - var agentlessUrl = _settings.AgentlessUrl; - if (!string.IsNullOrWhiteSpace(agentlessUrl)) - { - builder = new UriBuilder(agentlessUrl); - builder.Path = EventPlatformPath; - } - else - { - builder = new UriBuilder( - scheme: "https", - host: $"{EventPlatformSubdomain}.{_settings.Site}", - port: 443, - pathValue: EventPlatformPath); - } + case TracesTransportType.WindowsNamedPipe: + case TracesTransportType.UnixDomainSocket: + builder = new UriBuilder("http://localhost"); + break; + case TracesTransportType.Default: + default: + builder = new UriBuilder(_settings.TracerSettings.ExporterInternal.AgentUriInternal); + break; + } + if (CIVisibility.EventPlatformProxySupport == EventPlatformProxySupport.V4) + { + builder.Path = $"/evp_proxy/v4/{EventPlatformPath}"; _useGzip = true; } - else + else if (CIVisibility.EventPlatformProxySupport == EventPlatformProxySupport.V2) { - // Use Agent EVP Proxy - switch (_settings.TracerSettings.ExporterInternal.TracesTransport) - { - case TracesTransportType.WindowsNamedPipe: - case TracesTransportType.UnixDomainSocket: - builder = new UriBuilder("http://localhost"); - break; - case TracesTransportType.Default: - default: - builder = new UriBuilder(_settings.TracerSettings.ExporterInternal.AgentUriInternal); - break; - } - - if (CIVisibility.EventPlatformProxySupport == EventPlatformProxySupport.V4) - { - builder.Path = $"/evp_proxy/v4/{EventPlatformPath}"; - _useGzip = true; - } - else if (CIVisibility.EventPlatformProxySupport == EventPlatformProxySupport.V2) - { - builder.Path = $"/evp_proxy/v2/{EventPlatformPath}"; - _useGzip = false; - } + builder.Path = $"/evp_proxy/v2/{EventPlatformPath}"; + _useGzip = false; } - - _url = builder.Uri; } + + _url = builder.Uri; + return builder.Uri; } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventsBuffer.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventsBuffer.cs index c4378675ff3c..9a0e07422e50 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventsBuffer.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventsBuffer.cs @@ -2,161 +2,160 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Threading; using Datadog.Trace.Logging; -using Datadog.Trace.Util; using Datadog.Trace.Vendors.MessagePack; using Datadog.Trace.Vendors.MessagePack.Formatters; -namespace Datadog.Trace.Ci.Agent.Payloads +namespace Datadog.Trace.Ci.Agent.Payloads; + +internal class EventsBuffer { - internal class EventsBuffer - { - protected static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor>(); + protected static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor>(); - internal const int HeaderSize = 5; - internal const int InitialBufferSize = 64 * 1024; + internal const int HeaderSize = 5; + internal const int InitialBufferSize = 64 * 1024; - private readonly IMessagePackFormatter _formatter; - private readonly IFormatterResolver _formatterResolver; - private readonly object _syncRoot = new object(); - private readonly int _maxBufferSize; + private readonly IMessagePackFormatter _formatter; + private readonly IFormatterResolver _formatterResolver; + private readonly object _syncRoot = new object(); + private readonly int _maxBufferSize; - private byte[] _buffer; - private bool _locked; - private int _offset; + private byte[] _buffer; + private bool _locked; + private int _offset; - public EventsBuffer(int maxBufferSize, IFormatterResolver formatterResolver) + public EventsBuffer(int maxBufferSize, IFormatterResolver formatterResolver) + { + if (maxBufferSize < HeaderSize) { - if (maxBufferSize < HeaderSize) - { - ThrowHelper.ThrowArgumentException($"Buffer size should be at least {HeaderSize}", nameof(maxBufferSize)); - } + ThrowHelper.ThrowArgumentException($"Buffer size should be at least {HeaderSize}", nameof(maxBufferSize)); + } - _maxBufferSize = maxBufferSize; - _offset = HeaderSize; - _buffer = new byte[Math.Min(InitialBufferSize, maxBufferSize)]; - _formatterResolver = formatterResolver; - _formatter = _formatterResolver.GetFormatter(); + _maxBufferSize = maxBufferSize; + _offset = HeaderSize; + _buffer = new byte[Math.Min(InitialBufferSize, maxBufferSize)]; + _formatterResolver = formatterResolver; + _formatter = _formatterResolver.GetFormatter(); - if (_formatter is null) - { - ThrowHelper.ThrowNullReferenceException($"Formatter for '{_formatter}' is null"); - } + if (_formatter is null) + { + ThrowHelper.ThrowNullReferenceException($"Formatter for '{_formatter}' is null"); } + } - public ArraySegment Data + public ArraySegment Data + { + get { - get + if (!_locked) { - if (!_locked) - { - // Sanity check - headers are written when the buffer is locked - ThrowHelper.ThrowInvalidOperationException("Data was extracted from the buffer without locking"); - } - - return new ArraySegment(_buffer, 0, _offset); + // Sanity check - headers are written when the buffer is locked + ThrowHelper.ThrowInvalidOperationException("Data was extracted from the buffer without locking"); } + + return new ArraySegment(_buffer, 0, _offset); } + } - public int Count { get; private set; } + public int Count { get; private set; } - public bool IsFull { get; private set; } + public bool IsFull { get; private set; } - // For tests only - internal bool IsLocked => _locked; + // For tests only + internal bool IsLocked => _locked; - // For tests only - internal bool IsEmpty => !_locked && !IsFull && Count == 0 && _offset == HeaderSize; + // For tests only + internal bool IsEmpty => !_locked && !IsFull && Count == 0 && _offset == HeaderSize; + + public bool TryWrite(T item) + { + bool lockTaken = false; - public bool TryWrite(T item) + try { - bool lockTaken = false; + Monitor.TryEnter(_syncRoot, ref lockTaken); - try + if (!lockTaken || _locked) { - Monitor.TryEnter(_syncRoot, ref lockTaken); + // A flush operation is in progress, consider this buffer full + return false; + } - if (!lockTaken || _locked) - { - // A flush operation is in progress, consider this buffer full - return false; - } + if (IsFull) + { + // Buffer is full + return false; + } - if (IsFull) + try + { + // We serialize the item + // Note: By serializing an item near the buffer limit can make the buffer to grow + // and at the same time we will reject that item. Although we don't expect that happens + // too often. + var size = _formatter.Serialize(ref _buffer, _offset, item, _formatterResolver); + + // In case we overpass the max buffer size, we reject the last serialization. + if (_offset + size > _maxBufferSize) { - // Buffer is full + IsFull = true; return false; } - try - { - // We serialize the item - // Note: By serializing an item near the buffer limit can make the buffer to grow - // and at the same time we will reject that item. Although we don't expect that happens - // too often. - var size = _formatter.Serialize(ref _buffer, _offset, item, _formatterResolver); - - // In case we overpass the max buffer size, we reject the last serialization. - if (_offset + size > _maxBufferSize) - { - IsFull = true; - return false; - } - - // Move the offset to accept the last serialization and increase the counter - _offset += size; - Count++; - } - catch (Exception ex) - { - Log.Error(ex, "Error serializing item to EventsBuffer"); - } - - if (_offset >= _maxBufferSize) - { - IsFull = true; - } + // Move the offset to accept the last serialization and increase the counter + _offset += size; + Count++; + } + catch (Exception ex) + { + Log.Error(ex, "Error serializing item to EventsBuffer"); + } - return true; + if (_offset >= _maxBufferSize) + { + IsFull = true; } - finally + + return true; + } + finally + { + if (lockTaken) { - if (lockTaken) - { - Monitor.Exit(_syncRoot); - } + Monitor.Exit(_syncRoot); } } + } - public bool Lock() + public bool Lock() + { + lock (_syncRoot) { - lock (_syncRoot) + if (_locked) { - if (_locked) - { - return false; - } + return false; + } - // Use a fixed-size header - MessagePackBinary.WriteArrayHeaderForceArray32Block(ref _buffer, 0, (uint)Count); - _locked = true; + // Use a fixed-size header + MessagePackBinary.WriteArrayHeaderForceArray32Block(ref _buffer, 0, (uint)Count); + _locked = true; - return true; - } + return true; } + } - public void Clear() + public void Clear() + { + lock (_syncRoot) { - lock (_syncRoot) - { - _offset = HeaderSize; - Count = 0; - IsFull = false; - _locked = false; - } + _offset = HeaderSize; + Count = 0; + IsFull = false; + _locked = false; } } } diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/MultipartPayload.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/MultipartPayload.cs index 94d23a20439a..c305d1b1876e 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/MultipartPayload.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/MultipartPayload.cs @@ -2,83 +2,82 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; using Datadog.Trace.Agent; using Datadog.Trace.Ci.Agent.MessagePack; using Datadog.Trace.Ci.Configuration; -using Datadog.Trace.Util; using Datadog.Trace.Vendors.MessagePack; -namespace Datadog.Trace.Ci.Agent.Payloads +namespace Datadog.Trace.Ci.Agent.Payloads; + +internal abstract class MultipartPayload : EventPlatformPayload { - internal abstract class MultipartPayload : EventPlatformPayload - { - internal const int DefaultMaxItemsPerPayload = 100; - internal const int DefaultMaxBytesPerPayload = 48_000_000; - internal const int HeaderSize = 1024; + internal const int DefaultMaxItemsPerPayload = 100; + internal const int DefaultMaxBytesPerPayload = 48_000_000; + internal const int HeaderSize = 1024; - private readonly EventsBuffer _events; - private readonly List _items; - private readonly IFormatterResolver _formatterResolver; - private readonly int _maxItemsPerPayload; - private readonly int _maxBytesPerPayload; + private readonly EventsBuffer _events; + private readonly List _items; + private readonly IFormatterResolver _formatterResolver; + private readonly int _maxItemsPerPayload; + private readonly int _maxBytesPerPayload; - public MultipartPayload(CIVisibilitySettings settings, int maxItemsPerPayload = DefaultMaxItemsPerPayload, int maxBytesPerPayload = DefaultMaxBytesPerPayload, IFormatterResolver formatterResolver = null) - : base(settings) + public MultipartPayload(CIVisibilitySettings settings, int maxItemsPerPayload = DefaultMaxItemsPerPayload, int maxBytesPerPayload = DefaultMaxBytesPerPayload, IFormatterResolver? formatterResolver = null) + : base(settings) + { + if (maxBytesPerPayload < HeaderSize) { - if (maxBytesPerPayload < HeaderSize) - { - ThrowHelper.ThrowArgumentException($"Buffer size should be at least {HeaderSize}", nameof(maxBytesPerPayload)); - } + ThrowHelper.ThrowArgumentException($"Buffer size should be at least {HeaderSize}", nameof(maxBytesPerPayload)); + } - _maxItemsPerPayload = maxItemsPerPayload; - _maxBytesPerPayload = maxBytesPerPayload; - _formatterResolver = formatterResolver ?? CIFormatterResolver.Instance; - _items = new List(Math.Min(maxItemsPerPayload, DefaultMaxItemsPerPayload)); + _maxItemsPerPayload = maxItemsPerPayload; + _maxBytesPerPayload = maxBytesPerPayload; + _formatterResolver = formatterResolver ?? CIFormatterResolver.Instance; + _items = new List(Math.Min(maxItemsPerPayload, DefaultMaxItemsPerPayload)); - // Because we don't know the size of the events array envelope we left 1024kb for that. - _events = new EventsBuffer(Math.Min(_maxBytesPerPayload, DefaultMaxBytesPerPayload) - HeaderSize, _formatterResolver); - } + // Because we don't know the size of the events array envelope we left 1024kb for that. + _events = new EventsBuffer(Math.Min(_maxBytesPerPayload, DefaultMaxBytesPerPayload) - HeaderSize, _formatterResolver); + } - public override bool HasEvents => _events.Count > 0; + public override bool HasEvents => _events.Count > 0; - public override int Count => _items.Count + (_events.Count > 0 ? 1 : 0); + public override int Count => _items.Count + (_events.Count > 0 ? 1 : 0); - internal EventsBuffer Events => _events; + internal EventsBuffer Events => _events; - protected abstract MultipartFormItem CreateMultipartFormItem(EventsBuffer eventsBuffer); + protected abstract MultipartFormItem CreateMultipartFormItem(EventsBuffer eventsBuffer); - protected void AddMultipartFormItem(MultipartFormItem item) + protected void AddMultipartFormItem(MultipartFormItem item) + { + lock (_items) { - lock (_items) + if (_items.Count < _maxItemsPerPayload - 1) { - if (_items.Count < _maxItemsPerPayload - 1) - { - _items.Add(item); - } + _items.Add(item); } } + } - public override bool TryProcessEvent(IEvent @event) => _events.TryWrite(@event); + public override bool TryProcessEvent(IEvent @event) => _events.TryWrite(@event); - public override void Reset() + public override void Reset() + { + lock (_items) { - lock (_items) - { - _events.Clear(); - _items.Clear(); - } + _events.Clear(); + _items.Clear(); } + } - public MultipartFormItem[] ToArray() + public MultipartFormItem[] ToArray() + { + lock (_items) { - lock (_items) - { - _items.Add(CreateMultipartFormItem(_events)); - return _items.ToArray(); - } + _items.Add(CreateMultipartFormItem(_events)); + return _items.ToArray(); } } } diff --git a/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/AvoidCoverageAttribute.cs b/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/AvoidCoverageAttribute.cs index 4d92afbffca6..54c1fc6e80c3 100644 --- a/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/AvoidCoverageAttribute.cs +++ b/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/AvoidCoverageAttribute.cs @@ -2,16 +2,16 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; -namespace Datadog.Trace.Ci.Coverage.Attributes +namespace Datadog.Trace.Ci.Coverage.Attributes; + +/// +/// Avoid coverage attribute +/// Used to ignore processing an assembly +/// +public class AvoidCoverageAttribute : Attribute { - /// - /// Avoid coverage attribute - /// Used to ignore processing an assembly - /// - public class AvoidCoverageAttribute : Attribute - { - } } diff --git a/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/CoveredAssemblyAttribute.cs b/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/CoveredAssemblyAttribute.cs index cda16299fc25..b076d444762b 100644 --- a/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/CoveredAssemblyAttribute.cs +++ b/tracer/src/Datadog.Trace/Ci/Coverage/Attributes/CoveredAssemblyAttribute.cs @@ -2,16 +2,16 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; -namespace Datadog.Trace.Ci.Coverage.Attributes +namespace Datadog.Trace.Ci.Coverage.Attributes; + +/// +/// Covered assembly attribute +/// This attributes marks an assembly as a processed. +/// +public class CoveredAssemblyAttribute : Attribute { - /// - /// Covered assembly attribute - /// This attributes marks an assembly as a processed. - /// - public class CoveredAssemblyAttribute : Attribute - { - } } diff --git a/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs b/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs index c68b7efcb54b..3b9c899cd75f 100644 --- a/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs +++ b/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs @@ -2,25 +2,25 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Runtime.CompilerServices; -namespace Datadog.Trace.Ci.Coverage.Exceptions +namespace Datadog.Trace.Ci.Coverage.Exceptions; + +/// +/// Pdb not found +/// +internal class PdbNotFoundException : Exception { /// - /// Pdb not found + /// Throw the exception /// - internal class PdbNotFoundException : Exception + /// Throws current exception + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Throw() { - /// - /// Throw the exception - /// - /// Throws current exception - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Throw() - { - throw new PdbNotFoundException(); - } + throw new PdbNotFoundException(); } } diff --git a/tracer/src/Datadog.Trace/Ci/Tagging/TestModuleSpanTags.cs b/tracer/src/Datadog.Trace/Ci/Tagging/TestModuleSpanTags.cs index 89ed468cf9c0..60e20dea3fc3 100644 --- a/tracer/src/Datadog.Trace/Ci/Tagging/TestModuleSpanTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tagging/TestModuleSpanTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Threading; using Datadog.Trace.Ci.Tags; @@ -27,37 +28,37 @@ public TestModuleSpanTags(TestSessionSpanTags sessionTags) public ulong ModuleId { get; set; } [Tag(TestTags.Type)] - public string Type { get; set; } + public string? Type { get; set; } [Tag(TestTags.Module)] - public string Module { get; set; } + public string? Module { get; set; } [Tag(TestTags.Bundle)] - public string Bundle => Module; + public string? Bundle => Module; [Tag(TestTags.Framework)] - public string Framework { get; set; } + public string? Framework { get; set; } [Tag(TestTags.FrameworkVersion)] - public string FrameworkVersion { get; set; } + public string? FrameworkVersion { get; set; } [Tag(CommonTags.RuntimeName)] - public string RuntimeName { get; set; } + public string? RuntimeName { get; set; } [Tag(CommonTags.RuntimeVersion)] - public string RuntimeVersion { get; set; } + public string? RuntimeVersion { get; set; } [Tag(CommonTags.RuntimeArchitecture)] - public string RuntimeArchitecture { get; set; } + public string? RuntimeArchitecture { get; set; } [Tag(CommonTags.OSArchitecture)] - public string OSArchitecture { get; set; } + public string? OSArchitecture { get; set; } [Tag(CommonTags.OSPlatform)] - public string OSPlatform { get; set; } + public string? OSPlatform { get; set; } [Tag(CommonTags.OSVersion)] - public string OSVersion { get; set; } + public string? OSVersion { get; set; } [Metric(IntelligentTestRunnerTags.SkippingCount)] public double? IntelligentTestRunnerSkippingCount => _itrSkippingCount == 0 ? null : _itrSkippingCount; diff --git a/tracer/src/Datadog.Trace/Ci/Tagging/TestSessionSpanTags.cs b/tracer/src/Datadog.Trace/Ci/Tagging/TestSessionSpanTags.cs index 3ef5d5c6f88c..19eb200fe81b 100644 --- a/tracer/src/Datadog.Trace/Ci/Tagging/TestSessionSpanTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tagging/TestSessionSpanTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Globalization; @@ -24,109 +25,109 @@ public TestSessionSpanTags() public ulong SessionId { get; set; } [Tag(TestTags.Command)] - public string Command { get; set; } + public string? Command { get; set; } [Tag(TestTags.CommandWorkingDirectory)] - public string WorkingDirectory { get; set; } + public string? WorkingDirectory { get; set; } [Tag(TestTags.CommandExitCode)] - public string CommandExitCode { get; set; } + public string? CommandExitCode { get; set; } [Tag(TestTags.Status)] - public string Status { get; set; } + public string? Status { get; set; } [Tag(CommonTags.LibraryVersion)] - public string LibraryVersion { get; } + public string? LibraryVersion { get; } [Tag(CommonTags.CIProvider)] - public string CIProvider { get; set; } + public string? CIProvider { get; set; } [Tag(CommonTags.CIPipelineId)] - public string CIPipelineId { get; set; } + public string? CIPipelineId { get; set; } [Tag(CommonTags.CIPipelineName)] - public string CIPipelineName { get; set; } + public string? CIPipelineName { get; set; } [Tag(CommonTags.CIPipelineNumber)] - public string CIPipelineNumber { get; set; } + public string? CIPipelineNumber { get; set; } [Tag(CommonTags.CIPipelineUrl)] - public string CIPipelineUrl { get; set; } + public string? CIPipelineUrl { get; set; } [Tag(CommonTags.CIJobUrl)] - public string CIJobUrl { get; set; } + public string? CIJobUrl { get; set; } [Tag(CommonTags.CIJobName)] - public string CIJobName { get; set; } + public string? CIJobName { get; set; } [Tag(CommonTags.StageName)] - public string StageName { get; set; } + public string? StageName { get; set; } [Tag(CommonTags.CIWorkspacePath)] - public string CIWorkspacePath { get; set; } + public string? CIWorkspacePath { get; set; } [Tag(CommonTags.GitRepository)] - public string GitRepository { get; set; } + public string? GitRepository { get; set; } [Tag(CommonTags.GitCommit)] - public string GitCommit { get; set; } + public string? GitCommit { get; set; } [Tag(CommonTags.GitBranch)] - public string GitBranch { get; set; } + public string? GitBranch { get; set; } [Tag(CommonTags.GitTag)] - public string GitTag { get; set; } + public string? GitTag { get; set; } [Tag(CommonTags.GitCommitAuthorName)] - public string GitCommitAuthorName { get; set; } + public string? GitCommitAuthorName { get; set; } [Tag(CommonTags.GitCommitAuthorEmail)] - public string GitCommitAuthorEmail { get; set; } + public string? GitCommitAuthorEmail { get; set; } [Tag(CommonTags.GitCommitCommitterName)] - public string GitCommitCommitterName { get; set; } + public string? GitCommitCommitterName { get; set; } [Tag(CommonTags.GitCommitCommitterEmail)] - public string GitCommitCommitterEmail { get; set; } + public string? GitCommitCommitterEmail { get; set; } [Tag(CommonTags.GitCommitMessage)] - public string GitCommitMessage { get; set; } + public string? GitCommitMessage { get; set; } [Tag(CommonTags.BuildSourceRoot)] - public string BuildSourceRoot { get; set; } + public string? BuildSourceRoot { get; set; } [Tag(CommonTags.GitCommitAuthorDate)] - public string GitCommitAuthorDate { get; set; } + public string? GitCommitAuthorDate { get; set; } [Tag(CommonTags.GitCommitCommitterDate)] - public string GitCommitCommitterDate { get; set; } + public string? GitCommitCommitterDate { get; set; } [Tag(CommonTags.CiEnvVars)] - public string CiEnvVars { get; set; } + public string? CiEnvVars { get; set; } [Tag(IntelligentTestRunnerTags.TestsSkipped)] - public string TestsSkipped { get; set; } + public string? TestsSkipped { get; set; } [Tag(IntelligentTestRunnerTags.SkippingType)] - public string IntelligentTestRunnerSkippingType { get; set; } + public string? IntelligentTestRunnerSkippingType { get; set; } [Tag(EarlyFlakeDetectionTags.Enabled)] - public string EarlyFlakeDetectionTestEnabled { get; set; } + public string? EarlyFlakeDetectionTestEnabled { get; set; } [Tag(EarlyFlakeDetectionTags.AbortReason)] - public string EarlyFlakeDetectionTestAbortReason { get; set; } + public string? EarlyFlakeDetectionTestAbortReason { get; set; } [Metric(CommonTags.LogicalCpuCount)] public double? LogicalCpuCount { get; } [Tag(CommonTags.GitHeadCommit)] - public string GitHeadCommit { get; set; } + public string? GitHeadCommit { get; set; } [Tag(CommonTags.GitPrBaseCommit)] - public string GitPrBaseCommit { get; set; } + public string? GitPrBaseCommit { get; set; } [Tag(CommonTags.GitPrBaseBranch)] - public string GitPrBaseBranch { get; set; } + public string? GitPrBaseBranch { get; set; } public void SetCIEnvironmentValues(CIEnvironmentValues environmentValues) { diff --git a/tracer/src/Datadog.Trace/Ci/Tagging/TestSpanTags.cs b/tracer/src/Datadog.Trace/Ci/Tagging/TestSpanTags.cs index 4054cfb19fdb..267184db120c 100644 --- a/tracer/src/Datadog.Trace/Ci/Tagging/TestSpanTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tagging/TestSpanTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.Ci.Tags; using Datadog.Trace.SourceGenerators; @@ -60,13 +61,13 @@ public TestSpanTags(TestSuiteSpanTags suiteTags, string testName) } [Tag(TestTags.Name)] - public string Name { get; set; } + public string? Name { get; set; } [Tag(TestTags.Parameters)] - public string Parameters { get; set; } + public string? Parameters { get; set; } [Tag(TestTags.SourceFile)] - public string SourceFile { get; set; } + public string? SourceFile { get; set; } [Metric(TestTags.SourceStart)] public double? SourceStart { get; set; } @@ -75,41 +76,41 @@ public TestSpanTags(TestSuiteSpanTags suiteTags, string testName) public double? SourceEnd { get; set; } [Tag(TestTags.CodeOwners)] - public string CodeOwners { get; set; } + public string? CodeOwners { get; set; } [Tag(TestTags.Traits)] - public string Traits { get; set; } + public string? Traits { get; set; } [Tag(TestTags.SkipReason)] - public string SkipReason { get; set; } + public string? SkipReason { get; set; } [Tag(IntelligentTestRunnerTags.SkippedBy)] - public string SkippedByIntelligentTestRunner { get; set; } + public string? SkippedByIntelligentTestRunner { get; set; } [Tag(IntelligentTestRunnerTags.UnskippableTag)] - public string Unskippable { get; set; } + public string? Unskippable { get; set; } [Tag(IntelligentTestRunnerTags.ForcedRunTag)] - public string ForcedRun { get; set; } + public string? ForcedRun { get; set; } [Tag(EarlyFlakeDetectionTags.TestIsNew)] - public string EarlyFlakeDetectionTestIsNew { get; set; } + public string? EarlyFlakeDetectionTestIsNew { get; set; } [Tag(EarlyFlakeDetectionTags.TestIsRetry)] - public string EarlyFlakeDetectionTestIsRetry { get; set; } + public string? EarlyFlakeDetectionTestIsRetry { get; set; } [Tag(BrowserTags.BrowserDriver)] - public string BrowserDriver { get; set; } + public string? BrowserDriver { get; set; } [Tag(BrowserTags.BrowserDriverVersion)] - public string BrowserDriverVersion { get; set; } + public string? BrowserDriverVersion { get; set; } [Tag(BrowserTags.BrowserName)] - public string BrowserName { get; set; } + public string? BrowserName { get; set; } [Tag(BrowserTags.BrowserVersion)] - public string BrowserVersion { get; set; } + public string? BrowserVersion { get; set; } [Tag(BrowserTags.IsRumActive)] - public string IsRumActive { get; set; } + public string? IsRumActive { get; set; } } diff --git a/tracer/src/Datadog.Trace/Ci/Tagging/TestSuiteSpanTags.cs b/tracer/src/Datadog.Trace/Ci/Tagging/TestSuiteSpanTags.cs index 37e4edc8d335..78431ff8a5cc 100644 --- a/tracer/src/Datadog.Trace/Ci/Tagging/TestSuiteSpanTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tagging/TestSuiteSpanTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.Ci.Tags; using Datadog.Trace.SourceGenerators; @@ -60,5 +61,5 @@ public TestSuiteSpanTags(TestModuleSpanTags moduleTags, string suiteName) public ulong SuiteId { get; set; } [Tag(TestTags.Suite)] - public string Suite { get; set; } + public string? Suite { get; set; } } diff --git a/tracer/src/Datadog.Trace/Ci/Tags/BenchmarkTestTags.cs b/tracer/src/Datadog.Trace/Ci/Tags/BenchmarkTestTags.cs index 12fcfbc6e0b5..90f7f7152e87 100644 --- a/tracer/src/Datadog.Trace/Ci/Tags/BenchmarkTestTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tags/BenchmarkTestTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci.Tags; diff --git a/tracer/src/Datadog.Trace/Ci/Tags/BuildTags.cs b/tracer/src/Datadog.Trace/Ci/Tags/BuildTags.cs index bf75ed62e5f8..fe299a3aa8ef 100644 --- a/tracer/src/Datadog.Trace/Ci/Tags/BuildTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tags/BuildTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci.Tags { diff --git a/tracer/src/Datadog.Trace/Ci/Tags/CodeCoverageTags.cs b/tracer/src/Datadog.Trace/Ci/Tags/CodeCoverageTags.cs index 49993a683878..90db5b73b42f 100644 --- a/tracer/src/Datadog.Trace/Ci/Tags/CodeCoverageTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tags/CodeCoverageTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci.Tags; diff --git a/tracer/src/Datadog.Trace/Ci/Tags/CommonTags.cs b/tracer/src/Datadog.Trace/Ci/Tags/CommonTags.cs index d7145a8af753..d8a2a42183d0 100644 --- a/tracer/src/Datadog.Trace/Ci/Tags/CommonTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tags/CommonTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci.Tags; diff --git a/tracer/src/Datadog.Trace/Ci/Tags/IntelligentTestRunnerTags.cs b/tracer/src/Datadog.Trace/Ci/Tags/IntelligentTestRunnerTags.cs index c8e5192445f0..4e1e1daaa022 100644 --- a/tracer/src/Datadog.Trace/Ci/Tags/IntelligentTestRunnerTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tags/IntelligentTestRunnerTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci.Tags; diff --git a/tracer/src/Datadog.Trace/Ci/Tags/TestTags.cs b/tracer/src/Datadog.Trace/Ci/Tags/TestTags.cs index 625574b24349..c1c3db1c8e1a 100644 --- a/tracer/src/Datadog.Trace/Ci/Tags/TestTags.cs +++ b/tracer/src/Datadog.Trace/Ci/Tags/TestTags.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.Ci.Tags; From 5f7eae39bfde718ec251ce0b637207cd3e05ab63 Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 11:33:22 +0100 Subject: [PATCH 4/9] Add MsTest integration --- tracer/missing-nullability-files.csv | 25 -- tracer/src/Datadog.Trace/Ci/TestParameters.cs | 2 +- tracer/src/Datadog.Trace/Ci/TestSession.cs | 4 +- .../AutoInstrumentationExtensions.cs | 25 +- .../Ci/Proxies/ITestParameters.cs | 2 +- .../AutoInstrumentation/Testing/Common.cs | 280 +++++++++--------- .../Testing/MsTestV2/ITestAssemblyInfo.cs | 3 +- .../Testing/MsTestV2/ITestClassInfo.cs | 7 +- .../Testing/MsTestV2/ITestMethod.cs | 13 +- .../Testing/MsTestV2/ITestMethodInfo.cs | 5 +- .../Testing/MsTestV2/ITestMethodOptions.cs | 3 +- .../Testing/MsTestV2/ITestMethodRunner.cs | 4 +- .../Testing/MsTestV2/ITestResult.cs | 8 +- .../MsTestV2/ItrSkipTestMethodExecutor.cs | 12 +- .../Testing/MsTestV2/MsTestIntegration.cs | 53 ++-- ...lyInfoExecuteAssemblyCleanupIntegration.cs | 3 +- ...semblyInfoRunAssemblyCleanupIntegration.cs | 5 +- ...blyInfoRunAssemblyInitializeIntegration.cs | 7 +- .../MsTestV2/TestCategoryAttributeStruct.cs | 3 +- ...ClassInfoExecuteClassCleanupIntegration.cs | 3 +- ...TestClassInfoRunClassCleanupIntegration.cs | 9 +- ...tClassInfoRunClassInitializeIntegration.cs | 9 +- .../Testing/MsTestV2/TestContextStruct.cs | 1 + .../TestMethodAttributeExecuteIntegration.cs | 40 ++- .../MsTestV2/TestMethodContextStruct.cs | 7 +- .../TestMethodRunnerExecuteIntegration.cs | 8 +- .../TestMethodRunnerExecuteTestIntegration.cs | 13 +- .../MsTestV2/TestPropertyAttributeStruct.cs | 5 +- .../Testing/MsTestV2/UnitTestOutcome.cs | 1 + .../Testing/MsTestV2/UnitTestResultOutcome.cs | 1 + .../Testing/MsTestV2/UnitTestResultStruct.cs | 5 +- .../UnitTestRunnerRunCleanupIntegration.cs | 5 +- .../UnitTestRunnerRunSingleTestIntegration.cs | 12 +- .../Testing/NUnit/NUnitIntegration.cs | 2 +- .../Testing/XUnit/XUnitIntegration.cs | 2 +- 35 files changed, 305 insertions(+), 282 deletions(-) diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index 6235de6b1211..37658262d5ef 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -259,7 +259,6 @@ src/Datadog.Trace/Agent/Transports/MimeTypes.cs src/Datadog.Trace/Agent/Transports/SocketHandlerRequestFactory.cs src/Datadog.Trace/AppSec/Waf/WafConstants.cs src/Datadog.Trace/AppSec/Waf/WafReturnCode.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs src/Datadog.Trace/ClrProfiler/Helpers/HttpBypassHelper.cs src/Datadog.Trace/ClrProfiler/Helpers/Interception.cs src/Datadog.Trace/ClrProfiler/ServerlessInstrumentation/LambdaMetadata.cs @@ -510,7 +509,6 @@ src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Process/EnvironmentVariablesSc src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Process/ProcessStartIntegration.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Process/ProcessStartTags.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/RabbitMQ/RabbitMQConstants.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/Common.cs src/Datadog.Trace/Debugger/Configurations/Models/Capture.cs src/Datadog.Trace/Debugger/Configurations/Models/DebuggerExpression.cs src/Datadog.Trace/Debugger/Configurations/Models/EvaluateAt.cs @@ -654,29 +652,6 @@ src/Datadog.Trace/ClrProfiler/AutoInstrumentation/MongoDb/BsonSerialization/Mong src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Redis/ServiceStack/IRedisNativeClient.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Redis/ServiceStack/RedisNativeClientSendReceiveIntegration.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Redis/ServiceStack/RedisNativeClientSendReceiveIntegration_6_2_0.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestAssemblyInfo.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestClassInfo.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethod.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfo.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodOptions.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodRunner.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ItrSkipTestMethodExecutor.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoExecuteAssemblyCleanupIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyCleanupIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestCategoryAttributeStruct.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoExecuteClassCleanupIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassCleanupIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassInitializeIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodContextStruct.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteTestIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestPropertyAttributeStruct.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestOutcome.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultOutcome.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultStruct.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunCleanupIntegration.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/FailureSite.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs diff --git a/tracer/src/Datadog.Trace/Ci/TestParameters.cs b/tracer/src/Datadog.Trace/Ci/TestParameters.cs index 933deb295b03..afe9a00c9ffa 100644 --- a/tracer/src/Datadog.Trace/Ci/TestParameters.cs +++ b/tracer/src/Datadog.Trace/Ci/TestParameters.cs @@ -18,7 +18,7 @@ public class TestParameters /// Gets or sets the test parameters metadata /// [JsonProperty("metadata")] - public Dictionary? Metadata { get; set; } + public Dictionary? Metadata { get; set; } /// /// Gets or sets the test arguments diff --git a/tracer/src/Datadog.Trace/Ci/TestSession.cs b/tracer/src/Datadog.Trace/Ci/TestSession.cs index 0bcfea550091..46b4ef907ea2 100644 --- a/tracer/src/Datadog.Trace/Ci/TestSession.cs +++ b/tracer/src/Datadog.Trace/Ci/TestSession.cs @@ -537,12 +537,12 @@ private void OnIpcMessageReceived(object message) } } - private Dictionary GetPropagateEnvironmentVariables() + private Dictionary GetPropagateEnvironmentVariables() { var span = _span; var tags = Tags; - var environmentVariables = new Dictionary + var environmentVariables = new Dictionary { [TestSuiteVisibilityTags.TestSessionCommandEnvironmentVariable] = tags.Command, [TestSuiteVisibilityTags.TestSessionWorkingDirectoryEnvironmentVariable] = tags.WorkingDirectory, diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs index 7c898bb3f9c4..85958899f6bb 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AutoInstrumentationExtensions.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Runtime.CompilerServices; @@ -11,22 +12,24 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation internal static class AutoInstrumentationExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void DisposeWithException(this Scope scope, Exception exception) + internal static void DisposeWithException(this Scope? scope, Exception? exception) { - if (scope != null) + if (scope == null) { - try - { - if (exception != null) - { - scope.Span?.SetException(exception); - } - } - finally + return; + } + + try + { + if (exception != null) { - scope.Dispose(); + scope.Span?.SetException(exception); } } + finally + { + scope.Dispose(); + } } } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs index 5ec197ad50cc..50eed1b1e6a5 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs @@ -16,7 +16,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.ManualInstrumentation.Ci [DuckType("Datadog.Trace.Ci.TestParameters", "Datadog.Trace.Manual")] internal interface ITestParameters : IDuckType { - public Dictionary? Metadata { get; } + public Dictionary? Metadata { get; } public Dictionary? Arguments { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/Common.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/Common.cs index 36bd2ffb48e1..1ee9f9fc926f 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/Common.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/Common.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Reflection; @@ -11,190 +12,191 @@ using Datadog.Trace.Logging; using Datadog.Trace.Util; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing; + +internal static class Common { - internal static class Common + internal static readonly IDatadogLogger Log = CIVisibility.Log; + + internal static string GetParametersValueData(object? paramValue) { - internal static readonly IDatadogLogger Log = Ci.CIVisibility.Log; + if (paramValue is null) + { + return "(null)"; + } - internal static string GetParametersValueData(object paramValue) + if (paramValue is string strValue) { - if (paramValue is null) - { - return "(null)"; - } + return strValue; + } - if (paramValue is string strValue) - { - return strValue; - } + if (paramValue is Array pValueArray) + { + const int maxArrayLength = 50; + var length = pValueArray.Length > maxArrayLength ? maxArrayLength : pValueArray.Length; - if (paramValue is Array pValueArray) + var strValueArray = new string[length]; + for (var i = 0; i < length; i++) { - const int maxArrayLength = 50; - int length = pValueArray.Length > maxArrayLength ? maxArrayLength : pValueArray.Length; - - string[] strValueArray = new string[length]; - for (var i = 0; i < length; i++) - { - strValueArray[i] = GetParametersValueData(pValueArray.GetValue(i)); - } - - return "[" + string.Join(", ", strValueArray) + (pValueArray.Length > maxArrayLength ? ", ..." : string.Empty) + "]"; + strValueArray[i] = GetParametersValueData(pValueArray.GetValue(i)); } - if (paramValue is Delegate pValueDelegate) - { - return $"{paramValue}[{pValueDelegate.Target}|{pValueDelegate.Method}]"; - } + return "[" + string.Join(", ", strValueArray) + (pValueArray.Length > maxArrayLength ? ", ..." : string.Empty) + "]"; + } - return paramValue.ToString(); + if (paramValue is Delegate pValueDelegate) + { + return $"{paramValue}[{pValueDelegate.Target}|{pValueDelegate.Method}]"; } - internal static bool ShouldSkip(string testSuite, string testName, object[] testMethodArguments, ParameterInfo[] methodParameters) + return paramValue.ToString() ?? "(null)"; + } + + internal static bool ShouldSkip(string testSuite, string testName, object[]? testMethodArguments, ParameterInfo[]? methodParameters) + { + var currentContext = SynchronizationContext.Current; + try { - var currentContext = SynchronizationContext.Current; - try + SynchronizationContext.SetSynchronizationContext(null); + var skippableTests = AsyncUtil.RunSync(() => CIVisibility.GetSkippableTestsFromSuiteAndNameAsync(testSuite, testName)); + if (skippableTests.Count > 0) { - SynchronizationContext.SetSynchronizationContext(null); - var skippableTests = AsyncUtil.RunSync(() => CIVisibility.GetSkippableTestsFromSuiteAndNameAsync(testSuite, testName)); - if (skippableTests.Count > 0) + foreach (var skippableTest in skippableTests) { - foreach (var skippableTest in skippableTests) + var parameters = skippableTest.GetParameters(); + + // Same test name and no parameters + if ((parameters?.Arguments is null || parameters.Arguments.Count == 0) && + (testMethodArguments is null || testMethodArguments.Length == 0)) { - var parameters = skippableTest.GetParameters(); + return true; + } - // Same test name and no parameters - if ((parameters?.Arguments is null || parameters.Arguments.Count == 0) && - (testMethodArguments is null || testMethodArguments.Length == 0)) + if (parameters?.Arguments is not null && + testMethodArguments is not null && + methodParameters is not null) + { + var matchSignature = true; + for (var i = 0; i < methodParameters.Length; i++) { - return true; - } + var targetValue = "(default)"; + if (i < testMethodArguments.Length) + { + targetValue = GetParametersValueData(testMethodArguments[i]); + } - if (parameters?.Arguments is not null) - { - var matchSignature = true; - for (var i = 0; i < methodParameters.Length; i++) + if (!parameters.Arguments.TryGetValue(methodParameters[i].Name ?? string.Empty, out var argValue)) + { + matchSignature = false; + break; + } + + if (argValue is not string strArgValue) { - var targetValue = "(default)"; - if (i < testMethodArguments.Length) - { - targetValue = GetParametersValueData(testMethodArguments[i]); - } - - if (!parameters.Arguments.TryGetValue(methodParameters[i].Name ?? string.Empty, out var argValue)) - { - matchSignature = false; - break; - } - - if (argValue is not string strArgValue) - { - strArgValue = argValue?.ToString() ?? "(null)"; - } - - if (strArgValue != targetValue) - { - matchSignature = false; - break; - } + strArgValue = argValue?.ToString() ?? "(null)"; } - if (matchSignature) + if (strArgValue != targetValue) { - return true; + matchSignature = false; + break; } } + + if (matchSignature) + { + return true; + } } } } - finally - { - SynchronizationContext.SetSynchronizationContext(currentContext); - } - - return false; } - - internal static int GetNumberOfExecutionsForDuration(TimeSpan duration) + finally { - int numberOfExecutions; - var slowRetriesSettings = CIVisibility.EarlyFlakeDetectionSettings.SlowTestRetries; - if (slowRetriesSettings.FiveSeconds.HasValue && duration.TotalSeconds < 5) - { - numberOfExecutions = slowRetriesSettings.FiveSeconds.Value; - Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 5 seconds.", numberOfExecutions); - } - else if (slowRetriesSettings.TenSeconds.HasValue && duration.TotalSeconds < 10) - { - numberOfExecutions = slowRetriesSettings.TenSeconds.Value; - Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 10 seconds.", numberOfExecutions); - } - else if (slowRetriesSettings.ThirtySeconds.HasValue && duration.TotalSeconds < 30) - { - numberOfExecutions = slowRetriesSettings.ThirtySeconds.Value; - Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 30 seconds.", numberOfExecutions); - } - else if (slowRetriesSettings.FiveMinutes.HasValue && duration.TotalMinutes < 5) - { - numberOfExecutions = slowRetriesSettings.FiveMinutes.Value; - Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 5 minutes.", numberOfExecutions); - } - else - { - numberOfExecutions = 1; - Log.Information("EFD: Number of executions has been set to 1 (No retries). Current test duration is {Value}", duration); - } + SynchronizationContext.SetSynchronizationContext(currentContext); + } + + return false; + } - return numberOfExecutions; + internal static int GetNumberOfExecutionsForDuration(TimeSpan duration) + { + int numberOfExecutions; + var slowRetriesSettings = CIVisibility.EarlyFlakeDetectionSettings.SlowTestRetries; + if (slowRetriesSettings.FiveSeconds.HasValue && duration.TotalSeconds < 5) + { + numberOfExecutions = slowRetriesSettings.FiveSeconds.Value; + Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 5 seconds.", numberOfExecutions); + } + else if (slowRetriesSettings.TenSeconds.HasValue && duration.TotalSeconds < 10) + { + numberOfExecutions = slowRetriesSettings.TenSeconds.Value; + Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 10 seconds.", numberOfExecutions); + } + else if (slowRetriesSettings.ThirtySeconds.HasValue && duration.TotalSeconds < 30) + { + numberOfExecutions = slowRetriesSettings.ThirtySeconds.Value; + Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 30 seconds.", numberOfExecutions); + } + else if (slowRetriesSettings.FiveMinutes.HasValue && duration.TotalMinutes < 5) + { + numberOfExecutions = slowRetriesSettings.FiveMinutes.Value; + Log.Information("EFD: Number of executions has been set to {Value} for this test that runs under 5 minutes.", numberOfExecutions); + } + else + { + numberOfExecutions = 1; + Log.Information("EFD: Number of executions has been set to 1 (No retries). Current test duration is {Value}", duration); } - internal static void SetEarlyFlakeDetectionTestTagsAndAbortReason(Test test, bool isRetry, ref long newTestCases, ref long totalTestCases) + return numberOfExecutions; + } + + internal static void SetEarlyFlakeDetectionTestTagsAndAbortReason(Test test, bool isRetry, ref long newTestCases, ref long totalTestCases) + { + // Early flake detection flags + if (CIVisibility.Settings.EarlyFlakeDetectionEnabled == true) { - // Early flake detection flags - if (CIVisibility.Settings.EarlyFlakeDetectionEnabled == true) + var isTestNew = !CIVisibility.IsAnEarlyFlakeDetectionTest(test.Suite.Module.Name, test.Suite.Name, test.Name ?? string.Empty); + if (isTestNew) { - var isTestNew = !CIVisibility.IsAnEarlyFlakeDetectionTest(test.Suite.Module.Name, test.Suite.Name, test.Name ?? string.Empty); - if (isTestNew) + test.SetTag(EarlyFlakeDetectionTags.TestIsNew, "true"); + if (isRetry) { - test.SetTag(EarlyFlakeDetectionTags.TestIsNew, "true"); - if (isRetry) - { - test.SetTag(EarlyFlakeDetectionTags.TestIsRetry, "true"); - } - else - { - CheckFaultyThreshold(test, Interlocked.Increment(ref newTestCases), Interlocked.Read(ref totalTestCases)); - } + test.SetTag(EarlyFlakeDetectionTags.TestIsRetry, "true"); + } + else + { + CheckFaultyThreshold(test, Interlocked.Increment(ref newTestCases), Interlocked.Read(ref totalTestCases)); } } } + } - internal static void SetFlakyRetryTags(Test test, bool isRetry) + internal static void SetFlakyRetryTags(Test test, bool isRetry) + { + if (CIVisibility.Settings.FlakyRetryEnabled == true && isRetry) { - if (CIVisibility.Settings.FlakyRetryEnabled == true && isRetry) - { - test.SetTag(EarlyFlakeDetectionTags.TestIsRetry, "true"); - } + test.SetTag(EarlyFlakeDetectionTags.TestIsRetry, "true"); } + } - internal static void CheckFaultyThreshold(Test test, long nTestCases, long tTestCases) + internal static void CheckFaultyThreshold(Test test, long nTestCases, long tTestCases) + { + if (tTestCases > 0 && CIVisibility.EarlyFlakeDetectionSettings.FaultySessionThreshold is { } faultySessionThreshold and > 0 and < 100) { - if (tTestCases > 0 && CIVisibility.EarlyFlakeDetectionSettings.FaultySessionThreshold is { } faultySessionThreshold and > 0 and < 100) + if (((double)nTestCases * 100 / (double)tTestCases) > faultySessionThreshold) { - if (((double)nTestCases * 100 / (double)tTestCases) > faultySessionThreshold) - { - /* Spec: - * If the number of new tests goes above a threshold: - * We will stop the feature altogether: no more tests will be considered new and no retries will be done. - */ - - // We need to stop the EFD feature off and set the session as a faulty. - // But session object is not available from the test host - test.SetTag(EarlyFlakeDetectionTags.TestIsNew, (string)null); - test.Suite?.Module?.TrySetSessionTag(EarlyFlakeDetectionTags.AbortReason, "faulty"); - Log.Warning("EFD: The number of new tests goes above the Faulty Session Threshold. Disabling early flake detection for this session. [NewCases={NewCases}/TotalCases={TotalCases} | {FaltyThreshold}%]", nTestCases, tTestCases, faultySessionThreshold); - } + /* Spec: + * If the number of new tests goes above a threshold: + * We will stop the feature altogether: no more tests will be considered new and no retries will be done. + */ + + // We need to stop the EFD feature off and set the session as a faulty. + // But session object is not available from the test host + test.SetTag(EarlyFlakeDetectionTags.TestIsNew, (string?)null); + test.Suite?.Module?.TrySetSessionTag(EarlyFlakeDetectionTags.AbortReason, "faulty"); + Log.Warning("EFD: The number of new tests goes above the Faulty Session Threshold. Disabling early flake detection for this session. [NewCases={NewCases}/TotalCases={TotalCases} | {FaltyThreshold}%]", nTestCases, tTestCases, faultySessionThreshold); } } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestAssemblyInfo.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestAssemblyInfo.cs index be6a88d6af3b..eb373a27816f 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestAssemblyInfo.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestAssemblyInfo.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Reflection; using Datadog.Trace.DuckTyping; @@ -13,5 +14,5 @@ internal interface ITestAssemblyInfo : IDuckType /// /// Gets or sets AssemblyCleanup method for the assembly. /// - MethodInfo AssemblyCleanupMethod { get; set; } + MethodInfo? AssemblyCleanupMethod { get; set; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestClassInfo.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestClassInfo.cs index 7533c8e2e4e2..3ade99aa7fc7 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestClassInfo.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestClassInfo.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Reflection; @@ -14,15 +15,15 @@ internal interface ITestClassInfo : IDuckType /// /// Gets the class type. /// - Type ClassType { get; } + Type? ClassType { get; } /// /// Gets or sets the class cleanup method. /// - public MethodInfo ClassCleanupMethod { get; set; } + MethodInfo? ClassCleanupMethod { get; set; } /// /// Gets the parent . /// - ITestAssemblyInfo Parent { get; } + ITestAssemblyInfo? Parent { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethod.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethod.cs index 40815e93b178..1c9f8b9f6c6d 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethod.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethod.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Reflection; @@ -17,29 +18,29 @@ internal interface ITestMethod : IDuckType /// /// Gets the test method name /// - string TestMethodName { get; } + string? TestMethodName { get; } /// /// Gets the test class name /// - string TestClassName { get; } + string? TestClassName { get; } /// /// Gets the MethodInfo /// - MethodInfo MethodInfo { get; } + MethodInfo? MethodInfo { get; } /// /// Gets the test arguments /// - object[] Arguments { get; } + object[]? Arguments { get; } /// /// Gets all attributes /// /// Injerits all the attributes from base classes /// Attribute array - Attribute[] GetAllAttributes(bool inherit); + Attribute[]? GetAllAttributes(bool inherit); /// /// Invokes the test method. @@ -53,5 +54,5 @@ internal interface ITestMethod : IDuckType /// /// This call handles asynchronous test methods as well. /// - object Invoke(object[] arguments); + object? Invoke(object[]? arguments); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfo.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfo.cs index b2813418e489..25c3c07a7ec0 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfo.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfo.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; @@ -13,10 +14,10 @@ internal interface ITestMethodInfo : ITestMethod /// /// Gets the test method options /// - ITestMethodOptions TestMethodOptions { get; } + ITestMethodOptions? TestMethodOptions { get; } /// /// Gets the parent class Info object. /// - ITestClassInfo Parent { get; } + ITestClassInfo? Parent { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodOptions.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodOptions.cs index a6d3693ae01c..0f85d7ff1d7b 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodOptions.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodOptions.cs @@ -2,10 +2,11 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; internal interface ITestMethodOptions { - object Executor { get; set; } + object? Executor { get; set; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodRunner.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodRunner.cs index 2bb1cf7e746c..7310f4ddb779 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodRunner.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodRunner.cs @@ -2,7 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // - +#nullable enable using Datadog.Trace.DuckTyping; namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; @@ -16,5 +16,5 @@ internal interface ITestMethodRunner /// Gets the TestMethodInfo instance /// [DuckField(Name = "testMethodInfo,_testMethodInfo")] - ITestMethodInfo TestMethodInfo { get; } + ITestMethodInfo? TestMethodInfo { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResult.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResult.cs index b575cfb69801..8246a0ff29a0 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResult.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResult.cs @@ -14,20 +14,20 @@ internal interface ITestResult : IDuckType /// /// Gets or sets the outcome enum /// - public UnitTestOutcome Outcome { get; set; } + UnitTestOutcome Outcome { get; set; } /// /// Gets or sets the test failure exception /// - public Exception? TestFailureException { get; set; } + Exception? TestFailureException { get; set; } /// /// Gets or sets the inner results count of the result. /// - public int InnerResultsCount { get; set; } + int InnerResultsCount { get; set; } /// /// Gets or sets the duration of test execution. /// - public TimeSpan Duration { get; set; } + TimeSpan Duration { get; set; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ItrSkipTestMethodExecutor.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ItrSkipTestMethodExecutor.cs index 25a751c44f39..d183398720e2 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ItrSkipTestMethodExecutor.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ItrSkipTestMethodExecutor.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Reflection; @@ -17,16 +18,19 @@ internal class ItrSkipTestMethodExecutor public ItrSkipTestMethodExecutor(Assembly assembly) { - var testResultType = assembly.GetType("Microsoft.VisualStudio.TestTools.UnitTesting.TestResult", throwOnError: true); + var testResultType = assembly.GetType("Microsoft.VisualStudio.TestTools.UnitTesting.TestResult", throwOnError: true)!; var array = Array.CreateInstance(testResultType, 1); var result = Activator.CreateInstance(testResultType); - var iResult = DuckType.Create(result); - iResult.Outcome = UnitTestOutcome.Inconclusive; // Inconclusive is reported as Skipped in the CLI + if (DuckType.Create(result) is { } iResult) + { + iResult.Outcome = UnitTestOutcome.Inconclusive; // Inconclusive is reported as Skipped in the CLI + } + array.SetValue(result, 0); _arrayInstance = array; } - [DuckReverseMethod(Name = "Execute", ParameterTypeNames = new[] { "Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod" })] + [DuckReverseMethod(Name = "Execute", ParameterTypeNames = ["Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod"])] public object Execute(object testMethod) { if (testMethod.TryDuckCast(out var testMethodInfo)) diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs index f80924f1c17a..13491788b1d0 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Collections.Generic; @@ -25,10 +26,10 @@ internal static class MsTestIntegration internal const IntegrationId IntegrationId = Configuration.IntegrationId.MsTestV2; internal static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(MsTestIntegration)); - internal static readonly ThreadLocal IsTestMethodRunnableThreadLocal = new(); + internal static readonly ThreadLocal IsTestMethodRunnableThreadLocal = new(); - internal static readonly ConditionalWeakTable TestModuleByTestAssemblyInfos = new(); - internal static readonly ConditionalWeakTable TestSuiteByTestClassInfos = new(); + internal static readonly ConditionalWeakTable TestModuleByTestAssemblyInfos = new(); + internal static readonly ConditionalWeakTable TestSuiteByTestClassInfos = new(); private static readonly Dictionary MsTestVersionByModuleId = new() { @@ -66,11 +67,11 @@ internal static class MsTestIntegration internal static bool IsEnabled => CIVisibility.IsRunning && Tracer.Instance.Settings.IsIntegrationEnabled(IntegrationId); - internal static Test OnMethodBegin(TTestMethod testMethodInstance, Type type, bool isRetry, DateTimeOffset? startDate = null) + internal static Test? OnMethodBegin(TTestMethod testMethodInstance, Type type, bool isRetry, DateTimeOffset? startDate = null) where TTestMethod : ITestMethod { var testMethod = testMethodInstance.MethodInfo; - var testName = testMethodInstance.TestMethodName; + var testName = testMethodInstance.TestMethodName ?? string.Empty; var suite = TestSuite.Current; if (suite is null && testMethodInstance.Instance.TryDuckCast(out var testMethodInfo)) @@ -117,13 +118,16 @@ internal static Test OnMethodBegin(TTestMethod testMethodInstance, Common.SetFlakyRetryTags(test, isRetry); // Set test method - test.SetTestMethodInfo(testMethod); + if (testMethod is not null) + { + test.SetTestMethodInfo(testMethod); + } test.ResetStartTime(); return test; } - internal static void UpdateTestParameters(Test test, TTestMethod testMethodInstance, string displayName = null) + internal static void UpdateTestParameters(Test test, TTestMethod testMethodInstance, string? displayName = null) where TTestMethod : ITestMethod { var testMethod = testMethodInstance.MethodInfo; @@ -131,12 +135,12 @@ internal static void UpdateTestParameters(Test test, TTestMethod te var testName = testMethodInstance.TestMethodName; // Get test parameters - var methodParameters = testMethod.GetParameters(); + var methodParameters = testMethod?.GetParameters(); if (methodParameters?.Length > 0) { var testParameters = new TestParameters { - Metadata = new Dictionary(), + Metadata = new Dictionary(), Arguments = new Dictionary() }; @@ -161,9 +165,14 @@ internal static void UpdateTestParameters(Test test, TTestMethod te } } - private static Dictionary> GetTraits(MethodInfo methodInfo) + private static Dictionary>? GetTraits(MethodInfo? methodInfo) { - Dictionary> testProperties = null; + if (methodInfo is null) + { + return null; + } + + Dictionary>? testProperties = null; try { var testAttributes = methodInfo.GetCustomAttributes(true); @@ -177,13 +186,13 @@ private static Dictionary> GetTraits(MethodInfo methodInfo) testProperties ??= new Dictionary>(); if (!testProperties.TryGetValue("Category", out var categoryList)) { - categoryList = new List(); + categoryList = []; testProperties["Category"] = categoryList; } if (tattr.TryDuckCast(out var tattrStruct)) { - categoryList.AddRange(tattrStruct.TestCategories); + categoryList.AddRange(tattrStruct.TestCategories ?? []); } } @@ -194,7 +203,7 @@ private static Dictionary> GetTraits(MethodInfo methodInfo) { if (!testProperties.TryGetValue(tattrStruct.Name, out var propertyList)) { - propertyList = new List(); + propertyList = []; testProperties[tattrStruct.Name] = propertyList; } @@ -214,13 +223,13 @@ private static Dictionary> GetTraits(MethodInfo methodInfo) testProperties ??= new Dictionary>(); if (!testProperties.TryGetValue("Category", out var categoryList)) { - categoryList = new List(); + categoryList = []; testProperties["Category"] = categoryList; } if (tattr.TryDuckCast(out var tattrStruct)) { - categoryList.AddRange(tattrStruct.TestCategories); + categoryList.AddRange(tattrStruct.TestCategories ?? []); } } } @@ -234,7 +243,7 @@ private static Dictionary> GetTraits(MethodInfo methodInfo) return testProperties; } - internal static bool ShouldSkip(TTestMethod testMethodInfo, out bool isUnskippable, out bool isForcedRun, Dictionary> traits = null) + internal static bool ShouldSkip(TTestMethod testMethodInfo, out bool isUnskippable, out bool isForcedRun, Dictionary>? traits = null) where TTestMethod : ITestMethod { isUnskippable = false; @@ -254,10 +263,10 @@ internal static bool ShouldSkip(TTestMethod testMethodInfo, out boo return itrShouldSkip && !isUnskippable; } - internal static TestModule GetOrCreateTestModuleFromTestAssemblyInfo(TAsmInfo testAssemblyInfo, string assemblyName = null) + internal static TestModule? GetOrCreateTestModuleFromTestAssemblyInfo(TAsmInfo? testAssemblyInfo, string? assemblyName = null) where TAsmInfo : ITestAssemblyInfo { - if (testAssemblyInfo.Instance is not { } objTestAssemblyInfo) + if (testAssemblyInfo?.Instance is not { } objTestAssemblyInfo) { return default; } @@ -312,10 +321,10 @@ internal static TestModule GetOrCreateTestModuleFromTestAssemblyInfo(T }); } - internal static TestSuite GetOrCreateTestSuiteFromTestClassInfo(TClassInfo testClassInfo) + internal static TestSuite? GetOrCreateTestSuiteFromTestClassInfo(TClassInfo? testClassInfo) where TClassInfo : ITestClassInfo { - if (testClassInfo.Instance is not { } objTestClassInfo) + if (testClassInfo?.Instance is not { } objTestClassInfo) { return default; } @@ -324,7 +333,7 @@ internal static TestSuite GetOrCreateTestSuiteFromTestClassInfo(TCla objTestClassInfo, key => { - var module = TestModule.Current ?? GetOrCreateTestModuleFromTestAssemblyInfo(testClassInfo.Parent, testClassInfo.ClassType.Assembly.FullName); + var module = TestModule.Current ?? GetOrCreateTestModuleFromTestAssemblyInfo(testClassInfo.Parent, testClassInfo.ClassType?.Assembly.FullName); if (module is null) { Common.Log.Error("There is no current module, a new suite cannot be created."); diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoExecuteAssemblyCleanupIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoExecuteAssemblyCleanupIntegration.cs index 089368c775dc..735b2cb7da32 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoExecuteAssemblyCleanupIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoExecuteAssemblyCleanupIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -50,7 +51,7 @@ internal static CallTargetState OnMethodBegin(TTarget instance) /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception? exception, in CallTargetState state) { if (state.State is TestModule module) { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyCleanupIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyCleanupIntegration.cs index 83a8f63fe51a..15b52436b025 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyCleanupIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyCleanupIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -52,7 +53,7 @@ internal static CallTargetState OnMethodBegin(TTarget instance) /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn? returnValue, Exception? exception, in CallTargetState state) { if (state.State is TestModule module) { @@ -72,6 +73,6 @@ internal static CallTargetReturn OnMethodEnd(TTarget CIVisibility.Close(); } - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs index 6b7b5f6ab49b..9c89d50d04cf 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -30,7 +31,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; [EditorBrowsable(EditorBrowsableState.Never)] public static class TestAssemblyInfoRunAssemblyInitializeIntegration { - private static readonly MethodInfo EmptyCleanUpMethodInfo = typeof(TestAssemblyInfoRunAssemblyInitializeIntegration).GetMethod("EmptyCleanUpMethod", BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo EmptyCleanUpMethodInfo = typeof(TestAssemblyInfoRunAssemblyInitializeIntegration).GetMethod(nameof(EmptyCleanUpMethod), BindingFlags.NonPublic | BindingFlags.Static)!; /// /// OnMethodBegin callback @@ -40,7 +41,7 @@ public static class TestAssemblyInfoRunAssemblyInitializeIntegration /// Instance value, aka `this` of the instrumented method. /// Test context instance /// Calltarget state value - internal static CallTargetState OnMethodBegin(TTarget instance, TContext testContext) + internal static CallTargetState OnMethodBegin(TTarget instance, TContext? testContext) where TTarget : ITestAssemblyInfo { if (!MsTestIntegration.IsEnabled) @@ -69,7 +70,7 @@ internal static CallTargetState OnMethodBegin(TTarget instanc /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception? exception, in CallTargetState state) { if (state.State is TestModule module && exception is not null) { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestCategoryAttributeStruct.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestCategoryAttributeStruct.cs index e7bd269676b4..7230ebfc8a5e 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestCategoryAttributeStruct.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestCategoryAttributeStruct.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Collections.Generic; using Datadog.Trace.DuckTyping; @@ -19,6 +20,6 @@ internal struct TestCategoryAttributeStruct /// /// Gets the test categories /// - public IList TestCategories; + public IList? TestCategories; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoExecuteClassCleanupIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoExecuteClassCleanupIntegration.cs index b253fae7a4a0..e3b037a62b63 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoExecuteClassCleanupIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoExecuteClassCleanupIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -50,7 +51,7 @@ internal static CallTargetState OnMethodBegin(TTarget instance) /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception? exception, in CallTargetState state) { if (state.State is TestSuite suite) { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassCleanupIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassCleanupIntegration.cs index 2c7d8c1cfc31..a41491fe151c 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassCleanupIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassCleanupIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -18,7 +19,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; TypeName = "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo", MethodName = "RunClassCleanup", ReturnTypeName = ClrNames.String, - ParameterTypeNames = new[] { ClrNames.Ignore }, + ParameterTypeNames = [ClrNames.Ignore], MinimumVersion = "14.0.0", MaximumVersion = "14.*.*", IntegrationName = MsTestIntegration.IntegrationName)] @@ -34,7 +35,7 @@ public static class TestClassInfoRunClassCleanupIntegration /// Instance value, aka `this` of the instrumented method. /// Instance of the argument /// Calltarget state value - internal static CallTargetState OnMethodBegin(TTarget instance, TArg arg) + internal static CallTargetState OnMethodBegin(TTarget instance, TArg? arg) where TTarget : ITestClassInfo { if (MsTestIntegration.IsEnabled && MsTestIntegration.GetOrCreateTestSuiteFromTestClassInfo(instance) is { } suite) @@ -55,7 +56,7 @@ internal static CallTargetState OnMethodBegin(TTarget instance, T /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn? returnValue, Exception? exception, in CallTargetState state) { if (state.State is TestSuite suite) { @@ -72,6 +73,6 @@ internal static CallTargetReturn OnMethodEnd(TTarget suite.Close(); } - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassInitializeIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassInitializeIntegration.cs index 8a7f165b1883..be99bda2ec7d 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassInitializeIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestClassInfoRunClassInitializeIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -21,7 +22,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; TypeName = "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo", MethodName = "RunClassInitialize", ReturnTypeName = ClrNames.Void, - ParameterTypeNames = new[] { ClrNames.Ignore }, + ParameterTypeNames = [ClrNames.Ignore], MinimumVersion = "14.0.0", MaximumVersion = "14.*.*", IntegrationName = MsTestIntegration.IntegrationName)] @@ -29,7 +30,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; [EditorBrowsable(EditorBrowsableState.Never)] public static class TestClassInfoRunClassInitializeIntegration { - private static readonly MethodInfo EmptyCleanUpMethodInfo = typeof(TestAssemblyInfoRunAssemblyInitializeIntegration).GetMethod("EmptyCleanUpMethod", BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo EmptyCleanUpMethodInfo = typeof(TestAssemblyInfoRunAssemblyInitializeIntegration).GetMethod(nameof(EmptyCleanUpMethod), BindingFlags.NonPublic | BindingFlags.Static)!; /// /// OnMethodBegin callback @@ -39,7 +40,7 @@ public static class TestClassInfoRunClassInitializeIntegration /// Instance value, aka `this` of the instrumented method. /// Test context instance /// Calltarget state value - internal static CallTargetState OnMethodBegin(TTarget instance, TContext testContext) + internal static CallTargetState OnMethodBegin(TTarget instance, TContext? testContext) where TTarget : ITestClassInfo { if (!MsTestIntegration.IsEnabled || instance.Instance is null) @@ -63,7 +64,7 @@ internal static CallTargetState OnMethodBegin(TTarget instanc /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, Exception? exception, in CallTargetState state) { if (state.State is TestSuite suite && exception is not null) { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs index 5b9b536542cc..a99014fcf164 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.DuckTyping; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodAttributeExecuteIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodAttributeExecuteIntegration.cs index 31ad7450ed38..85ea945a0539 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodAttributeExecuteIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodAttributeExecuteIntegration.cs @@ -79,9 +79,9 @@ internal static CallTargetState OnMethodBegin(TTarget inst /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception? exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn? returnValue, Exception? exception, in CallTargetState state) { - if (state.State is TestRunnerState testMethodState) + if (state.State is TestRunnerState { Test: not null } testMethodState) { var duration = testMethodState.Elapsed; var testMethod = testMethodState.TestMethod; @@ -94,24 +94,27 @@ internal static CallTargetReturn OnMethodEnd(TTarget for (var i = 0; i < returnValueList.Count; i++) { var test = i == 0 ? testMethodState.Test : MsTestIntegration.OnMethodBegin(testMethodState.TestMethod, testMethodState.TestMethod.Type, isRetry: false, testMethodState.Test.StartTime); - if (test.GetTags() is { } testTags) + if (test is not null) { - isTestNew = isTestNew || testTags.EarlyFlakeDetectionTestIsNew == "true"; - if (isTestNew && duration.TotalMinutes >= 5) + if (test.GetTags() is { } testTags) { - testTags.EarlyFlakeDetectionTestAbortReason = "slow"; + isTestNew = isTestNew || testTags.EarlyFlakeDetectionTestIsNew == "true"; + if (isTestNew && duration.TotalMinutes >= 5) + { + testTags.EarlyFlakeDetectionTestAbortReason = "slow"; + } } - } - resultStatus = HandleTestResult(test, testMethod, returnValueList[i], exception); - allowRetries = allowRetries || resultStatus != TestStatus.Skip; + resultStatus = HandleTestResult(test, testMethod, returnValueList[i], exception); + allowRetries = allowRetries || resultStatus != TestStatus.Skip; + } } } else { Common.Log.Warning("Failed to extract TestResult from return value"); testMethodState.Test.Close(TestStatus.Fail); - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } if (isTestNew && allowRetries) @@ -131,7 +134,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget } // Calculate final results - returnValue = (TReturn)GetFinalResults(results); + returnValue = (TReturn?)GetFinalResults(results); } } else if (CIVisibility.Settings.FlakyRetryEnabled == true && resultStatus == TestStatus.Fail) @@ -169,7 +172,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget } } - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); static void RunRetry(ITestMethod testMethod, TestRunnerState testMethodState, List resultsCollection, out bool hasFailed) { @@ -191,7 +194,12 @@ static void RunRetry(ITestMethod testMethod, TestRunnerState testMethodState, Li { for (var j = 0; j < retryTestResultList.Count; j++) { - var ciRetryTest = j == 0 ? retryTest : MsTestIntegration.OnMethodBegin(testMethod, testMethod.Type, isRetry: true, retryTest.StartTime); + var ciRetryTest = j == 0 ? retryTest : MsTestIntegration.OnMethodBegin(testMethod, testMethod.Type, isRetry: true, retryTest?.StartTime); + if (ciRetryTest is null) + { + continue; + } + if (HandleTestResult(ciRetryTest, testMethod, retryTestResultList[j], retryException) == TestStatus.Fail) { hasFailed = true; @@ -202,7 +210,7 @@ static void RunRetry(ITestMethod testMethod, TestRunnerState testMethodState, Li } else { - if (HandleTestResult(retryTest, testMethod, retryTestResult, retryException) == TestStatus.Fail) + if (retryTest is not null && HandleTestResult(retryTest, testMethod, retryTestResult, retryException) == TestStatus.Fail) { hasFailed = true; } @@ -339,10 +347,10 @@ private readonly struct TestRunnerState { private readonly TraceClock _clock; public readonly ITestMethod TestMethod; - public readonly Test Test; + public readonly Test? Test; public readonly DateTimeOffset StartTime; - public TestRunnerState(ITestMethod testMethod, Test test) + public TestRunnerState(ITestMethod testMethod, Test? test) { TestMethod = testMethod; Test = test; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodContextStruct.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodContextStruct.cs index aecc17fce5e7..0b000ec35622 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodContextStruct.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodContextStruct.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.DuckTyping; @@ -12,9 +13,9 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; [DuckCopy] internal struct TestMethodContextStruct { - public string Name; + public string? Name; - public string FullClassName; + public string? FullClassName; - public string AssemblyName; + public string? AssemblyName; } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteIntegration.cs index 843a7c464ffd..15e11a4c43e4 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteIntegration.cs @@ -38,12 +38,12 @@ public static class TestMethodRunnerExecuteIntegration /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn? returnValue, Exception? exception, in CallTargetState state) where TTarget : ITestMethodRunner { if (!MsTestIntegration.IsEnabled) { - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } if (returnValue is IList { Count: > 0 } lstResults) @@ -54,7 +54,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget { if (unitTestResult.Outcome is UnitTestResultOutcome.Inconclusive) { - if (!MsTestIntegration.ShouldSkip(instance.TestMethodInfo, out _, out _)) + if (instance.TestMethodInfo is not null && !MsTestIntegration.ShouldSkip(instance.TestMethodInfo, out _, out _)) { // This instrumentation catches all tests being ignored MsTestIntegration.OnMethodBegin(instance.TestMethodInfo, instance.GetType(), isRetry: false)? @@ -69,7 +69,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget } } - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } [DuckCopy] diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteTestIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteTestIntegration.cs index bf45569a62ef..2e2be4a51f47 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteTestIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestMethodRunnerExecuteTestIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.ComponentModel; using Datadog.Trace.ClrProfiler.CallTarget; @@ -17,7 +18,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; TypeName = "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner", MethodName = "ExecuteTest", ReturnTypeName = "Microsoft.VisualStudio.TestTools.UnitTesting.TestResult[]", - ParameterTypeNames = new[] { "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo" }, + ParameterTypeNames = ["Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo"], MinimumVersion = "14.0.0", MaximumVersion = "14.*.*", IntegrationName = MsTestIntegration.IntegrationName)] @@ -25,7 +26,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; [EditorBrowsable(EditorBrowsableState.Never)] public static class TestMethodRunnerExecuteTestIntegration { - private static ItrSkipTestMethodExecutor _skipTestMethodExecutor; + private static ItrSkipTestMethodExecutor? _skipTestMethodExecutor; /// /// OnMethodBegin callback @@ -44,9 +45,11 @@ internal static CallTargetState OnMethodBegin(TTarget inst { // In order to skip a test we change the Executor to one that returns a valid outcome without calling // the MethodInfo of the test - var executor = instance.TestMethodInfo.TestMethodOptions.Executor; - _skipTestMethodExecutor ??= new ItrSkipTestMethodExecutor(executor.GetType().Assembly); - instance.TestMethodInfo.TestMethodOptions.Executor = DuckType.CreateReverse(executor.GetType(), _skipTestMethodExecutor); + if (instance.TestMethodInfo is { TestMethodOptions: { Executor: { } executor } } testMethodInfo) + { + _skipTestMethodExecutor ??= new ItrSkipTestMethodExecutor(executor.GetType().Assembly); + testMethodInfo.TestMethodOptions.Executor = DuckType.CreateReverse(executor.GetType(), _skipTestMethodExecutor); + } } return CallTargetState.GetDefault(); diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestPropertyAttributeStruct.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestPropertyAttributeStruct.cs index 72de7ff1ba2b..25f730db7c28 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestPropertyAttributeStruct.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestPropertyAttributeStruct.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.DuckTyping; @@ -18,10 +19,10 @@ internal struct TestPropertyAttributeStruct /// /// Gets the name. /// - public string Name; + public string? Name; /// /// Gets the value. /// - public string Value; + public string? Value; } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestOutcome.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestOutcome.cs index 112844d4311f..d96bc7eaa93d 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestOutcome.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestOutcome.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultOutcome.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultOutcome.cs index 0e266950295c..5088ad936991 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultOutcome.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultOutcome.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultStruct.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultStruct.cs index 421dc2bb9468..3e434dd2c6a0 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultStruct.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestResultStruct.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.DuckTyping; @@ -18,12 +19,12 @@ internal struct UnitTestResultStruct /// /// Gets the error message /// - public string ErrorMessage; + public string? ErrorMessage; /// /// Gets the error stacktrace /// - public string ErrorStackTrace; + public string? ErrorStackTrace; /// /// Gets the outcome enum diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunCleanupIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunCleanupIntegration.cs index df4d13aee643..2d000d04abcd 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunCleanupIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunCleanupIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -35,7 +36,7 @@ public static class UnitTestRunnerRunCleanupIntegration /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn? returnValue, Exception? exception, in CallTargetState state) { if (TestModule.Current is { } module) { @@ -45,6 +46,6 @@ internal static CallTargetReturn OnMethodEnd(TTarget CIVisibility.Close(); } - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunSingleTestIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunSingleTestIntegration.cs index 99a12d695ecb..6cf272a427b2 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunSingleTestIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/UnitTestRunnerRunSingleTestIntegration.cs @@ -39,11 +39,11 @@ public static class UnitTestRunnerRunSingleTestIntegration /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A response value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn? returnValue, Exception? exception, in CallTargetState state) { if (instance is null || !MsTestIntegration.IsEnabled) { - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } var methodInfoCacheItem = MsTestIntegration.IsTestMethodRunnableThreadLocal.Value; @@ -73,7 +73,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget if (methodInfoCacheItem.TestMethodInfo.TryDuckCast(out var testMethodInfo)) { // We need to check if the test is failing because a Class initialization error - if (testMethodInfo.Parent.Instance.TryDuckCast(out var classInfoInitializationExceptionStruct)) + if (testMethodInfo.Parent?.Instance.TryDuckCast(out var classInfoInitializationExceptionStruct) == true) { if (classInfoInitializationExceptionStruct.ClassInitializationException is { } classInitializationException && MsTestIntegration.OnMethodBegin(testMethodInfo, instance.GetType(), isRetry: false) is { } test) @@ -88,7 +88,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget } // We need to check if the test is failing because a Class cleanup error - if (testMethodInfo.Parent.Instance.TryDuckCast(out var classInfoCleanupExceptionsStruct)) + if (testMethodInfo.Parent?.Instance.TryDuckCast(out var classInfoCleanupExceptionsStruct) == true) { if (classInfoCleanupExceptionsStruct.ClassCleanupException is { } classCleanupException && MsTestIntegration.GetOrCreateTestSuiteFromTestClassInfo(testMethodInfo.Parent) is { } suite) @@ -102,7 +102,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget } // We need to check if the test is failing because a Assembly initialization error - if (testMethodInfo.Parent.Parent.Instance.TryDuckCast(out var assemblyInfoExceptionsStruct)) + if (testMethodInfo.Parent?.Parent?.Instance.TryDuckCast(out var assemblyInfoExceptionsStruct) == true) { if (assemblyInfoExceptionsStruct.AssemblyInitializationException is { } assemblyInitializationException && MsTestIntegration.OnMethodBegin(testMethodInfo, instance.GetType(), isRetry: false) is { } test) @@ -121,7 +121,7 @@ internal static CallTargetReturn OnMethodEnd(TTarget } } - return new CallTargetReturn(returnValue); + return new CallTargetReturn(returnValue); } [DuckCopy] diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs index 3873b9323199..8e928b0e28f5 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs @@ -238,7 +238,7 @@ internal static void GetExceptionAndMessage(ITestResult result, out string excep if (methodParameters?.Length > 0) { var testParameters = new TestParameters(); - testParameters.Metadata = new Dictionary(); + testParameters.Metadata = new Dictionary(); testParameters.Arguments = new Dictionary(); testParameters.Metadata[TestTags.MetadataTestName] = currentTest.Name ?? string.Empty; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs index f62d1f2ce7d2..cd12a94c3081 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs @@ -42,7 +42,7 @@ internal static class XUnitIntegration if (methodParameters?.Length > 0 && testMethodArguments?.Length > 0) { var testParameters = new TestParameters(); - testParameters.Metadata = new Dictionary(); + testParameters.Metadata = new Dictionary(); testParameters.Arguments = new Dictionary(); testParameters.Metadata[TestTags.MetadataTestName] = runnerInstance.TestCase.DisplayName ?? string.Empty; From 26dc97e4fa9d0b7ea264a6e1b10fc17eacf8908f Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 12:03:07 +0100 Subject: [PATCH 5/9] Adding NUnit integration --- tracer/missing-nullability-files.csv | 14 - .../Ci/Coverage/Models/Tests/FileCoverage.cs | 2 +- .../Testing/NUnit/CIVisibilityTestCommand.cs | 8 +- .../Testing/NUnit/FailureSite.cs | 52 +- .../Testing/NUnit/ICompositeWorkItem.cs | 18 +- .../Testing/NUnit/IMethodInfo.cs | 18 +- .../Testing/NUnit/IPropertyBag.cs | 58 +- .../Testing/NUnit/IResultState.cs | 30 +- .../Testing/NUnit/ITest.cs | 220 ++++---- .../Testing/NUnit/ITestCommand.cs | 4 +- .../Testing/NUnit/ITestExecutionContext.cs | 50 +- .../Testing/NUnit/ITestResult.cs | 113 ++-- .../Testing/NUnit/ITestSuite.cs | 14 +- .../Testing/NUnit/IWorkItem.cs | 98 ++-- ...ommandBuilderMakeTestCommandIntegration.cs | 2 +- .../Testing/NUnit/NUnitIntegration.cs | 529 +++++++++--------- ...impleWorkItemMakeTestCommandIntegration.cs | 3 +- .../NUnitWorkItemPerformWorkIntegration.cs | 5 +- ...UnitWorkItemWorkItemCompleteIntegration.cs | 1 + .../Testing/NUnit/RunState.cs | 56 +- .../Testing/NUnit/TestStatus.cs | 1 + 21 files changed, 640 insertions(+), 656 deletions(-) diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index 37658262d5ef..79d38422e5d9 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -546,7 +546,6 @@ src/Datadog.Trace/Util/Http/QueryStringObfuscation/NullObfuscator.cs src/Datadog.Trace/Util/Http/QueryStringObfuscation/Obfuscator.cs src/Datadog.Trace/Util/Http/QueryStringObfuscation/ObfuscatorBase.cs src/Datadog.Trace/Util/Http/QueryStringObfuscation/ObfuscatorFactory.cs -src/Datadog.Trace/Ci/Coverage/Models/Tests/FileCoverage.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/MySql/MySqlDefinitions.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/Npgsql/NpgsqlDefinitions.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AdoNet/Oracle/OracleDefinitions.cs @@ -652,19 +651,6 @@ src/Datadog.Trace/ClrProfiler/AutoInstrumentation/MongoDb/BsonSerialization/Mong src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Redis/ServiceStack/IRedisNativeClient.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Redis/ServiceStack/RedisNativeClientSendReceiveIntegration.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Redis/ServiceStack/RedisNativeClientSendReceiveIntegration_6_2_0.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/FailureSite.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IPropertyBag.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IResultState.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestExecutionContext.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestSuite.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemPerformWorkIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/RunState.cs -src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/TestStatus.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Azure/Functions/Isolated/FunctionExecutionMiddlewareInvokeIntegration.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Grpc/GrpcLegacy/Client/CachedMetadataHelper.cs src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Grpc/GrpcLegacy/Client/TemporaryHeaders.cs diff --git a/tracer/src/Datadog.Trace/Ci/Coverage/Models/Tests/FileCoverage.cs b/tracer/src/Datadog.Trace/Ci/Coverage/Models/Tests/FileCoverage.cs index a18aafb2bb7f..76628fe9d124 100644 --- a/tracer/src/Datadog.Trace/Ci/Coverage/Models/Tests/FileCoverage.cs +++ b/tracer/src/Datadog.Trace/Ci/Coverage/Models/Tests/FileCoverage.cs @@ -2,7 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // -#nullable enable +#nullable enable using Datadog.Trace.Vendors.Newtonsoft.Json; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/CIVisibilityTestCommand.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/CIVisibilityTestCommand.cs index fd81d69ffe95..45d2e752a6f2 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/CIVisibilityTestCommand.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/CIVisibilityTestCommand.cs @@ -126,20 +126,20 @@ private static void AgregateResults(ITestResult result, ITestResult retryResult) } result.SetResult( - result.ResultState.Status != TestStatus.Passed ? retryResult.ResultState : result.ResultState, + result.ResultState?.Status != TestStatus.Passed ? retryResult.ResultState : result.ResultState, message, stackTrace); if (retryResult.Output is { } testResultOutput && testResultOutput != "\r\n" && testResultOutput != "\n") { - result.OutWriter.WriteLine(testResultOutput); + result.OutWriter?.WriteLine(testResultOutput); } - if (retryResult.AssertionResults.Count > 0) + if (retryResult.AssertionResults?.Count > 0 && result.AssertionResults is { } assertionResults) { foreach (var assertionResult in retryResult.AssertionResults) { - result.AssertionResults.Add(assertionResult); + assertionResults.Add(assertionResult); } } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/FailureSite.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/FailureSite.cs index c67f951f0a2e..2db326f663c1 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/FailureSite.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/FailureSite.cs @@ -2,38 +2,38 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// The FailureSite enum indicates the stage of a test +/// in which an error or failure occurred. +/// +internal enum FailureSite { /// - /// The FailureSite enum indicates the stage of a test - /// in which an error or failure occurred. + /// Failure in the test itself /// - internal enum FailureSite - { - /// - /// Failure in the test itself - /// - Test, + Test, - /// - /// Failure in the SetUp method - /// - SetUp, + /// + /// Failure in the SetUp method + /// + SetUp, - /// - /// Failure in the TearDown method - /// - TearDown, + /// + /// Failure in the TearDown method + /// + TearDown, - /// - /// Failure of a parent test - /// - Parent, + /// + /// Failure of a parent test + /// + Parent, - /// - /// Failure of a child test - /// - Child - } + /// + /// Failure of a child test + /// + Child } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs index fe592f41587c..91d014dc4d94 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs @@ -2,19 +2,19 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Collections; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Internal.Execution.CompositeWorkItem +/// +internal interface ICompositeWorkItem : IWorkItem { /// - /// DuckTyping interface for NUnit.Framework.Internal.Execution.CompositeWorkItem + /// Gets the List of Child WorkItems /// - internal interface ICompositeWorkItem : IWorkItem - { - /// - /// Gets the List of Child WorkItems - /// - IEnumerable Children { get; } - } + IEnumerable Children { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs index 1fdeac974d4e..1f4a474efcd8 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs @@ -2,19 +2,19 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Reflection; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Interfaces.IMethodInfo +/// +internal interface IMethodInfo { /// - /// DuckTyping interface for NUnit.Framework.Interfaces.IMethodInfo + /// Gets the MethodInfo for this method. /// - internal interface IMethodInfo - { - /// - /// Gets the MethodInfo for this method. - /// - MethodInfo MethodInfo { get; } - } + MethodInfo MethodInfo { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IPropertyBag.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IPropertyBag.cs index fa41de32c31e..7d215a2d2763 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IPropertyBag.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IPropertyBag.cs @@ -2,43 +2,43 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Collections; using System.Collections.Generic; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Interfaces.IPropertyBag +/// +internal interface IPropertyBag { /// - /// DuckTyping interface for NUnit.Framework.Interfaces.IPropertyBag + /// Gets a collection containing all the keys in the property set /// - internal interface IPropertyBag - { - /// - /// Gets a collection containing all the keys in the property set - /// - ICollection Keys { get; } + ICollection? Keys { get; } - /// - /// Gets or sets the list of values for a particular key - /// - /// The key for which the values are to be retrieved - IList this[string key] { get; } + /// + /// Gets or sets the list of values for a particular key + /// + /// The key for which the values are to be retrieved + IList? this[string key] { get; } - /// - /// Gets a single value for a key, using the first - /// one if multiple values are present and returning - /// null if the value is not found. - /// - /// the key for which the values are to be retrieved - /// First value of the list for the key; otherwise null. - object Get(string key); + /// + /// Gets a single value for a key, using the first + /// one if multiple values are present and returning + /// null if the value is not found. + /// + /// the key for which the values are to be retrieved + /// First value of the list for the key; otherwise null. + object? Get(string key); - /// - /// Sets the value for a key, removing any other - /// values that are already in the property set. - /// - /// the key for which the values are to be retrieved - /// value for the key - void Set(string key, object value); - } + /// + /// Sets the value for a key, removing any other + /// values that are already in the property set. + /// + /// the key for which the values are to be retrieved + /// value for the key + void Set(string key, object? value); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IResultState.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IResultState.cs index 4bcec4c0df1b..82ac9b96bf2a 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IResultState.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IResultState.cs @@ -2,24 +2,24 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Interfaces.ResultState +/// +internal interface IResultState { /// - /// DuckTyping interface for NUnit.Framework.Interfaces.ResultState + /// Gets the TestStatus for the test. /// - internal interface IResultState - { - /// - /// Gets the TestStatus for the test. - /// - /// The status. - TestStatus Status { get; } + /// The status. + TestStatus Status { get; } - /// - /// Gets the stage of test execution in which - /// the failure or other result took place. - /// - FailureSite Site { get; } - } + /// + /// Gets the stage of test execution in which + /// the failure or other result took place. + /// + FailureSite Site { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs index 8bebb75dec4d..8a38338390e3 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs @@ -2,120 +2,120 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System.Reflection; using Datadog.Trace.DuckTyping; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Internal.Test +/// +internal interface ITest : IDuckType +{ + /// + /// Gets the id of the test + /// + string? Id { get; } + + /// + /// Gets the test name + /// + string? Name { get; } + + /// + /// Gets the type of the test + /// + string? TestType { get; } + + /// + /// Gets the fully qualified name of the test + /// + string FullName { get; } + + /// + /// Gets the name of the class containing this test. Returns + /// null if the test is not associated with a class. + /// + string? ClassName { get; } + + /// + /// Gets the name of the method implementing this test. + /// Returns null if the test is not implemented as a method. + /// + string? MethodName { get; } + + /// + /// Gets the Type of the test fixture, if applicable, or + /// null if no fixture type is associated with this test. + /// + ITypeInfo? TypeInfo { get; } + + /// + /// Gets a MethodInfo for the method implementing this test. + /// Returns null if the test is not implemented as a method. + /// + IMethodInfo Method { get; } + + /// + /// Gets or sets whether or not the test should be run + /// + RunState RunState { get; set; } + + /// + /// Gets the count of the test cases ( 1 if this is a test case ) + /// + int TestCaseCount { get; } + + /// + /// Gets the properties for this test + /// + IPropertyBag Properties { get; } + + /// + /// Gets a value indicating whether if the instance is a TestSuite + /// + bool IsSuite { get; } + + /// + /// Gets a value indicating whether the current test + /// has any descendant tests. + /// + bool HasChildren { get; } + + /// + /// Gets this test's child tests + /// + System.Collections.IList? Tests { get; } + + /// + /// Gets a fixture object for running this test. + /// + object? Fixture { get; } + + /// + /// Gets the arguments to use in creating the test or empty array if none required. + /// + object[]? Arguments { get; } + + /// + /// Gets the parent test, if any. + /// + /// The parent test or null if none exists. + ITest? Parent { get; } + + /// + /// Make test result + /// + /// TestResult instance + ITestResult MakeTestResult(); +} + +internal struct TestAssemblyStruct { /// - /// DuckTyping interface for NUnit.Framework.Internal.Test - /// - internal interface ITest : IDuckType - { - /// - /// Gets the id of the test - /// - string Id { get; } - - /// - /// Gets the test name - /// - string Name { get; } - - /// - /// Gets the type of the test - /// - string TestType { get; } - - /// - /// Gets the fully qualified name of the test - /// - string FullName { get; } - - /// - /// Gets the name of the class containing this test. Returns - /// null if the test is not associated with a class. - /// - string ClassName { get; } - - /// - /// Gets the name of the method implementing this test. - /// Returns null if the test is not implemented as a method. - /// - string MethodName { get; } - - /// - /// Gets the Type of the test fixture, if applicable, or - /// null if no fixture type is associated with this test. - /// - ITypeInfo TypeInfo { get; } - - /// - /// Gets a MethodInfo for the method implementing this test. - /// Returns null if the test is not implemented as a method. - /// - IMethodInfo Method { get; } - - /// - /// Gets or sets whether or not the test should be run - /// - public RunState RunState { get; set; } - - /// - /// Gets the count of the test cases ( 1 if this is a test case ) - /// - int TestCaseCount { get; } - - /// - /// Gets the properties for this test - /// - IPropertyBag Properties { get; } - - /// - /// Gets a value indicating whether if the instance is a TestSuite - /// - bool IsSuite { get; } - - /// - /// Gets a value indicating whether the current test - /// has any descendant tests. - /// - bool HasChildren { get; } - - /// - /// Gets this test's child tests - /// - System.Collections.IList Tests { get; } - - /// - /// Gets a fixture object for running this test. - /// - object Fixture { get; } - - /// - /// Gets the arguments to use in creating the test or empty array if none required. - /// - object[] Arguments { get; } - - /// - /// Gets the parent test, if any. - /// - /// The parent test or null if none exists. - ITest Parent { get; } - - /// - /// Make test result - /// - /// TestResult instance - ITestResult MakeTestResult(); - } - - internal struct TestAssemblyStruct - { - /// - /// Gets the assembly in which the type is declared - /// - public Assembly Assembly; - } + /// Gets the assembly in which the type is declared + /// + public Assembly? Assembly; } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs index 1541ba3ea6e6..a40ea9996e3f 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs @@ -10,7 +10,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; internal interface ITestCommand : IDuckType { - ITest Test { get; } + ITest? Test { get; } - ITestResult Execute(ITestExecutionContext context); + ITestResult Execute(ITestExecutionContext? context); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestExecutionContext.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestExecutionContext.cs index f22479814216..dd746466ea9f 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestExecutionContext.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestExecutionContext.cs @@ -2,40 +2,40 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using Datadog.Trace.DuckTyping; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Internal.TestExecutionContext +/// +internal interface ITestExecutionContext : IDuckType { /// - /// DuckTyping interface for NUnit.Framework.Internal.TestExecutionContext + /// Gets the current test /// - internal interface ITestExecutionContext : IDuckType - { - /// - /// Gets the current test - /// - ITest CurrentTest { get; } + ITest CurrentTest { get; } - /// - /// Gets or sets the current result - /// - ITestResult CurrentResult { get; set; } + /// + /// Gets or sets the current result + /// + ITestResult CurrentResult { get; set; } - /// - /// Gets or sets the current test object - /// - object TestObject { get; set; } - } + /// + /// Gets or sets the current test object + /// + object? TestObject { get; set; } +} +/// +/// DuckTyping interface for NUnit.Framework.Internal.TestExecutionContext with repeat count +/// +internal interface ITestExecutionContextWithRepeatCount : ITestExecutionContext +{ /// - /// DuckTyping interface for NUnit.Framework.Internal.TestExecutionContext with repeat count + /// Gets or sets the current repeat count /// - internal interface ITestExecutionContextWithRepeatCount : ITestExecutionContext - { - /// - /// Gets or sets the current repeat count - /// - int CurrentRepeatCount { get; set; } - } + int CurrentRepeatCount { get; set; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestResult.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestResult.cs index f479332eae78..c702b27b5a1b 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestResult.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestResult.cs @@ -9,75 +9,74 @@ using System.IO; using Datadog.Trace.DuckTyping; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Internal.TestResult +/// +internal interface ITestResult : IDuckType { /// - /// DuckTyping interface for NUnit.Framework.Internal.TestResult + /// Gets the test with which this result is associated. /// - internal interface ITestResult : IDuckType - { - /// - /// Gets the test with which this result is associated. - /// - ITest Test { get; } + ITest Test { get; } - /// - /// Gets the resultstate of the test result. - /// - IResultState ResultState { get; } + /// + /// Gets the resultstate of the test result. + /// + IResultState ResultState { get; } - /// - /// Gets the message associated with a test failure. - /// - string? Message { get; } + /// + /// Gets the message associated with a test failure. + /// + string? Message { get; } - /// - /// Gets any stacktrace associated with an error or failure. - /// - string StackTrace { get; } + /// + /// Gets any stacktrace associated with an error or failure. + /// + string? StackTrace { get; } - /// - /// Gets or sets duration - /// - double Duration { get; set; } + /// + /// Gets or sets duration + /// + double Duration { get; set; } - /// - /// Gets or sets the time the test started running. - /// - DateTime StartTime { get; set; } + /// + /// Gets or sets the time the test started running. + /// + DateTime StartTime { get; set; } - /// - /// Gets or sets the time the test finished running. - /// - DateTime EndTime { get; set; } + /// + /// Gets or sets the time the test finished running. + /// + DateTime EndTime { get; set; } - /// - /// Gets a TextWriter, which will write output to be included in the result. - /// - TextWriter OutWriter { get; } + /// + /// Gets a TextWriter, which will write output to be included in the result. + /// + TextWriter? OutWriter { get; } - /// - /// Gets any text output written to this result. - /// - string Output { get; } + /// + /// Gets any text output written to this result. + /// + string? Output { get; } - /// - /// Gets a list of assertion results associated with the test. - /// - IList AssertionResults { get; } + /// + /// Gets a list of assertion results associated with the test. + /// + IList? AssertionResults { get; } - /// - /// Record an exception for the test result - /// - /// Exception instance - void RecordException(Exception ex); + /// + /// Record an exception for the test result + /// + /// Exception instance + void RecordException(Exception ex); - /// - /// Set the result of the test - /// - /// The ResultState to use in the result - /// A message associated with the result state - /// Stack trace giving the location of the command - void SetResult(IResultState resultState, string? message, string? stackTrace); - } + /// + /// Set the result of the test + /// + /// The ResultState to use in the result + /// A message associated with the result state + /// Stack trace giving the location of the command + void SetResult(IResultState resultState, string? message, string? stackTrace); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestSuite.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestSuite.cs index ed5c3db492f8..5e85d999589c 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestSuite.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestSuite.cs @@ -2,15 +2,13 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable -using System.Collections; +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +/// +/// DuckTyping interface for NUnit.Framework.Internal.TestSuite +/// +internal interface ITestSuite : ITest { - /// - /// DuckTyping interface for NUnit.Framework.Internal.TestSuite - /// - internal interface ITestSuite : ITest - { - } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs index 4df4ce9c20dd..d440265927b7 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs @@ -2,61 +2,61 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.Reflection; using Datadog.Trace.DuckTyping; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// DuckTyping interface for NUnit.Framework.Internal.Execution.WorkItem +/// +internal interface IWorkItem : IDuckType +{ + /// + /// Gets the test being executed by the work item + /// + ITest Test { get; } + + /// + /// Gets the test result + /// + ITestResult Result { get; } +} + +internal interface INUnitTestAssemblyRunner +{ + ITest LoadedTest { get; } + + IWorkItem TopLevelWorkItem { get; } +} + +internal interface ITypeInfo { /// - /// DuckTyping interface for NUnit.Framework.Internal.Execution.WorkItem + /// Gets the underlying Type on which this ITypeInfo is based + /// + Type Type { get; } + + /// + /// Gets the name of the Type + /// + string? Name { get; } + + /// + /// Gets the full name of the Type + /// + string? FullName { get; } + + /// + /// Gets the assembly in which the type is declared + /// + Assembly? Assembly { get; } + + /// + /// Gets the namespace of the Type /// - internal interface IWorkItem : IDuckType - { - /// - /// Gets the test being executed by the work item - /// - ITest Test { get; } - - /// - /// Gets the test result - /// - ITestResult Result { get; } - } - - internal interface INUnitTestAssemblyRunner - { - ITest LoadedTest { get; } - - IWorkItem TopLevelWorkItem { get; } - } - - internal interface ITypeInfo - { - /// - /// Gets the underlying Type on which this ITypeInfo is based - /// - Type Type { get; } - - /// - /// Gets the name of the Type - /// - string Name { get; } - - /// - /// Gets the full name of the Type - /// - string FullName { get; } - - /// - /// Gets the assembly in which the type is declared - /// - Assembly Assembly { get; } - - /// - /// Gets the namespace of the Type - /// - string Namespace { get; } - } + string? Namespace { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitCommandBuilderMakeTestCommandIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitCommandBuilderMakeTestCommandIntegration.cs index 588810da6f8b..690faac4333f 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitCommandBuilderMakeTestCommandIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitCommandBuilderMakeTestCommandIntegration.cs @@ -44,7 +44,7 @@ public class NUnitCommandBuilderMakeTestCommandIntegration /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A return value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TReturn returnValue, Exception? exception, in CallTargetState state) { return new CallTargetReturn(NUnitIntegration.WrapWithRetryCommand(returnValue)); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs index 8e928b0e28f5..1075cccd4145 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs @@ -15,366 +15,365 @@ using Datadog.Trace.DuckTyping; using Datadog.Trace.Logging; -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +internal static class NUnitIntegration { - internal static class NUnitIntegration - { - internal const string TestModuleConst = "Assembly"; - internal const string TestSuiteConst = "TestFixture"; + internal const string TestModuleConst = "Assembly"; + internal const string TestSuiteConst = "TestFixture"; - private static readonly ConditionalWeakTable ModulesItems = new(); - private static readonly ConditionalWeakTable SuiteItems = new(); - private static readonly Dictionary> Tests = new(); + private static readonly ConditionalWeakTable ModulesItems = new(); + private static readonly ConditionalWeakTable SuiteItems = new(); + private static readonly Dictionary> Tests = new(); - internal const string IntegrationName = nameof(Configuration.IntegrationId.NUnit); - internal const IntegrationId IntegrationId = Configuration.IntegrationId.NUnit; - internal const string SkipReasonKey = "_SKIPREASON"; - internal static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(NUnitIntegration)); + internal const string IntegrationName = nameof(Configuration.IntegrationId.NUnit); + internal const IntegrationId IntegrationId = Configuration.IntegrationId.NUnit; + internal const string SkipReasonKey = "_SKIPREASON"; + internal static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(NUnitIntegration)); - private static long _totalTestCases; - private static long _newTestCases; + private static long _totalTestCases; + private static long _newTestCases; - internal static bool IsEnabled => CIVisibility.IsRunning && Tracer.Instance.Settings.IsIntegrationEnabled(IntegrationId); + internal static bool IsEnabled => CIVisibility.IsRunning && Tracer.Instance.Settings.IsIntegrationEnabled(IntegrationId); - internal static Test? GetOrCreateTest(ITest currentTest, int repeatCount = 0) + internal static Test? GetOrCreateTest(ITest currentTest, int repeatCount = 0) + { + var key = $"{currentTest.Id}|{repeatCount}"; + lock (Tests) { - var key = $"{currentTest.Id}|{repeatCount}"; - lock (Tests) + if (Tests.TryGetValue(key, out var testReference) && testReference.TryGetTarget(out var test)) { - if (Tests.TryGetValue(key, out var testReference) && testReference.TryGetTarget(out var test)) - { - return test; - } - - test = InternalCreateTest(currentTest, repeatCount > 0); - if (test is not null) - { - Tests[key] = new WeakReference(test); - } - return test; } - } - internal static void FinishTest(Test test, ITestResult testResult) - { - GetExceptionAndMessage(testResult, out var exceptionType, out var resultMessage); - switch (testResult.ResultState.Status) + test = InternalCreateTest(currentTest, repeatCount > 0); + if (test is not null) { - case TestStatus.Skipped or TestStatus.Inconclusive: - test.Close(Ci.TestStatus.Skip, TimeSpan.Zero, resultMessage); - break; - case TestStatus.Failed: - test.SetErrorInfo(exceptionType, resultMessage, testResult.StackTrace); - test.Close(Ci.TestStatus.Fail); - break; - default: - if (!string.IsNullOrEmpty(resultMessage)) - { - test.SetTag(TestTags.Message, resultMessage); - } - - test.Close(Ci.TestStatus.Pass); - break; + Tests[key] = new WeakReference(test); } + + return test; } + } - internal static TTestCommand WrapWithRetryCommand(TTestCommand testCommand) + internal static void FinishTest(Test test, ITestResult testResult) + { + GetExceptionAndMessage(testResult, out var exceptionType, out var resultMessage); + switch (testResult.ResultState.Status) { - if (testCommand.TryDuckCast(out var duckTypedTestCommand)) - { - var retryTestCommand = new CIVisibilityTestCommand(duckTypedTestCommand); - return (TTestCommand)retryTestCommand.DuckImplement(typeof(TTestCommand)); - } + case TestStatus.Skipped or TestStatus.Inconclusive: + test.Close(Ci.TestStatus.Skip, TimeSpan.Zero, resultMessage); + break; + case TestStatus.Failed: + test.SetErrorInfo(exceptionType, resultMessage, testResult.StackTrace); + test.Close(Ci.TestStatus.Fail); + break; + default: + if (!string.IsNullOrEmpty(resultMessage)) + { + test.SetTag(TestTags.Message, resultMessage); + } - return testCommand; + test.Close(Ci.TestStatus.Pass); + break; } + } - internal static TestModule? GetTestModuleFrom(ITest? test) + internal static TTestCommand WrapWithRetryCommand(TTestCommand testCommand) + { + if (testCommand.TryDuckCast(out var duckTypedTestCommand)) { - if (test is null) - { - return null; - } - - if (test.TestType != TestModuleConst) - { - test = GetParentWithTestType(test, TestModuleConst); - } + var retryTestCommand = new CIVisibilityTestCommand(duckTypedTestCommand); + return (TTestCommand)retryTestCommand.DuckImplement(typeof(TTestCommand)); + } - if (test is not null && - ModulesItems.TryGetValue(test.Instance!, out var moduleObject) && - moduleObject is TestModule module) - { - return module; - } + return testCommand; + } + internal static TestModule? GetTestModuleFrom(ITest? test) + { + if (test is null) + { return null; } - internal static void SetTestModuleTo(ITest test, TestModule module) + if (test.TestType != TestModuleConst) { - if (test.TestType == TestModuleConst) - { - ModulesItems.Add(test.Instance!, module); - } - else if (GetParentWithTestType(test, TestModuleConst) is { } assemblyITest) - { - ModulesItems.Add(assemblyITest.Instance!, module); - } + test = GetParentWithTestType(test, TestModuleConst); } - internal static TestSuite? GetTestSuiteFrom(ITest? test) + if (test is not null && + ModulesItems.TryGetValue(test.Instance!, out var moduleObject) && + moduleObject is TestModule module) { - if (test is null) - { - return null; - } + return module; + } - if (test.TestType != TestSuiteConst) - { - test = GetParentWithTestType(test, TestSuiteConst); - } + return null; + } - if (test is not null && - SuiteItems.TryGetValue(test.Instance!, out var suiteObject) && - suiteObject is TestSuite suite) - { - return suite; - } + internal static void SetTestModuleTo(ITest test, TestModule module) + { + if (test.TestType == TestModuleConst) + { + ModulesItems.Add(test.Instance!, module); + } + else if (GetParentWithTestType(test, TestModuleConst) is { } assemblyITest) + { + ModulesItems.Add(assemblyITest.Instance!, module); + } + } + internal static TestSuite? GetTestSuiteFrom(ITest? test) + { + if (test is null) + { return null; } - internal static void SetTestSuiteTo(ITest test, TestSuite suite) + if (test.TestType != TestSuiteConst) { - if (test.TestType == TestSuiteConst) - { - SuiteItems.Add(test.Instance!, suite); - } - else if (GetParentWithTestType(test, TestSuiteConst) is { } suiteITest) - { - SuiteItems.Add(suiteITest.Instance!, suite); - } + test = GetParentWithTestType(test, TestSuiteConst); } - internal static bool ShouldSkip(ITest currentTest, out bool isUnskippable, out bool isForcedRun, Dictionary>? traits = null) + if (test is not null && + SuiteItems.TryGetValue(test.Instance!, out var suiteObject) && + suiteObject is TestSuite suite) { - isUnskippable = false; - isForcedRun = false; - - if (CIVisibility.Settings.IntelligentTestRunnerEnabled != true) - { - return false; - } + return suite; + } - var testMethod = currentTest.Method.MethodInfo; - var testSuite = testMethod.DeclaringType?.FullName ?? string.Empty; - var itrShouldSkip = Common.ShouldSkip(testSuite, testMethod.Name, currentTest.Arguments, testMethod.GetParameters()); - if (traits is null) - { - ExtractTraits(currentTest, ref traits); - } + return null; + } - isUnskippable = traits?.TryGetValue(IntelligentTestRunnerTags.UnskippableTraitName, out _) == true; - isForcedRun = itrShouldSkip && isUnskippable; - return itrShouldSkip && !isUnskippable; + internal static void SetTestSuiteTo(ITest test, TestSuite suite) + { + if (test.TestType == TestSuiteConst) + { + SuiteItems.Add(test.Instance!, suite); } - - internal static void GetExceptionAndMessage(ITestResult result, out string exceptionType, out string resultMessage) + else if (GetParentWithTestType(test, TestSuiteConst) is { } suiteITest) { - exceptionType = result.ResultState.Site switch - { - FailureSite.Child => "ChildException", - FailureSite.Parent => "ParentException", - FailureSite.Test => "TestException", - FailureSite.SetUp => "SetUpException", - FailureSite.TearDown => "TearDownException", - _ => string.Empty - }; - - resultMessage = result.Message ?? string.Empty; - while (true) - { - // Formatted result messages in NUnit contains the exception type and the type, but also can contain the origin so we can end up having something like: - // SetUpException: System.Exception: Exception of type 'System.Exception' was thrown. - // The goal of this algorithm is to extract the exception type and the message from the formatted message. - var resultSplittedMessage = resultMessage.Split(':'); - var tmpExType = resultSplittedMessage[0].Trim(); + SuiteItems.Add(suiteITest.Instance!, suite); + } + } - if (resultSplittedMessage.Length < 2 || string.IsNullOrWhiteSpace(tmpExType)) - { - Common.Log.Debug("Exception type: {ExceptionType}, Message: {ResultMessage}", exceptionType, resultMessage); - break; - } + internal static bool ShouldSkip(ITest currentTest, out bool isUnskippable, out bool isForcedRun, Dictionary>? traits = null) + { + isUnskippable = false; + isForcedRun = false; - resultMessage = string.Join(":", resultSplittedMessage.Skip(1)).Trim(); - exceptionType = tmpExType; - } + if (CIVisibility.Settings.IntelligentTestRunnerEnabled != true) + { + return false; } - private static Test? InternalCreateTest(ITest currentTest, bool isRetry) + var testMethod = currentTest.Method.MethodInfo; + var testSuite = testMethod.DeclaringType?.FullName ?? string.Empty; + var itrShouldSkip = Common.ShouldSkip(testSuite, testMethod.Name, currentTest.Arguments, testMethod.GetParameters()); + if (traits is null) { - var testMethod = currentTest.Method?.MethodInfo; - var testMethodArguments = currentTest.Arguments; - var testMethodProperties = currentTest.Properties; + ExtractTraits(currentTest, ref traits); + } - if (testMethod == null) - { - Log.Warning("Test method cannot be found. ITest.Method(IMethodInfo).MethodInfo is null."); - return null; - } + isUnskippable = traits?.TryGetValue(IntelligentTestRunnerTags.UnskippableTraitName, out _) == true; + isForcedRun = itrShouldSkip && isUnskippable; + return itrShouldSkip && !isUnskippable; + } - if (GetTestSuiteFrom(currentTest) is not { } suite) + internal static void GetExceptionAndMessage(ITestResult result, out string exceptionType, out string resultMessage) + { + exceptionType = result.ResultState.Site switch + { + FailureSite.Child => "ChildException", + FailureSite.Parent => "ParentException", + FailureSite.Test => "TestException", + FailureSite.SetUp => "SetUpException", + FailureSite.TearDown => "TearDownException", + _ => string.Empty + }; + + resultMessage = result.Message ?? string.Empty; + while (true) + { + // Formatted result messages in NUnit contains the exception type and the type, but also can contain the origin so we can end up having something like: + // SetUpException: System.Exception: Exception of type 'System.Exception' was thrown. + // The goal of this algorithm is to extract the exception type and the message from the formatted message. + var resultSplittedMessage = resultMessage.Split(':'); + var tmpExType = resultSplittedMessage[0].Trim(); + + if (resultSplittedMessage.Length < 2 || string.IsNullOrWhiteSpace(tmpExType)) { - return null; + Common.Log.Debug("Exception type: {ExceptionType}, Message: {ResultMessage}", exceptionType, resultMessage); + break; } - var test = suite.InternalCreateTest(testMethod.Name); - string? skipReason = null; + resultMessage = string.Join(":", resultSplittedMessage.Skip(1)).Trim(); + exceptionType = tmpExType; + } + } - // Get test parameters - var methodParameters = testMethod.GetParameters(); - if (methodParameters?.Length > 0) - { - var testParameters = new TestParameters(); - testParameters.Metadata = new Dictionary(); - testParameters.Arguments = new Dictionary(); - testParameters.Metadata[TestTags.MetadataTestName] = currentTest.Name ?? string.Empty; + private static Test? InternalCreateTest(ITest currentTest, bool isRetry) + { + var testMethod = currentTest.Method?.MethodInfo; + var testMethodArguments = currentTest.Arguments; + var testMethodProperties = currentTest.Properties; - for (int i = 0; i < methodParameters.Length; i++) - { - var key = methodParameters[i].Name ?? string.Empty; - if (testMethodArguments != null && i < testMethodArguments.Length) - { - testParameters.Arguments[key] = Common.GetParametersValueData(testMethodArguments[i]); - } - else - { - testParameters.Arguments[key] = "(default)"; - } - } + if (testMethod == null) + { + Log.Warning("Test method cannot be found. ITest.Method(IMethodInfo).MethodInfo is null."); + return null; + } - test.SetParameters(testParameters); - } + if (GetTestSuiteFrom(currentTest) is not { } suite) + { + return null; + } - // Get traits - Dictionary>? traits = null; - if (testMethodProperties != null) - { - skipReason = (string)testMethodProperties.Get(SkipReasonKey); - ExtractTraits(currentTest, ref traits); - } + var test = suite.InternalCreateTest(testMethod.Name); + string? skipReason = null; + + // Get test parameters + var methodParameters = testMethod.GetParameters(); + if (methodParameters?.Length > 0) + { + var testParameters = new TestParameters(); + testParameters.Metadata = new Dictionary(); + testParameters.Arguments = new Dictionary(); + testParameters.Metadata[TestTags.MetadataTestName] = currentTest.Name ?? string.Empty; - if (traits?.Count > 0) + for (int i = 0; i < methodParameters.Length; i++) { - // Unskippable test - if (CIVisibility.Settings.IntelligentTestRunnerEnabled) + var key = methodParameters[i].Name ?? string.Empty; + if (testMethodArguments != null && i < testMethodArguments.Length) { - ShouldSkip(currentTest, out var isUnskippable, out var isForcedRun, traits); - test.SetTag(IntelligentTestRunnerTags.UnskippableTag, isUnskippable ? "true" : "false"); - test.SetTag(IntelligentTestRunnerTags.ForcedRunTag, isForcedRun ? "true" : "false"); - traits.Remove(IntelligentTestRunnerTags.UnskippableTraitName); + testParameters.Arguments[key] = Common.GetParametersValueData(testMethodArguments[i]); } - - test.SetTraits(traits); - } - else - { - // Unskippable test - if (CIVisibility.Settings.IntelligentTestRunnerEnabled) + else { - test.SetTag(IntelligentTestRunnerTags.UnskippableTag, "false"); - test.SetTag(IntelligentTestRunnerTags.ForcedRunTag, "false"); + testParameters.Arguments[key] = "(default)"; } } - // Early flake detection flags - Common.SetEarlyFlakeDetectionTestTagsAndAbortReason(test, isRetry, ref _newTestCases, ref _totalTestCases); - - // Flaky Retry - Common.SetFlakyRetryTags(test, isRetry); + test.SetParameters(testParameters); + } - // Test code and code owners - test.SetTestMethodInfo(testMethod); + // Get traits + Dictionary>? traits = null; + if (testMethodProperties != null) + { + skipReason = (string?)testMethodProperties.Get(SkipReasonKey); + ExtractTraits(currentTest, ref traits); + } - // Telemetry - Tracer.Instance.TracerManager.Telemetry.IntegrationGeneratedSpan(IntegrationId); + if (traits?.Count > 0) + { + // Unskippable test + if (CIVisibility.Settings.IntelligentTestRunnerEnabled) + { + ShouldSkip(currentTest, out var isUnskippable, out var isForcedRun, traits); + test.SetTag(IntelligentTestRunnerTags.UnskippableTag, isUnskippable ? "true" : "false"); + test.SetTag(IntelligentTestRunnerTags.ForcedRunTag, isForcedRun ? "true" : "false"); + traits.Remove(IntelligentTestRunnerTags.UnskippableTraitName); + } - // Skip tests - if (skipReason is not null) + test.SetTraits(traits); + } + else + { + // Unskippable test + if (CIVisibility.Settings.IntelligentTestRunnerEnabled) { - test.Close(Ci.TestStatus.Skip, skipReason: skipReason, duration: TimeSpan.Zero); - return test; + test.SetTag(IntelligentTestRunnerTags.UnskippableTag, "false"); + test.SetTag(IntelligentTestRunnerTags.ForcedRunTag, "false"); } + } - test.ResetStartTime(); + // Early flake detection flags + Common.SetEarlyFlakeDetectionTestTagsAndAbortReason(test, isRetry, ref _newTestCases, ref _totalTestCases); + + // Flaky Retry + Common.SetFlakyRetryTags(test, isRetry); + + // Test code and code owners + test.SetTestMethodInfo(testMethod); + + // Telemetry + Tracer.Instance.TracerManager.Telemetry.IntegrationGeneratedSpan(IntegrationId); + + // Skip tests + if (skipReason is not null) + { + test.Close(Ci.TestStatus.Skip, skipReason: skipReason, duration: TimeSpan.Zero); return test; } - private static void ExtractTraits(ITest currentTest, ref Dictionary>? traits) + test.ResetStartTime(); + return test; + } + + private static void ExtractTraits(ITest currentTest, ref Dictionary>? traits) + { + if (currentTest?.Instance is null) { - if (currentTest?.Instance is null) - { - return; - } + return; + } - if (currentTest.Parent is { Instance: { } }) - { - ExtractTraits(currentTest.Parent, ref traits); - } + if (currentTest.Parent is { Instance: { } }) + { + ExtractTraits(currentTest.Parent, ref traits); + } - if (currentTest.Properties is { } properties) + if (currentTest.Properties is { } properties) + { + var keys = properties.Keys; + if (keys?.Count > 0) { - var keys = properties.Keys; - if (keys?.Count > 0) + foreach (var key in keys) { - foreach (var key in keys) + if (key is SkipReasonKey or "_APPDOMAIN" or "_JOINTYPE" or "_PID" or "_PROVIDERSTACKTRACE") + { + continue; + } + + var value = properties[key]; + if (value is not null) { - if (key is SkipReasonKey or "_APPDOMAIN" or "_JOINTYPE" or "_PID" or "_PROVIDERSTACKTRACE") + traits ??= new(); + if (!traits.TryGetValue(key, out var lstValues)) { - continue; + lstValues = new List(); + traits[key] = lstValues; } - var value = properties[key]; - if (value is not null) + foreach (var valObj in value) { - traits ??= new(); - if (!traits.TryGetValue(key, out var lstValues)) + if (valObj is null) { - lstValues = new List(); - traits[key] = lstValues; + continue; } - foreach (var valObj in value) - { - if (valObj is null) - { - continue; - } - - lstValues.Add(valObj.ToString() ?? string.Empty); - } + lstValues.Add(valObj.ToString() ?? string.Empty); } } } } } + } - private static ITest? GetParentWithTestType(ITest test, string testType) + private static ITest? GetParentWithTestType(ITest test, string testType) + { + var parent = test?.Parent; + if (parent?.Instance is null) { - var parent = test?.Parent; - if (parent?.Instance is null) - { - return null; - } - - return parent.TestType == testType ? parent : GetParentWithTestType(parent, testType); + return null; } - internal static void IncrementTotalTestCases() - { - Interlocked.Increment(ref _totalTestCases); - } + return parent.TestType == testType ? parent : GetParentWithTestType(parent, testType); + } + + internal static void IncrementTotalTestCases() + { + Interlocked.Increment(ref _totalTestCases); } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitSimpleWorkItemMakeTestCommandIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitSimpleWorkItemMakeTestCommandIntegration.cs index 46ff41a2c559..c5f8e8731a2c 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitSimpleWorkItemMakeTestCommandIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitSimpleWorkItemMakeTestCommandIntegration.cs @@ -7,7 +7,6 @@ using System; using System.ComponentModel; using Datadog.Trace.ClrProfiler.CallTarget; -using Datadog.Trace.DuckTyping; namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; @@ -37,7 +36,7 @@ public class NUnitSimpleWorkItemMakeTestCommandIntegration /// Exception instance in case the original code threw an exception. /// Calltarget state value /// A return value, in an async scenario will be T of Task of T - internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) + internal static CallTargetReturn OnMethodEnd(TTarget instance, TReturn returnValue, Exception? exception, in CallTargetState state) { return new CallTargetReturn(NUnitIntegration.WrapWithRetryCommand(returnValue)); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemPerformWorkIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemPerformWorkIntegration.cs index 47d5abc41f68..99d6e541032e 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemPerformWorkIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemPerformWorkIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; @@ -62,8 +63,8 @@ internal static CallTargetState OnMethodBegin(TTarget instance) case "TestMethod": if (NUnitIntegration.ShouldSkip(item, out _, out _)) { - var testMethod = item.Method.MethodInfo; - Common.Log.Debug("ITR: Test skipped: {Class}.{Name}", testMethod.DeclaringType?.FullName, testMethod.Name); + var testMethod = item.Method?.MethodInfo; + Common.Log.Debug("ITR: Test skipped: {Class}.{Name}", testMethod?.DeclaringType?.FullName, testMethod?.Name); item.RunState = RunState.Ignored; item.Properties.Set(NUnitIntegration.SkipReasonKey, IntelligentTestRunnerTags.SkippedByReason); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs index 79ea1f00e7ab..3da08a4afe1a 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable using System; using System.ComponentModel; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/RunState.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/RunState.cs index 2df71bfc59f2..8bf299a7c5c8 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/RunState.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/RunState.cs @@ -2,40 +2,40 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable -namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit; + +/// +/// The RunState enum indicates whether a test can be executed. +/// +internal enum RunState { /// - /// The RunState enum indicates whether a test can be executed. + /// The test is not runnable. /// - internal enum RunState - { - /// - /// The test is not runnable. - /// - NotRunnable, + NotRunnable, - /// - /// The test is runnable. - /// - Runnable, + /// + /// The test is runnable. + /// + Runnable, - /// - /// The test can only be run explicitly - /// - Explicit, + /// + /// The test can only be run explicitly + /// + Explicit, - /// - /// The test has been skipped. This value may - /// appear on a Test when certain attributes - /// are used to skip the test. - /// - Skipped, + /// + /// The test has been skipped. This value may + /// appear on a Test when certain attributes + /// are used to skip the test. + /// + Skipped, - /// - /// The test has been ignored. May appear on - /// a Test, when the IgnoreAttribute is used. - /// - Ignored - } + /// + /// The test has been ignored. May appear on + /// a Test, when the IgnoreAttribute is used. + /// + Ignored } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/TestStatus.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/TestStatus.cs index 6581a6aeddd3..b89ec863582d 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/TestStatus.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/TestStatus.cs @@ -2,6 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +#nullable enable namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.NUnit { From 3bc2d511c4c5dc23bea4a05e20a25c0e86efe5ac Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 13:26:10 +0100 Subject: [PATCH 6/9] fix --- tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs b/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs index 9d9e34028854..184f6135ba12 100644 --- a/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs +++ b/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs @@ -148,7 +148,7 @@ public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) if (benchmarkCase.HasParameters) { - var testParameters = new TestParameters { Arguments = new Dictionary(), Metadata = new Dictionary() }; + var testParameters = new TestParameters { Arguments = new Dictionary(), Metadata = new Dictionary() }; foreach (var parameter in benchmarkCase.Parameters.Items) { var parameterValue = ClrProfiler.AutoInstrumentation.Testing.Common.GetParametersValueData(parameter.Value); From b280769036fcff75b92bc8144b643ed4ae7a0775 Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 14:39:12 +0100 Subject: [PATCH 7/9] fix --- ...icApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt b/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt index b564d5846983..06f0fc2e282f 100644 --- a/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt +++ b/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt @@ -103,7 +103,7 @@ namespace Datadog.Trace.Ci { public TestParameters() { } public System.Collections.Generic.Dictionary? Arguments { get; set; } - public System.Collections.Generic.Dictionary? Metadata { get; set; } + public System.Collections.Generic.Dictionary? Metadata { get; set; } } public sealed class TestSession { From 3d98a45a5bebdc0c7de9990b062d3e8eb506713d Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Fri, 29 Nov 2024 17:25:38 +0100 Subject: [PATCH 8/9] changes from the review --- .../DatadogExporter.cs | 2 +- .../MessagePack/EventMessagePackFormatter.cs | 2 +- .../Ci/Agent/Payloads/EventPlatformPayload.cs | 7 ++++--- .../Datadog.Trace/Ci/CITracerManagerFactory.cs | 4 ++-- .../Coverage/Exceptions/PdbNotFoundException.cs | 2 ++ .../Ci/Processors/OriginTagTraceProcessor.cs | 7 ++++++- .../Processors/TestSuiteVisibilityProcessor.cs | 2 +- tracer/src/Datadog.Trace/Ci/TestParameters.cs | 2 +- .../Ci/Proxies/ITestParameters.cs | 2 +- .../Testing/MsTestV2/MsTestIntegration.cs | 6 +++--- ...emblyInfoRunAssemblyInitializeIntegration.cs | 2 +- .../Testing/MsTestV2/TestContextStruct.cs | 2 +- .../Testing/NUnit/ICompositeWorkItem.cs | 2 +- .../Testing/NUnit/IMethodInfo.cs | 2 +- .../AutoInstrumentation/Testing/NUnit/ITest.cs | 2 +- .../Testing/NUnit/ITestCommand.cs | 2 +- .../Testing/NUnit/IWorkItem.cs | 4 ++-- .../Testing/NUnit/NUnitIntegration.cs | 17 ++++++++++++----- .../NUnitWorkItemWorkItemCompleteIntegration.cs | 7 ++++--- .../Testing/XUnit/XUnitIntegration.cs | 8 +++++--- .../Datadog.Trace/Processors/ITraceProcessor.cs | 2 +- 21 files changed, 52 insertions(+), 34 deletions(-) diff --git a/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs b/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs index 184f6135ba12..cc31e533091f 100644 --- a/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs +++ b/tracer/src/Datadog.Trace.BenchmarkDotNet/DatadogExporter.cs @@ -148,7 +148,7 @@ public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) if (benchmarkCase.HasParameters) { - var testParameters = new TestParameters { Arguments = new Dictionary(), Metadata = new Dictionary() }; + var testParameters = new TestParameters { Arguments = new Dictionary(), Metadata = new Dictionary() }; foreach (var parameter in benchmarkCase.Parameters.Items) { var parameterValue = ClrProfiler.AutoInstrumentation.Testing.Common.GetParametersValueData(parameter.Value); diff --git a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs index 94ec26477fa8..165890b7b3f8 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/MessagePack/EventMessagePackFormatter.cs @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // #nullable enable -#pragma warning disable SA1402 +#pragma warning disable SA1402 // disable check to only have one class per file using System; using Datadog.Trace.Logging; diff --git a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs index 84b343e439c1..22eed2c1a4ae 100644 --- a/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs +++ b/tracer/src/Datadog.Trace/Ci/Agent/Payloads/EventPlatformPayload.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Diagnostics.CodeAnalysis; using Datadog.Trace.Agent; using Datadog.Trace.Ci.Configuration; using Datadog.Trace.Telemetry.Metrics; @@ -61,7 +62,7 @@ public Uri Url { if (_url is null) { - return EnsureUrl(); + EnsureUrl(); } return _url; @@ -123,7 +124,8 @@ public virtual bool UseGZip /// public abstract void Reset(); - private Uri EnsureUrl() + [MemberNotNull(nameof(_url))] + private void EnsureUrl() { UriBuilder builder; if (_settings.Agentless) @@ -173,6 +175,5 @@ private Uri EnsureUrl() } _url = builder.Uri; - return builder.Uri; } } diff --git a/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs b/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs index 154a3723b54e..ddc833f135bd 100644 --- a/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs +++ b/tracer/src/Datadog.Trace/Ci/CITracerManagerFactory.cs @@ -81,7 +81,7 @@ protected override ITraceSampler GetSampler(ImmutableTracerSettings settings) protected override bool ShouldEnableRemoteConfiguration(ImmutableTracerSettings settings) => false; - protected override IAgentWriter? GetAgentWriter(ImmutableTracerSettings settings, IDogStatsd statsd, Action> updateSampleRates, IDiscoveryService discoveryService) + protected override IAgentWriter GetAgentWriter(ImmutableTracerSettings settings, IDogStatsd statsd, Action> updateSampleRates, IDiscoveryService discoveryService) { // Check for agentless scenario if (_settings.Agentless) @@ -92,7 +92,7 @@ protected override ITraceSampler GetSampler(ImmutableTracerSettings settings) } Environment.FailFast("An API key is required in Agentless mode."); - return null; + return null!; } // With agent scenario: diff --git a/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs b/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs index 3b9c899cd75f..56ae02095406 100644 --- a/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs +++ b/tracer/src/Datadog.Trace/Ci/Coverage/Exceptions/PdbNotFoundException.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace Datadog.Trace.Ci.Coverage.Exceptions; @@ -19,6 +20,7 @@ internal class PdbNotFoundException : Exception /// /// Throws current exception [MethodImpl(MethodImplOptions.NoInlining)] + [DoesNotReturn] public static void Throw() { throw new PdbNotFoundException(); diff --git a/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs b/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs index 276021c2c4ec..d7c1aa6370cc 100644 --- a/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs +++ b/tracer/src/Datadog.Trace/Ci/Processors/OriginTagTraceProcessor.cs @@ -85,8 +85,13 @@ public ArraySegment Process(ArraySegment trace) return trace; } - public Span Process(Span span) + public Span? Process(Span? span) { + if (span is null) + { + return span; + } + // Sets the origin tag on the TraceContext to ensure the CI track. var traceContext = span.Context.TraceContext; diff --git a/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs b/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs index 78675bb82288..83565d4f1ce7 100644 --- a/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs +++ b/tracer/src/Datadog.Trace/Ci/Processors/TestSuiteVisibilityProcessor.cs @@ -48,7 +48,7 @@ public ArraySegment Process(ArraySegment trace) return spans is null ? new ArraySegment([]) : new ArraySegment(spans, 0, spIdx); } - public Span? Process(Span span) + public Span? Process(Span? span) { // If agentless is enabled we don't filter anything. if (span is null) diff --git a/tracer/src/Datadog.Trace/Ci/TestParameters.cs b/tracer/src/Datadog.Trace/Ci/TestParameters.cs index afe9a00c9ffa..cddd7fcc2d8c 100644 --- a/tracer/src/Datadog.Trace/Ci/TestParameters.cs +++ b/tracer/src/Datadog.Trace/Ci/TestParameters.cs @@ -24,7 +24,7 @@ public class TestParameters /// Gets or sets the test arguments /// [JsonProperty("arguments")] - public Dictionary? Arguments { get; set; } + public Dictionary? Arguments { get; set; } internal string ToJSON() { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs index 50eed1b1e6a5..9ab7869c079c 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/ManualInstrumentation/Ci/Proxies/ITestParameters.cs @@ -18,5 +18,5 @@ internal interface ITestParameters : IDuckType { public Dictionary? Metadata { get; } - public Dictionary? Arguments { get; } + public Dictionary? Arguments { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs index 13491788b1d0..c7de2e4256f5 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestIntegration.cs @@ -141,7 +141,7 @@ internal static void UpdateTestParameters(Test test, TTestMethod te var testParameters = new TestParameters { Metadata = new Dictionary(), - Arguments = new Dictionary() + Arguments = new Dictionary() }; if (!string.IsNullOrEmpty(displayName) && displayName != testName) @@ -227,9 +227,9 @@ internal static void UpdateTestParameters(Test test, TTestMethod te testProperties["Category"] = categoryList; } - if (tattr.TryDuckCast(out var tattrStruct)) + if (tattr.TryDuckCast(out var tattrStruct) && tattrStruct.TestCategories != null) { - categoryList.AddRange(tattrStruct.TestCategories ?? []); + categoryList.AddRange(tattrStruct.TestCategories); } } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs index 9c89d50d04cf..7e0e87876096 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestAssemblyInfoRunAssemblyInitializeIntegration.cs @@ -59,7 +59,7 @@ internal static CallTargetState OnMethodBegin(TTarget instanc instance.AssemblyCleanupMethod ??= EmptyCleanUpMethodInfo; } - return new CallTargetState(null, MsTestIntegration.GetOrCreateTestModuleFromTestAssemblyInfo(instance, context.TestMethod.AssemblyName)); + return new CallTargetState(null, MsTestIntegration.GetOrCreateTestModuleFromTestAssemblyInfo(instance, context.TestMethod?.AssemblyName)); } /// diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs index a99014fcf164..cf86420c9f4d 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/TestContextStruct.cs @@ -14,5 +14,5 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2; internal struct TestContextStruct { [DuckField(Name = "testMethod,_testMethod")] - public TestMethodContextStruct TestMethod; + public TestMethodContextStruct? TestMethod; } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs index 91d014dc4d94..d48aca6e2744 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ICompositeWorkItem.cs @@ -16,5 +16,5 @@ internal interface ICompositeWorkItem : IWorkItem /// /// Gets the List of Child WorkItems /// - IEnumerable Children { get; } + IEnumerable? Children { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs index 1f4a474efcd8..b806fc76ea5b 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IMethodInfo.cs @@ -16,5 +16,5 @@ internal interface IMethodInfo /// /// Gets the MethodInfo for this method. /// - MethodInfo MethodInfo { get; } + MethodInfo? MethodInfo { get; } } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs index 8a38338390e3..49d2ef074ee2 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITest.cs @@ -56,7 +56,7 @@ internal interface ITest : IDuckType /// Gets a MethodInfo for the method implementing this test. /// Returns null if the test is not implemented as a method. /// - IMethodInfo Method { get; } + IMethodInfo? Method { get; } /// /// Gets or sets whether or not the test should be run diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs index a40ea9996e3f..16f96316f356 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/ITestCommand.cs @@ -12,5 +12,5 @@ internal interface ITestCommand : IDuckType { ITest? Test { get; } - ITestResult Execute(ITestExecutionContext? context); + ITestResult Execute(ITestExecutionContext context); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs index d440265927b7..e3ff94059b38 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/IWorkItem.cs @@ -28,9 +28,9 @@ internal interface IWorkItem : IDuckType internal interface INUnitTestAssemblyRunner { - ITest LoadedTest { get; } + ITest? LoadedTest { get; } - IWorkItem TopLevelWorkItem { get; } + IWorkItem? TopLevelWorkItem { get; } } internal interface ITypeInfo diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs index 1075cccd4145..5e39cd9ef42f 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitIntegration.cs @@ -168,7 +168,12 @@ internal static bool ShouldSkip(ITest currentTest, out bool isUnskippable, out b return false; } - var testMethod = currentTest.Method.MethodInfo; + var testMethod = currentTest.Method?.MethodInfo; + if (testMethod is null) + { + return false; + } + var testSuite = testMethod.DeclaringType?.FullName ?? string.Empty; var itrShouldSkip = Common.ShouldSkip(testSuite, testMethod.Name, currentTest.Arguments, testMethod.GetParameters()); if (traits is null) @@ -237,9 +242,11 @@ internal static void GetExceptionAndMessage(ITestResult result, out string excep var methodParameters = testMethod.GetParameters(); if (methodParameters?.Length > 0) { - var testParameters = new TestParameters(); - testParameters.Metadata = new Dictionary(); - testParameters.Arguments = new Dictionary(); + var testParameters = new TestParameters + { + Metadata = new Dictionary(), + Arguments = new Dictionary() + }; testParameters.Metadata[TestTags.MetadataTestName] = currentTest.Name ?? string.Empty; for (int i = 0; i < methodParameters.Length; i++) @@ -262,7 +269,7 @@ internal static void GetExceptionAndMessage(ITestResult result, out string excep Dictionary>? traits = null; if (testMethodProperties != null) { - skipReason = (string?)testMethodProperties.Get(SkipReasonKey); + skipReason = testMethodProperties.Get(SkipReasonKey) as string; ExtractTraits(currentTest, ref traits); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs index 3da08a4afe1a..2950074007c4 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/NUnit/NUnitWorkItemWorkItemCompleteIntegration.cs @@ -74,7 +74,7 @@ internal static CallTargetState OnMethodBegin(TTarget instance) suite.Tags.Status = TestTags.StatusFail; // Handle setup errors - if (result.ResultState.Site == FailureSite.SetUp && compositeWorkItem is not null) + if (result.ResultState.Site == FailureSite.SetUp && compositeWorkItem?.Children != null) { foreach (var child in compositeWorkItem.Children) { @@ -93,7 +93,7 @@ internal static CallTargetState OnMethodBegin(TTarget instance) } // Handle ignored children in a Theory if the theory has been marked as ignored - if (compositeWorkItem is not null) + if (compositeWorkItem?.Children is not null) { foreach (var child in compositeWorkItem.Children) { @@ -162,7 +162,8 @@ private static void WriteDebugInfo(IWorkItem workItem) } if (workItem.Result.ResultState.Site == FailureSite.SetUp && - workItem.Instance.TryDuckCast(out var compositeWorkItem)) + workItem.Instance.TryDuckCast(out var compositeWorkItem) && + compositeWorkItem.Children != null) { foreach (var child in compositeWorkItem.Children) { diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs index cd12a94c3081..d9165612c888 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/XUnit/XUnitIntegration.cs @@ -41,9 +41,11 @@ internal static class XUnitIntegration var methodParameters = testMethod?.GetParameters(); if (methodParameters?.Length > 0 && testMethodArguments?.Length > 0) { - var testParameters = new TestParameters(); - testParameters.Metadata = new Dictionary(); - testParameters.Arguments = new Dictionary(); + var testParameters = new TestParameters + { + Metadata = new Dictionary(), + Arguments = new Dictionary() + }; testParameters.Metadata[TestTags.MetadataTestName] = runnerInstance.TestCase.DisplayName ?? string.Empty; for (var i = 0; i < methodParameters.Length; i++) diff --git a/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs b/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs index 9fa5241bbc80..c68ee9588d2a 100644 --- a/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs +++ b/tracer/src/Datadog.Trace/Processors/ITraceProcessor.cs @@ -12,7 +12,7 @@ internal interface ITraceProcessor { ArraySegment Process(ArraySegment trace); - Span? Process(Span span); + Span? Process(Span? span); ITagProcessor? GetTagProcessor(); } From 8cd87ba8a855ab87160e35687305ad8366d91446 Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Wed, 4 Dec 2024 10:15:49 +0100 Subject: [PATCH 9/9] changes --- ...icApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt b/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt index 06f0fc2e282f..6bb7521f24b5 100644 --- a/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt +++ b/tracer/test/Datadog.Trace.Tests/Snapshots/PublicApiTests.Datadog.Trace.PublicApiHasNotChanged.verified.txt @@ -102,7 +102,7 @@ namespace Datadog.Trace.Ci public class TestParameters { public TestParameters() { } - public System.Collections.Generic.Dictionary? Arguments { get; set; } + public System.Collections.Generic.Dictionary? Arguments { get; set; } public System.Collections.Generic.Dictionary? Metadata { get; set; } } public sealed class TestSession