< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.AsyncResponsePayloadReflection
Assembly: AsyncResponse.Abstractions
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/AsyncResponse.Abstractions/AsyncResponsePayloadReflection.cs
Line coverage
100%
Covered lines: 14
Uncovered lines: 0
Coverable lines: 14
Total lines: 62
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
OverridesShouldResumeOnRecovery(...)100%11100%
DetectOverride(...)100%66100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/AsyncResponse.Abstractions/AsyncResponsePayloadReflection.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Reflection;
 4
 5namespace AsyncResponse;
 6
 7/// <summary>
 8/// Reflection helpers over <see cref="IAsyncResponsePayload"/> implementations. Durable channels
 9/// use this to fail fast when a recovery-enabled flow's payload has not overridden
 10/// <see cref="IAsyncResponsePayload.ShouldResumeOnRecovery"/> and would otherwise silently take the
 11/// conservative default (never resume).
 12/// </summary>
 13public static class AsyncResponsePayloadReflection
 14{
 315    private static readonly ConcurrentDictionary<Type, bool> OverrideCache = new();
 16
 17    /// <summary>
 18    /// Returns <c>true</c> when <paramref name="payloadType"/> provides its own implementation of
 19    /// <see cref="IAsyncResponsePayload.ShouldResumeOnRecovery"/> rather than inheriting the
 20    /// interface's default. The result is cached per type.
 21    /// </summary>
 22    public static bool OverridesShouldResumeOnRecovery(Type payloadType)
 23    {
 324        ArgumentNullException.ThrowIfNull(payloadType);
 25
 326        return OverrideCache.GetOrAdd(payloadType, DetectOverride);
 27    }
 28
 29    [UnconditionalSuppressMessage("Trimming", "IL2070",
 30        Justification = "Best-effort diagnostic: payload types reaching this check are statically referenced by the wait
 31                        "registration that triggers it (typeof(T)), so their methods are preserved; when the runtime sti
 32                        "cannot answer, the check fails open rather than failing a correct app.")]
 33    private static bool DetectOverride(Type type)
 34    {
 335        if (type.IsInterface || !typeof(IAsyncResponsePayload).IsAssignableFrom(type))
 336            return false;
 37
 38        // Implicit implementations (the overwhelmingly common shape) declare a public
 39        // ShouldResumeOnRecovery on the class itself — detectable without the interface map.
 340        if (type.GetMethod(nameof(IAsyncResponsePayload.ShouldResumeOnRecovery), BindingFlags.Instance | BindingFlags.Pu
 341            return true;
 42
 43        try
 44        {
 345            var map = type.GetInterfaceMap(typeof(IAsyncResponsePayload));
 346            var interfaceMethod = typeof(IAsyncResponsePayload).GetMethod(nameof(IAsyncResponsePayload.ShouldResumeOnRec
 347            var index = Array.IndexOf(map.InterfaceMethods, interfaceMethod);
 48
 49            // When the type does not implement the method, the interface map points the target
 50            // back at the interface's own default implementation.
 351            return map.TargetMethods[index].DeclaringType != typeof(IAsyncResponsePayload);
 52        }
 153        catch (NotSupportedException)
 54        {
 55            // Native AOT cannot compute the interface map for interfaces with default
 56            // implementations. This check exists to fail fast on a payload that silently
 57            // inherits the conservative default — when the runtime cannot answer, assume the
 58            // payload is fine instead of breaking a correct app (fail open).
 159            return true;
 60        }
 361    }
 62}