/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Shared/test/Shared.Tests/StackTraceHelperTest.cs
382 строки
14 KB
Medha Tiwari
Make StackTraceHelper tests runtime-agnostic (#64007)
12 дек 2025, 03:18
Не верифицирован
12 дек 2025, 03:18
c5f8422
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.Extensions.StackTrace.Sources; using ThrowingLibrary; using Xunit; namespace Microsoft.Extensions.Internal; public class StackTraceHelperTest { private static bool IsMono => Type.GetType("Mono.RuntimeStructs") != null; [Fact] public void StackTraceHelper_IncludesLineNumbersForFiles() { // Arrange Exception exception = null; try { // Throwing an exception in the current assembly always seems to populate the full stack // trace regardless of symbol type. Crossing assembly boundaries ensures PortablePdbReader gets used // on desktop. Thrower.Throw(); } catch (Exception ex) { exception = ex; } // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert Assert.Collection(stackFrames, frame => { Assert.Contains("Thrower.cs", frame.FilePath); Assert.Equal(17, frame.LineNumber); }, frame => { Assert.Contains("StackTraceHelperTest.cs", frame.FilePath); }); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForGenericMethods() { // Arrange var exception = Record.Exception(() => GenericMethod<string>(null)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.GenericMethod<T>(T val)", methods[0]); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithOutParameters() { // Arrange var exception = Record.Exception(() => MethodWithOutParameter(out var value)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithOutParameter(out int value)", methods[0]); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericOutParameters() { // Arrange var exception = Record.Exception(() => MethodWithGenericOutParameter("Test", out int value)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericOutParameter<TVal>(string a, out TVal value)", methods[0]); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithRefParameters() { // Arrange var value = 0; var exception = Record.Exception(() => MethodWithRefParameter(ref value)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithRefParameter(ref int value)", methods[0]); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithGenericRefParameters() { // Arrange var value = 0; var exception = Record.Exception(() => MethodWithGenericRefParameter(ref value)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithGenericRefParameter<TVal>(ref TVal value)", methods[0]); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForMethodsWithNullableParameters() { // Arrange var value = 0; var exception = Record.Exception(() => MethodWithNullableParameter(value)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.MethodWithNullableParameter(Nullable<int> value)", methods[0]); } [Fact] public void StackTraceHelper_PrettyPrintsStackTraceForMethodsOnGenericTypes() { // Arrange var exception = Record.Exception(() => new GenericClass<int>().Throw(0)); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.Throw(T parameter)", methods[0]); } [Fact] public void StackTraceHelper_ProducesReadableOutput() { // Arrange var expectedCallStack = new List<string>() { "Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()", "string.Join(string separator, IEnumerable<string> values)", "Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.GenericMethod<V>(ref V value)", "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)", "Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync<TValue>(TValue value)", "Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)", "Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()", }; Exception exception = null; try { Method("test"); } catch (Exception ex) { exception = ex; } // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); 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<T>.GenericMethod<V>", 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>(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] public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute() { // Arrange var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute()); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]); } [Fact] public void StackTraceHelper_DoesNotIncludeStaticMethodsOnTypesWithStackTraceHiddenAttribute() { // Arrange var exception = Record.Exception(() => InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute()", methods[1]); } [Fact] public void StackTraceHelper_DoesNotIncludeMethodsWithStackTraceHiddenAttribute() { // Arrange var exception = Record.Exception(() => new TypeWithMethodWithStackTraceHiddenAttribute().Throw()); // Act var stackFrames = StackTraceHelper.GetFrames(exception, out _); // Assert var methods = stackFrames.Select(frame => frame.MethodDisplayInfo.ToString()).ToArray(); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest.ThrowCore()", methods[0]); Assert.Equal("Microsoft.Extensions.Internal.StackTraceHelperTest+TypeWithMethodWithStackTraceHiddenAttribute.Throw()", methods[1]); } [Fact] public void GetFrames_DoesNotFailForDynamicallyGeneratedAssemblies() { // Arrange var action = (Action)Expression.Lambda( Expression.Throw( Expression.New(typeof(Exception)))).Compile(); var exception = Record.Exception(action); // Act var frames = StackTraceHelper.GetFrames(exception, out _).ToArray(); // Assert var frame = frames[0]; Assert.Null(frame.FilePath); var methodDisplay = frame.MethodDisplayInfo.ToString(); 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)] async Task<string> MethodAsync(int value) { await Task.Delay(0); return GenericClass<byte>.GenericMethod(ref value); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] async Task<string> MethodAsync<TValue>(TValue value) { return await MethodAsync(1); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] string Method(string value) { return MethodAsync(value).GetAwaiter().GetResult(); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] static IEnumerable<string> Iterator() { yield return "Success"; throw new Exception(); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void MethodWithOutParameter(out int value) => throw new Exception(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void MethodWithGenericOutParameter<TVal>(string a, out TVal value) => throw new Exception(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void MethodWithRefParameter(ref int value) => throw new Exception(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void MethodWithGenericRefParameter<TVal>(ref TVal value) => throw new Exception(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void MethodWithNullableParameter(int? value) => throw new Exception(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void InvokeMethodOnTypeWithStackTraceHiddenAttribute() => new TypeWithStackTraceHiddenAttribute().Throw(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] void InvokeStaticMethodOnTypeWithStackTraceHiddenAttribute() => TypeWithStackTraceHiddenAttribute.ThrowStatic(); class GenericClass<T> { [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public static string GenericMethod<V>(ref V value) { var returnVal = ""; for (var i = 0; i < 10; i++) { returnVal += string.Join(", ", Iterator()); } return returnVal; } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public void Throw(T parameter) => throw new Exception(); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private void GenericMethod<T>(T val) where T : class => throw new Exception(); private class StackTraceHiddenAttribute : Attribute { } [StackTraceHidden] private class TypeWithStackTraceHiddenAttribute { [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public void Throw() => ThrowCore(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public static void ThrowStatic() => ThrowCore(); } private class TypeWithMethodWithStackTraceHiddenAttribute { [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] [StackTraceHidden] public void MethodWithStackTraceHiddenAttribute() { ThrowCore(); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public void Throw() => MethodWithStackTraceHiddenAttribute(); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private static void ThrowCore() => throw new Exception(); }