Skip to content

Commit

Permalink
[CI Visibility] Add support for coverlet.msbuild coverage reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyredondo committed Nov 14, 2024
1 parent 5325593 commit b261d56
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class CoverageGetCoverageResultIntegration
{
internal static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception? exception, in CallTargetState state)
{
if (!DotnetCommon.IsDataCollectorDomain)
if (!DotnetCommon.IsDataCollectorDomain && !DotnetCommon.IsMsBuildTask)
{
return new CallTargetReturn<TReturn>(returnValue);
}
Expand Down Expand Up @@ -64,7 +64,6 @@ internal static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget
var percentage = coverageDetails.Percent;
DotnetCommon.Log.Information("CoverageGetCoverageResult.Percentage: {Value}", percentage);

// Extract session variables (from out of process sessions)
var context = SpanContextPropagator.Instance.Extract(
EnvironmentHelpers.GetEnvironmentVariables(),
new DictionaryGetterAndSetter(DictionaryGetterAndSetter.EnvironmentVariableKeyProcessor));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
Expand All @@ -28,6 +29,7 @@ internal static class DotnetCommon

internal static readonly IDatadogLogger Log = Ci.CIVisibility.Log;
private static bool? _isDataCollectorDomainCache;
private static bool? _isMsBuildTaskCache;

internal static bool DotnetTestIntegrationEnabled => CIVisibility.IsRunning && Tracer.Instance.Settings.IsIntegrationEnabled(DotnetTestIntegrationId);

Expand All @@ -40,10 +42,46 @@ internal static bool IsDataCollectorDomain
return value;
}

// If the AppDomainName contains "DataCollector" we are in the data collector domain
return (_isDataCollectorDomainCache = DomainMetadata.Instance.AppDomainName.ToLowerInvariant().Contains("datacollector")).Value;
}
}

internal static bool IsMsBuildTask
{
get
{
if (_isMsBuildTaskCache is { } value)
{
return value;
}

// Let's try to detect if we are in the MSBuild task scenario
// the process name is not guaranteed so we need to check the stack trace
// to see if we are getting called from the Coverlet.MSbuild.Tasks namespace
// because we don't need any symbols this should be fast enough.
if (new StackTrace(3, false).GetFrames() is { } frames)
{
foreach (var stackFrame in frames)
{
if (stackFrame is null)
{
continue;
}

if (stackFrame.GetMethod()?.DeclaringType?.FullName?.Contains("Coverlet.MSbuild.Tasks") == true)
{
return (_isMsBuildTaskCache = true).Value;
}
}

_isMsBuildTaskCache = false;
}

return _isMsBuildTaskCache ?? false;
}
}

internal static TestSession? CreateSession()
{
// We create test session if not DataCollector
Expand Down
4 changes: 2 additions & 2 deletions tracer/src/Datadog.Trace/DuckTyping/DuckType.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private static void CreateMethods(
DuckTypeTargetMethodNotFoundException.Throw(proxyMethodDefinition);
}

targetMethod = targetMethod.MakeGenericMethod(proxyDuckAttribute.GenericParameterTypeNames.Select(name => Type.GetType(name, throwOnError: true)!).ToArray());
targetMethod = targetMethod.MakeGenericMethod(proxyDuckAttribute.GenericParameterTypeNames.Select(name => GetTypeFromPartialName(name, throwOnError: true)!).ToArray());
}

// Gets target method parameters
Expand Down Expand Up @@ -383,7 +383,7 @@ private static void CreateReverseProxyMethods(TypeBuilder? proxyTypeBuilder, Typ
}

Type[] parameterTypes = proxyMethodDuckAttributeParameterTypeNames
.Select(pName => Type.GetType(pName, throwOnError: false))
.Select(pName => GetTypeFromPartialName(pName))
.Where(type => type is not null)
.ToArray()!;
if (parameterTypes.Length == proxyMethodDuckAttributeParameterTypeNames.Length)
Expand Down
70 changes: 70 additions & 0 deletions tracer/src/Datadog.Trace/DuckTyping/DuckType.Statics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;

// ReSharper disable InconsistentNaming

namespace Datadog.Trace.DuckTyping
Expand Down Expand Up @@ -185,6 +187,74 @@ static ModuleBuilder CreateModuleBuilder(string name, Assembly targetAssembly)
}
}

private static Type? GetTypeFromPartialName(string partialName, bool throwOnError = false)
{
Type? type = null;
ExceptionDispatchInfo? loadException = null;

try
{
// Let's try to find the type using the partial name first.
type = Type.GetType(partialName, throwOnError: throwOnError);
}
catch (Exception ex)
{
// let's capture the exception
loadException = ExceptionDispatchInfo.Capture(ex);
}

// If the type cannot be found, and the name doesn't contain a version,
// we try to find the type in the current domain/alc using any assembly that has the same name.
if (type is null && !partialName.Contains("Version="))
{
var typePair = partialName.Split([','], StringSplitOptions.RemoveEmptyEntries);
if (typePair.Length != 2)
{
if (throwOnError)
{
DuckTypeException.Throw($"Invalid type name: {partialName}");
}

return null;
}

var typeValue = typePair[0].Trim();
var assemblyValue = typePair[1].Trim();

try
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetName().Name != assemblyValue)
{
continue;
}

type = assembly.GetType(typeValue, throwOnError: false);
if (type is not null)
{
break;
}
}
}
catch
{
// If we cannot get the assemblies, we just ignore them.
// we are not interested in throwing an exception here.
// we will just continue with an empty array.
// And will throw later the original Type.GetType exception.
}
}

// If we were unable to load the type, and we have to throw an error, we do it now.
if (type is null && throwOnError)
{
loadException?.Throw();
}

return type;
}

/// <summary>
/// DynamicMethods delegates cache
/// </summary>
Expand Down

0 comments on commit b261d56

Please sign in to comment.