From f1778770dcbc4b22c4927026ec7916f135e1f8a2 Mon Sep 17 00:00:00 2001 From: Medha Tiwari Date: Sun, 12 Oct 2025 06:05:45 -0400 Subject: [PATCH 1/2] Make StackTraceHelper tests runtime-agnostic --- .../test/Shared.Tests/StackTraceHelperTest.cs | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs index 4f3b6ed178de..af9516783a5c 100644 --- a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs +++ b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs @@ -176,10 +176,27 @@ public void StackTraceHelper_ProducesReadableOutput() // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); - var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray(); - - // Assert - Assert.Equal(expectedCallStack, methodNames); + var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToList(); + + // Assert - Runtime-agnostic checks for essential stack trace components + // Instead of exact string matching, verify key components are present + Assert.Equal(expectedCallStack.Count, methodNames.Count); + + // Check each frame contains the essential method information + Assert.Contains("Iterator()+MoveNext()", methodNames[0]); + Assert.Contains("string.Join", methodNames[1]); + Assert.Contains("GenericClass.GenericMethod", methodNames[2]); + Assert.Contains("MethodAsync(int value)", methodNames[3]); + + // For async generic method, check for either resolved form or state machine form + var asyncGenericFrame = methodNames[4]; + Assert.True( + asyncGenericFrame.Contains("MethodAsync(TValue value)") || // CoreCLR resolved form + asyncGenericFrame.Contains("MethodAsync") && asyncGenericFrame.Contains("TValue"), // Mono state machine form + $"Expected async generic method info in: {asyncGenericFrame}"); + + Assert.Contains("Method(string value)", methodNames[5]); + Assert.Contains("StackTraceHelper_ProducesReadableOutput()", methodNames[6]); } [Fact] @@ -242,9 +259,12 @@ public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies() // Assert var frame = frames[0]; Assert.Null(frame.FilePath); - // lambda_method{RandomNumber}(Closure ) - Assert.StartsWith("lambda_method", frame.MethodDisplayInfo.ToString()); - Assert.EndsWith("(Closure )", frame.MethodDisplayInfo.ToString()); + // Runtime-agnostic test: should contain "lambda_method" regardless of prefix + // CoreCLR: "lambda_method34(Closure )" + // Mono: "object.lambda_method34(Closure )" + var methodDisplay = frame.MethodDisplayInfo.ToString(); + Assert.Contains("lambda_method", methodDisplay); + Assert.EndsWith("(Closure )", methodDisplay); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] From db1e3c251f03a2b0a06dac0b4be2a2d4f9f7bedc Mon Sep 17 00:00:00 2001 From: Medha Tiwari Date: Tue, 21 Oct 2025 19:52:53 +0200 Subject: [PATCH 2/2] Use runtime-specific assertions for Mono compatibility --- .../test/Shared.Tests/StackTraceHelperTest.cs | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs index af9516783a5c..449396d8c064 100644 --- a/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs +++ b/src/Shared/test/Shared.Tests/StackTraceHelperTest.cs @@ -15,6 +15,7 @@ namespace Microsoft.Extensions.Internal; public class StackTraceHelperTest { + private static bool IsMono => Type.GetType("Mono.RuntimeStructs") != null; [Fact] public void StackTraceHelper_IncludesLineNumbersForFiles() { @@ -176,27 +177,33 @@ public void StackTraceHelper_ProducesReadableOutput() // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); - var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToList(); - - // Assert - Runtime-agnostic checks for essential stack trace components - // Instead of exact string matching, verify key components are present - Assert.Equal(expectedCallStack.Count, methodNames.Count); - - // Check each frame contains the essential method information - Assert.Contains("Iterator()+MoveNext()", methodNames[0]); - Assert.Contains("string.Join", methodNames[1]); - Assert.Contains("GenericClass.GenericMethod", methodNames[2]); - Assert.Contains("MethodAsync(int value)", methodNames[3]); - - // For async generic method, check for either resolved form or state machine form - var asyncGenericFrame = methodNames[4]; - Assert.True( - asyncGenericFrame.Contains("MethodAsync(TValue value)") || // CoreCLR resolved form - asyncGenericFrame.Contains("MethodAsync") && asyncGenericFrame.Contains("TValue"), // Mono state machine form - $"Expected async generic method info in: {asyncGenericFrame}"); - - Assert.Contains("Method(string value)", methodNames[5]); - Assert.Contains("StackTraceHelper_ProducesReadableOutput()", methodNames[6]); + var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray(); + + // Assert + Assert.Equal(expectedCallStack.Count, methodNames.Length); + + if (IsMono) + { + // On Mono, verify key components are present but allow for runtime-specific formatting + Assert.Contains("Iterator()+MoveNext()", methodNames[0]); + Assert.Contains("string.Join", methodNames[1]); + Assert.Contains("GenericClass.GenericMethod", methodNames[2]); + Assert.Contains("MethodAsync(int value)", methodNames[3]); + + // For async generic method on Mono, check for either resolved form or state machine form + var asyncGenericFrame = methodNames[4]; + Assert.True( + asyncGenericFrame.Contains("MethodAsync(TValue value)") || // Resolved form + asyncGenericFrame.Contains("MethodAsync") && asyncGenericFrame.Contains("TValue"), // State machine form + $"Expected async generic method info in: {asyncGenericFrame}"); + + Assert.Contains("Method(string value)", methodNames[5]); + Assert.Contains("StackTraceHelper_ProducesReadableOutput()", methodNames[6]); + } + else + { + Assert.Equal(expectedCallStack, methodNames); + } } [Fact] @@ -259,12 +266,20 @@ public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies() // Assert var frame = frames[0]; Assert.Null(frame.FilePath); - // Runtime-agnostic test: should contain "lambda_method" regardless of prefix - // CoreCLR: "lambda_method34(Closure )" - // Mono: "object.lambda_method34(Closure )" var methodDisplay = frame.MethodDisplayInfo.ToString(); - Assert.Contains("lambda_method", methodDisplay); - Assert.EndsWith("(Closure )", methodDisplay); + + if (IsMono) + { + // On Mono, lambda methods may include declaring type prefix (e.g., "object.lambda_method34") + Assert.Contains("lambda_method", methodDisplay); + Assert.EndsWith("(Closure )", methodDisplay); + } + else + { + // On CoreCLR, maintain strict check to prevent regressions + Assert.StartsWith("lambda_method", methodDisplay); + Assert.EndsWith("(Closure )", methodDisplay); + } } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]