mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42:51 +08:00
Adding better type names for return types and parameters
Uses the typenamehelper from Ben.Demystifer to help break down things like generic lists into their actual type display name.
This commit is contained in:
parent
a0e20f299c
commit
78958aae27
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -10,7 +12,8 @@ public static class Program
|
||||
{
|
||||
try
|
||||
{
|
||||
DoMagic(42, null);
|
||||
var foo = new List<string>();
|
||||
DoMagic(42, null, ref foo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -47,22 +50,23 @@ public static class Program
|
||||
|
||||
try
|
||||
{
|
||||
await DoMagicAsync(42, null);
|
||||
await DoMagicAsync<int>(42, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Write(new Rule("Async").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex);
|
||||
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenPaths);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoMagic(int foo, string[,] bar)
|
||||
private static void DoMagic(int foo, string[,] bar, ref List<string> result)
|
||||
{
|
||||
try
|
||||
{
|
||||
CheckCredentials(foo, bar);
|
||||
result = new List<string>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -70,16 +74,16 @@ public static class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckCredentials(int qux, string[,] corgi)
|
||||
private static bool CheckCredentials(int? qux, string[,] corgi)
|
||||
{
|
||||
throw new InvalidCredentialException("The credentials are invalid.");
|
||||
}
|
||||
|
||||
private static async Task DoMagicAsync(int foo, string[,] bar)
|
||||
private static async Task DoMagicAsync<T>(T foo, string[,] bar)
|
||||
{
|
||||
try
|
||||
{
|
||||
await CheckCredentialsAsync(foo, bar);
|
||||
await CheckCredentialsAsync(new[] { foo }.ToList(), new []{ foo }, bar);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -87,7 +91,7 @@ public static class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CheckCredentialsAsync(int qux, string[,] corgi)
|
||||
private static async Task<bool> CheckCredentialsAsync<T>(List<T> qux, T[] otherArray, string[,] corgi)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
throw new InvalidCredentialException("The credentials are invalid.");
|
||||
|
@ -19,11 +19,7 @@ internal static class ExceptionFormatter
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
return new Rows(new IRenderable[]
|
||||
{
|
||||
GetMessage(exception, settings),
|
||||
GetStackFrames(exception, settings),
|
||||
}).Expand();
|
||||
return new Rows(GetMessage(exception, settings), GetStackFrames(exception, settings)).Expand();
|
||||
}
|
||||
|
||||
private static Markup GetMessage(Exception ex, ExceptionSettings settings)
|
||||
@ -78,6 +74,13 @@ internal static class ExceptionFormatter
|
||||
builder.Append("async ");
|
||||
}
|
||||
|
||||
if (method is MethodInfo mi)
|
||||
{
|
||||
var returnParameter = mi.ReturnParameter;
|
||||
builder.AppendWithStyle(styles.ParameterType, GetParameterName(returnParameter).EscapeMarkup());
|
||||
builder.Append(' ');
|
||||
}
|
||||
|
||||
builder.Append(Emphasize(methodName, new[] { '.' }, styles.Method, shortenMethods, settings));
|
||||
builder.AppendWithStyle(styles.Parenthesis, "(");
|
||||
AppendParameters(builder, method, settings);
|
||||
@ -114,7 +117,9 @@ internal static class ExceptionFormatter
|
||||
{
|
||||
var typeColor = settings.Style.ParameterType.ToMarkup();
|
||||
var nameColor = settings.Style.ParameterName.ToMarkup();
|
||||
var parameters = method?.GetParameters().Select(x => $"[{typeColor}]{x.ParameterType.Name.EscapeMarkup()}[/] [{nameColor}]{x.Name?.EscapeMarkup()}[/]");
|
||||
var parameters = method?.GetParameters()
|
||||
.Select(x => $"[{typeColor}]{GetParameterName(x).EscapeMarkup()}[/] [{nameColor}]{x.Name?.EscapeMarkup()}[/]");
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
builder.Append(string.Join(", ", parameters));
|
||||
@ -150,7 +155,8 @@ internal static class ExceptionFormatter
|
||||
}
|
||||
}
|
||||
|
||||
private static string Emphasize(string input, char[] separators, Style color, bool compact, ExceptionSettings settings)
|
||||
private static string Emphasize(string input, char[] separators, Style color, bool compact,
|
||||
ExceptionSettings settings)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
@ -240,6 +246,46 @@ internal static class ExceptionFormatter
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPrefix(ParameterInfo parameter)
|
||||
{
|
||||
if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute), false))
|
||||
{
|
||||
return "params";
|
||||
}
|
||||
|
||||
if (parameter.IsOut)
|
||||
{
|
||||
return "out";
|
||||
}
|
||||
|
||||
if (parameter.IsIn)
|
||||
{
|
||||
return "in";
|
||||
}
|
||||
|
||||
if (parameter.ParameterType.IsByRef)
|
||||
{
|
||||
return "ref";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string GetParameterName(ParameterInfo parameter)
|
||||
{
|
||||
var prefix = GetPrefix(parameter);
|
||||
var parameterType = parameter.ParameterType;
|
||||
|
||||
if (parameterType.IsByRef && parameterType.GetElementType() is { } elementType)
|
||||
{
|
||||
parameterType = elementType;
|
||||
}
|
||||
|
||||
var typeName = TypeNameHelper.GetTypeDisplayName(parameterType, false, true);
|
||||
|
||||
return string.IsNullOrWhiteSpace(prefix) ? typeName : $"{prefix} {typeName}";
|
||||
}
|
||||
|
||||
private static string GetMethodName(ref MethodBase method, out bool isAsync)
|
||||
{
|
||||
var declaringType = method.DeclaringType;
|
||||
@ -270,9 +316,9 @@ internal static class ExceptionFormatter
|
||||
builder.Append(method.Name);
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
builder.Append('[');
|
||||
builder.Append('<');
|
||||
builder.Append(string.Join(",", method.GetGenericArguments().Select(t => t.Name)));
|
||||
builder.Append(']');
|
||||
builder.Append('>');
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
@ -281,7 +327,8 @@ internal static class ExceptionFormatter
|
||||
private static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType)
|
||||
{
|
||||
// https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L400-L455
|
||||
declaringType = method.DeclaringType ?? throw new ArgumentException("Method must have a declaring type.", nameof(method));
|
||||
declaringType = method.DeclaringType ??
|
||||
throw new ArgumentException("Method must have a declaring type.", nameof(method));
|
||||
|
||||
var parentType = declaringType.DeclaringType;
|
||||
if (parentType == null)
|
||||
|
216
src/Spectre.Console/Widgets/Exceptions/TypeNameHelper.cs
Normal file
216
src/Spectre.Console/Widgets/Exceptions/TypeNameHelper.cs
Normal file
@ -0,0 +1,216 @@
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal static class TypeNameHelper
|
||||
{
|
||||
// from https://github.com/benaadams/Ben.Demystifier/blob/main/src/Ben.Demystifier/TypeNameHelper.cs
|
||||
// which was adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
|
||||
public static readonly Dictionary<Type, string> BuiltInTypeNames = new Dictionary<Type, string>
|
||||
{
|
||||
{ typeof(void), "void" },
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(byte), "byte" },
|
||||
{ typeof(char), "char" },
|
||||
{ typeof(decimal), "decimal" },
|
||||
{ typeof(double), "double" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(long), "long" },
|
||||
{ typeof(object), "object" },
|
||||
{ typeof(sbyte), "sbyte" },
|
||||
{ typeof(short), "short" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(uint), "uint" },
|
||||
{ typeof(ulong), "ulong" },
|
||||
{ typeof(ushort), "ushort" },
|
||||
};
|
||||
|
||||
public static readonly Dictionary<string, string> FSharpTypeNames = new Dictionary<string, string>
|
||||
{
|
||||
{ "Unit", "void" },
|
||||
{ "FSharpOption", "Option" },
|
||||
{ "FSharpAsync", "Async" },
|
||||
{ "FSharpOption`1", "Option" },
|
||||
{ "FSharpAsync`1", "Async" },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Pretty print a type name.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="Type"/>.</param>
|
||||
/// <param name="fullName"><c>true</c> to print a fully qualified name.</param>
|
||||
/// <param name="includeGenericParameterNames"><c>true</c> to include generic parameter names.</param>
|
||||
/// <returns>The pretty printed type name.</returns>
|
||||
public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true,
|
||||
bool includeGenericParameterNames = false)
|
||||
{
|
||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a name of given generic type without '`'.
|
||||
/// </summary>
|
||||
public static string GetTypeNameForGenericType(Type type)
|
||||
{
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
throw new ArgumentException("The given type should be generic", nameof(type));
|
||||
}
|
||||
|
||||
var genericPartIndex = type.Name.IndexOf('`');
|
||||
|
||||
return (genericPartIndex >= 0) ? type.Name.Substring(0, genericPartIndex) : type.Name;
|
||||
}
|
||||
|
||||
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var underlyingType = Nullable.GetUnderlyingType(type);
|
||||
if (underlyingType != null)
|
||||
{
|
||||
ProcessType(builder, underlyingType, options);
|
||||
builder.Append('?');
|
||||
}
|
||||
else
|
||||
{
|
||||
var genericArguments = type.GetGenericArguments();
|
||||
ProcessGenericType(builder, type, genericArguments, genericArguments.Length, options);
|
||||
}
|
||||
}
|
||||
else if (type.IsArray)
|
||||
{
|
||||
ProcessArrayType(builder, type, options);
|
||||
}
|
||||
else if (BuiltInTypeNames.TryGetValue(type, out var builtInName))
|
||||
{
|
||||
builder.Append(builtInName);
|
||||
}
|
||||
else if (type.Namespace == nameof(System))
|
||||
{
|
||||
builder.Append(type.Name);
|
||||
}
|
||||
else if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
|
||||
&& FSharpTypeNames.TryGetValue(type.Name, out builtInName))
|
||||
{
|
||||
builder.Append(builtInName);
|
||||
}
|
||||
else if (type.IsGenericParameter)
|
||||
{
|
||||
if (options.IncludeGenericParameterNames)
|
||||
{
|
||||
builder.Append(type.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(options.FullName ? type.FullName ?? type.Name : type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||
{
|
||||
var innerType = type;
|
||||
while (innerType.IsArray)
|
||||
{
|
||||
if (innerType.GetElementType() is { } inner)
|
||||
{
|
||||
innerType = inner;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessType(builder, innerType, options);
|
||||
|
||||
while (type.IsArray)
|
||||
{
|
||||
builder.Append('[');
|
||||
builder.Append(',', type.GetArrayRank() - 1);
|
||||
builder.Append(']');
|
||||
if (type.GetElementType() is not { } elementType)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
type = elementType;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length,
|
||||
DisplayNameOptions options)
|
||||
{
|
||||
var offset = 0;
|
||||
if (type.IsNested && type.DeclaringType is not null)
|
||||
{
|
||||
offset = type.DeclaringType.GetGenericArguments().Length;
|
||||
}
|
||||
|
||||
if (options.FullName)
|
||||
{
|
||||
if (type.IsNested && type.DeclaringType is not null)
|
||||
{
|
||||
ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options);
|
||||
builder.Append('+');
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(type.Namespace))
|
||||
{
|
||||
builder.Append(type.Namespace);
|
||||
builder.Append('.');
|
||||
}
|
||||
}
|
||||
|
||||
var genericPartIndex = type.Name.IndexOf('`');
|
||||
if (genericPartIndex <= 0)
|
||||
{
|
||||
builder.Append(type.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll"
|
||||
&& FSharpTypeNames.TryGetValue(type.Name, out var builtInName))
|
||||
{
|
||||
builder.Append(builtInName);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(type.Name, 0, genericPartIndex);
|
||||
}
|
||||
|
||||
builder.Append('<');
|
||||
for (var i = offset; i < length; i++)
|
||||
{
|
||||
ProcessType(builder, genericArguments[i], options);
|
||||
if (i + 1 == length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Append(',');
|
||||
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
||||
{
|
||||
builder.Append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append('>');
|
||||
}
|
||||
|
||||
private struct DisplayNameOptions
|
||||
{
|
||||
public DisplayNameOptions(bool fullName, bool includeGenericParameterNames)
|
||||
{
|
||||
FullName = fullName;
|
||||
IncludeGenericParameterNames = includeGenericParameterNames;
|
||||
}
|
||||
|
||||
public bool FullName { get; }
|
||||
|
||||
public bool IncludeGenericParameterNames { get; }
|
||||
}
|
||||
}
|
@ -29,4 +29,10 @@ public static class TestExceptions
|
||||
throw new InvalidOperationException("Something threw!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<T> GenericMethodWithOutThatThrows<T>(out List<T> firstFewItems)
|
||||
{
|
||||
firstFewItems = new List<T>();
|
||||
throw new InvalidOperationException("Throwing!");
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
System.InvalidOperationException: Something threw!
|
||||
System.InvalidOperationException: Throwing!
|
||||
at Spectre.Console.Tests.Data.TestExceptions.GenericMethodThatThrows[T0,T1,TRet](Nullable`1 number) in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Data.TestExceptions.ThrowWithGenericInnerException() in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Data.TestExceptions.ThrowWithGenericInnerException() in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exceptions_With_Generic_Type_Parameters_In_Callsite_As_Expected>b__4_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
at bool Spectre.Console.Tests.Data.TestExceptions.GenericMethodThatThrows<T0,T1,TRet>(int? number) in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Data.TestExceptions.ThrowWithGenericInnerException() in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Data.TestExceptions.ThrowWithGenericInnerException() in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exceptions_With_Generic_Type_Parameters_In_Callsite_As_Expected>b__4_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
|
@ -1,4 +1,4 @@
|
||||
System.InvalidOperationException: Throwing!
|
||||
at Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception>b__0_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
at bool Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(int? number) in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception>b__0_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
|
@ -1,7 +1,7 @@
|
||||
System.InvalidOperationException: Something threw!
|
||||
System.InvalidOperationException: Throwing!
|
||||
at Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Data.TestExceptions.ThrowWithInnerException() in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Data.TestExceptions.ThrowWithInnerException() in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Inner_Exception>b__3_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
at bool Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(int? number) in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Data.TestExceptions.ThrowWithInnerException() in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Data.TestExceptions.ThrowWithInnerException() in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Inner_Exception>b__3_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
|
@ -0,0 +1,4 @@
|
||||
InvalidOperationException: Throwing!
|
||||
at List<T> Spectre.Console.Tests.Data.TestExceptions.GenericMethodWithOutThatThrows<T>(out List<T> firstFewItems) in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Output_Param>b__5_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
@ -1,4 +1,4 @@
|
||||
System.InvalidOperationException: Throwing!
|
||||
at MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn
|
||||
at <Should_Write_Exception_With_Shortened_Methods>b__2_0() in /xyz/ExceptionTests.cs:nn
|
||||
at GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
at bool MethodThatThrows(int? number) in /xyz/Exceptions.cs:nn
|
||||
at void <Should_Write_Exception_With_Shortened_Methods>b__2_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
|
@ -1,4 +1,4 @@
|
||||
InvalidOperationException: Throwing!
|
||||
at Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Shortened_Types>b__1_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
at bool Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(int? number) in /xyz/Exceptions.cs:nn
|
||||
at void Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Shortened_Types>b__1_0() in /xyz/ExceptionTests.cs:nn
|
||||
at Exception Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn
|
||||
|
@ -79,6 +79,21 @@ public sealed class ExceptionTests
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("OutParam")]
|
||||
public Task Should_Write_Exception_With_Output_Param()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole().Width(1024);
|
||||
var dex = GetException(() => TestExceptions.GenericMethodWithOutThatThrows<int>(out _));
|
||||
|
||||
// When
|
||||
var result = console.WriteNormalizedException(dex, ExceptionFormats.ShortenTypes);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
|
||||
public static Exception GetException(Action action)
|
||||
{
|
||||
try
|
||||
|
Loading…
x
Reference in New Issue
Block a user