| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- // Copyright (c) .NET Foundation. All rights reserved.
- // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
- 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
- {
- [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);
- // 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);
- // 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);
- // 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);
- // 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);
- // 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);
- // 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);
- // 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);
- // 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);
- var methodNames = stackFrames.Select(stackFrame => stackFrame.MethodDisplayInfo.ToString()).ToArray();
- // Assert
- Assert.Equal(expectedCallStack, methodNames);
- }
- [Fact]
- public void StackTraceHelper_DoesNotIncludeInstanceMethodsOnTypesWithStackTraceHiddenAttribute()
- {
- // Arrange
- var exception = Record.Exception(() => InvokeMethodOnTypeWithStackTraceHiddenAttribute());
- // Act
- var stackFrames = StackTraceHelper.GetFrames(exception);
- // 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);
- // 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);
- // 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).ToArray();
- // Assert
- var frame = frames[0];
- Assert.Null(frame.FilePath);
- Assert.Equal($"lambda_method(Closure )", frame.MethodDisplayInfo.ToString());
- }
- [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();
- }
- }
|