Merge branch 'master' of https://github.com/benaadams/Ben.Demystifier
This commit is contained in:
commit
5fbf64f31b
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,6 +23,7 @@ bld/
|
|||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
|
|||||||
@ -13,7 +13,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier.Test", "tes
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{455921D3-DD54-4355-85CF-F4009DF2AB70}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{455921D3-DD54-4355-85CF-F4009DF2AB70}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StackTrace", "sample\StackTrace\StackTrace.csproj", "{E161FC12-53C2-47CD-A5FC-3684B86723A9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackTrace", "sample\StackTrace\StackTrace.csproj", "{E161FC12-53C2-47CD-A5FC-3684B86723A9}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5937ACDF-0059-488E-9604-D84689C72933}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
appveyor.yml = appveyor.yml
|
||||||
|
build.ps1 = build.ps1
|
||||||
|
directory.build.props = directory.build.props
|
||||||
|
README.md = README.md
|
||||||
|
version.json = version.json
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ben.Demystifier.Benchmarks", "test\Ben.Demystifier.Benchmarks\Ben.Demystifier.Benchmarks.csproj", "{EF5557DF-C48E-4999-846C-D99A92E86373}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -33,6 +44,10 @@ Global
|
|||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E161FC12-53C2-47CD-A5FC-3684B86723A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EF5557DF-C48E-4999-846C-D99A92E86373}.Debug|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EF5557DF-C48E-4999-846C-D99A92E86373}.Debug|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EF5557DF-C48E-4999-846C-D99A92E86373}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -41,6 +56,7 @@ Global
|
|||||||
{5410A056-89AB-4912-BD1E-A63616AD91D0} = {A2FCCAAC-BE90-4F7E-B95F-A72D46DDD6B3}
|
{5410A056-89AB-4912-BD1E-A63616AD91D0} = {A2FCCAAC-BE90-4F7E-B95F-A72D46DDD6B3}
|
||||||
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
|
{B9E150B0-AEEB-4D98-8BE1-92C1296699A2} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
|
||||||
{E161FC12-53C2-47CD-A5FC-3684B86723A9} = {455921D3-DD54-4355-85CF-F4009DF2AB70}
|
{E161FC12-53C2-47CD-A5FC-3684B86723A9} = {455921D3-DD54-4355-85CF-F4009DF2AB70}
|
||||||
|
{EF5557DF-C48E-4999-846C-D99A92E86373} = {59CA6310-4AA5-4093-95D4-472B94DC0CD4}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {841B7D5F-E810-4F94-A529-002C7E075216}
|
SolutionGuid = {841B7D5F-E810-4F94-A529-002C7E075216}
|
||||||
|
|||||||
@ -158,3 +158,11 @@ Which is far less helpful, and close to jibberish in places
|
|||||||
* **return types**
|
* **return types**
|
||||||
|
|
||||||
Skipped entirely from method signature
|
Skipped entirely from method signature
|
||||||
|
|
||||||
|
### Benchmarks
|
||||||
|
|
||||||
|
To run benchmarks from the repository root:
|
||||||
|
```
|
||||||
|
dotnet run -p .\test\Ben.Demystifier.Benchmarks\ -c Release -f netcoreapp2.0 All
|
||||||
|
```
|
||||||
|
<sub>Note: we're only kicking off via `netcoreapp2.0`, benchmarks will run for all configured platforms like `net462`.</sub>
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
image: Visual Studio 2017
|
image: Visual Studio 2017
|
||||||
|
|
||||||
shallow_clone: true
|
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
@ -22,7 +20,7 @@ nuget:
|
|||||||
disable_publish_on_pr: true
|
disable_publish_on_pr: true
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- ps: .\build.ps1 -target appveyor -buildAssemblyVersion ($env:BuildVersion + $env:APPVEYOR_BUILD_NUMBER) -buildSemanticVersion ($env:BuildSemanticVersion + $env:APPVEYOR_BUILD_NUMBER)
|
- ps: .\build.ps1 -target appveyor
|
||||||
|
|
||||||
test: off
|
test: off
|
||||||
|
|
||||||
@ -31,4 +29,4 @@ deploy: off
|
|||||||
artifacts:
|
artifacts:
|
||||||
- path: artifacts/build
|
- path: artifacts/build
|
||||||
- path: artifacts/packages
|
- path: artifacts/packages
|
||||||
- path: artifacts/test
|
- path: artifacts/test
|
||||||
|
|||||||
@ -22,11 +22,6 @@ function Exec
|
|||||||
|
|
||||||
if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse }
|
if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse }
|
||||||
|
|
||||||
exec { & dotnet restore }
|
|
||||||
|
|
||||||
$revision = @{ $true = $env:APPVEYOR_BUILD_NUMBER; $false = 1 }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL];
|
|
||||||
$revision = "{0:D4}" -f [convert]::ToInt32($revision, 10)
|
|
||||||
|
|
||||||
exec { & dotnet test .\test\Ben.Demystifier.Test -c Release }
|
exec { & dotnet test .\test\Ben.Demystifier.Test -c Release }
|
||||||
|
|
||||||
exec { & dotnet pack .\src\Ben.Demystifier -c Release -o .\artifacts --version-suffix=$revision }
|
exec { & dotnet pack .\src\Ben.Demystifier -c Release -o .\artifacts }
|
||||||
|
|||||||
5
directory.build.props
Normal file
5
directory.build.props
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<LangVersion>7.2</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@ -5,14 +5,6 @@
|
|||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<LangVersion>7.2</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<LangVersion>7.2</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Ben.Demystifier\Ben.Demystifier.csproj" />
|
<ProjectReference Include="..\..\src\Ben.Demystifier\Ben.Demystifier.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -9,17 +9,19 @@
|
|||||||
<PackageProjectUrl>https://github.com/benaadams/Ben.Demystifier</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/benaadams/Ben.Demystifier</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/benaadams/Ben.Demystifier/blob/master/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/benaadams/Ben.Demystifier/blob/master/LICENSE</PackageLicenseUrl>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
|
||||||
<IncludeSource>true</IncludeSource>
|
<IncludeSource>true</IncludeSource>
|
||||||
<Version>0.0.7</Version>
|
<DebugType>embedded</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
|
||||||
<LangVersion>7.1</LangVersion>
|
<SignAssembly>true</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Nerdbank.GitVersioning" Version="2.1.17" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.7.6" PrivateAssets="all" />
|
||||||
<PackageReference Include="System.Reflection.Metadata">
|
<PackageReference Include="System.Reflection.Metadata">
|
||||||
<Version>1.5.0</Version>
|
<Version>1.5.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@ -19,7 +19,6 @@ namespace System.Diagnostics
|
|||||||
{
|
{
|
||||||
public partial class EnhancedStackTrace
|
public partial class EnhancedStackTrace
|
||||||
{
|
{
|
||||||
|
|
||||||
private static List<EnhancedStackFrame> GetFrames(Exception exception)
|
private static List<EnhancedStackFrame> GetFrames(Exception exception)
|
||||||
{
|
{
|
||||||
if (exception == null)
|
if (exception == null)
|
||||||
@ -516,7 +515,7 @@ namespace System.Diagnostics
|
|||||||
{
|
{
|
||||||
foreach (var attrib in attribs)
|
foreach (var attrib in attribs)
|
||||||
{
|
{
|
||||||
if (attrib is Attribute att && att.GetType().Namespace == "System.Runtime.CompilerServices" && att.GetType().Name == "IsReadOnlyAttribute")
|
if (attrib is Attribute att && att.GetType().IsReadOnlyAttribute())
|
||||||
{
|
{
|
||||||
return "in";
|
return "in";
|
||||||
}
|
}
|
||||||
@ -579,6 +578,32 @@ namespace System.Diagnostics
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string name, Type parameterType)
|
private static ResolvedParameter GetValueTupleParameter(IList<string> tupleNames, string prefix, string name, Type parameterType)
|
||||||
|
{
|
||||||
|
string typeName;
|
||||||
|
|
||||||
|
if (parameterType.IsValueTuple())
|
||||||
|
{
|
||||||
|
typeName = GetValueTupleParameterName(tupleNames, parameterType);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Need to unwrap the first generic argument first.
|
||||||
|
var genericTypeName = TypeNameHelper.GetTypeNameForGenericType(parameterType);
|
||||||
|
var valueTupleFullName = GetValueTupleParameterName(tupleNames, parameterType.GetGenericArguments()[0]);
|
||||||
|
typeName = $"{genericTypeName}<{valueTupleFullName}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResolvedParameter
|
||||||
|
{
|
||||||
|
Prefix = prefix,
|
||||||
|
Name = name,
|
||||||
|
Type = typeName,
|
||||||
|
ResolvedType = parameterType,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetValueTupleParameterName(IList<string> tupleNames, Type parameterType)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append("(");
|
sb.Append("(");
|
||||||
@ -608,14 +633,7 @@ namespace System.Diagnostics
|
|||||||
}
|
}
|
||||||
|
|
||||||
sb.Append(")");
|
sb.Append(")");
|
||||||
|
return sb.ToString();
|
||||||
return new ResolvedParameter
|
|
||||||
{
|
|
||||||
Prefix = prefix,
|
|
||||||
Name = name,
|
|
||||||
Type = sb.ToString(),
|
|
||||||
ResolvedType = parameterType,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ShowInStackTrace(MethodBase method)
|
private static bool ShowInStackTrace(MethodBase method)
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic.Enumerable;
|
using System.Collections.Generic.Enumerable;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace System.Diagnostics
|
namespace System.Diagnostics
|
||||||
@ -104,21 +105,9 @@ namespace System.Diagnostics
|
|||||||
var filePath = frame.GetFileName();
|
var filePath = frame.GetFileName();
|
||||||
if (!string.IsNullOrEmpty(filePath))
|
if (!string.IsNullOrEmpty(filePath))
|
||||||
{
|
{
|
||||||
try
|
sb.Append(" in ");
|
||||||
{
|
sb.Append(TryGetFullPath(filePath));
|
||||||
sb.Append(" in ");
|
|
||||||
var uri = new Uri(filePath);
|
|
||||||
if (uri.IsFile)
|
|
||||||
{
|
|
||||||
sb.Append(System.IO.Path.GetFullPath(filePath));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{ }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineNo = frame.GetFileLineNumber();
|
var lineNo = frame.GetFileLineNumber();
|
||||||
@ -133,5 +122,27 @@ namespace System.Diagnostics
|
|||||||
EnumerableIList<EnhancedStackFrame> GetEnumerator() => EnumerableIList.Create(_frames);
|
EnumerableIList<EnhancedStackFrame> GetEnumerator() => EnumerableIList.Create(_frames);
|
||||||
IEnumerator<EnhancedStackFrame> IEnumerable<EnhancedStackFrame>.GetEnumerator() => _frames.GetEnumerator();
|
IEnumerator<EnhancedStackFrame> IEnumerable<EnhancedStackFrame>.GetEnumerator() => _frames.GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _frames.GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => _frames.GetEnumerator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to convert a given <paramref name="filePath"/> to a full path.
|
||||||
|
/// Returns original value if the conversion isn't possible or a given path is relative.
|
||||||
|
/// </summary>
|
||||||
|
public static string TryGetFullPath(string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = new Uri(filePath);
|
||||||
|
if (uri.IsFile)
|
||||||
|
{
|
||||||
|
return uri.AbsolutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri.ToString();
|
||||||
|
}
|
||||||
|
catch (ArgumentException) { }
|
||||||
|
catch (UriFormatException) { }
|
||||||
|
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,54 @@
|
|||||||
// Copyright (c) Ben A Adams. All rights reserved.
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic.Enumerable;
|
using System.Collections.Generic.Enumerable;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace System.Diagnostics
|
namespace System.Diagnostics
|
||||||
{
|
{
|
||||||
|
/// <nodoc />
|
||||||
public static class ExceptionExtentions
|
public static class ExceptionExtentions
|
||||||
{
|
{
|
||||||
private static readonly FieldInfo stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
|
private static readonly FieldInfo stackTraceString = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
|
||||||
public static T Demystify<T>(this T exception) where T : Exception
|
public static T Demystify<T>(this T exception) where T : Exception
|
||||||
|
=> Demystify(exception, originalStacksTracker: null);
|
||||||
|
|
||||||
|
private static string GetStackTracesString(this Exception exception)
|
||||||
|
=> (string)stackTraceString.GetValue(exception);
|
||||||
|
|
||||||
|
private static void SetStackTracesString(this Exception exception, string value)
|
||||||
|
=> stackTraceString.SetValue(exception, value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Demystifies the given <paramref name="exception"/> and tracks the original stack traces for the whole exception tree.
|
||||||
|
/// </summary>
|
||||||
|
private static T Demystify<T>(this T exception, Dictionary<Exception, string> originalStacksTracker) where T : Exception
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (originalStacksTracker?.ContainsKey(exception) == false)
|
||||||
|
{
|
||||||
|
originalStacksTracker[exception] = exception.GetStackTracesString();
|
||||||
|
}
|
||||||
|
|
||||||
var stackTrace = new EnhancedStackTrace(exception);
|
var stackTrace = new EnhancedStackTrace(exception);
|
||||||
|
|
||||||
if (stackTrace.FrameCount > 0)
|
if (stackTrace.FrameCount > 0)
|
||||||
{
|
{
|
||||||
stackTraceString.SetValue(exception, stackTrace.ToString());
|
exception.SetStackTracesString(stackTrace.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception is AggregateException aggEx)
|
if (exception is AggregateException aggEx)
|
||||||
{
|
{
|
||||||
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
|
foreach (var ex in EnumerableIList.Create(aggEx.InnerExceptions))
|
||||||
{
|
{
|
||||||
ex.Demystify();
|
ex.Demystify(originalStacksTracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exception.InnerException?.Demystify();
|
exception.InnerException?.Demystify(originalStacksTracker);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -38,5 +57,39 @@ namespace System.Diagnostics
|
|||||||
|
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets demystified string representation of the <paramref name="exception"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="Demystify{T}"/> method mutates the exception instance that can cause
|
||||||
|
/// issues if a system relies on the stack trace be in the specific form.
|
||||||
|
/// Unlike <see cref="Demystify{T}"/> this method is pure. It calls <see cref="Demystify{T}"/> first,
|
||||||
|
/// computes a demystified string representation and then restores the original state of the exception back.
|
||||||
|
/// </remarks>
|
||||||
|
[Contracts.Pure]
|
||||||
|
public static string ToStringDemystified(this Exception exception)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var originalStacks = new Dictionary<Exception, string>();
|
||||||
|
exception.Demystify(originalStacks);
|
||||||
|
|
||||||
|
var result = exception.ToString();
|
||||||
|
|
||||||
|
foreach (var kvp in originalStacks)
|
||||||
|
{
|
||||||
|
kvp.Key.SetStackTracesString(kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Processing exceptions shouldn't throw exceptions; if it fails
|
||||||
|
}
|
||||||
|
|
||||||
|
return exception.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/Ben.Demystifier/Internal/ReflectionHelper.cs
Normal file
30
src/Ben.Demystifier/Internal/ReflectionHelper.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) Ben A Adams. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
namespace System.Diagnostics.Internal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A helper class that contains utilities methods for dealing with reflection.
|
||||||
|
/// </summary>
|
||||||
|
public static class ReflectionHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if <paramref name="type"/> is <code>System.Runtime.CompilerServices.IsReadOnlyAttribute</code>.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsReadOnlyAttribute(this Type type)
|
||||||
|
{
|
||||||
|
return type.Namespace == "System.Runtime.CompilerServices" && type.Name == "IsReadOnlyAttribute";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the <paramref name="type"/> is a value tuple type.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsValueTuple(this Type type)
|
||||||
|
{
|
||||||
|
return type.Namespace == "System" && type.Name.Contains("ValueTuple`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,6 +43,22 @@ namespace System.Diagnostics
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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('`');
|
||||||
|
Debug.Assert(genericPartIndex >= 0);
|
||||||
|
|
||||||
|
return type.Name.Substring(0, genericPartIndex);
|
||||||
|
}
|
||||||
|
|
||||||
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||||
{
|
{
|
||||||
if (type.IsGenericType)
|
if (type.IsGenericType)
|
||||||
|
|||||||
BIN
src/Ben.Demystifier/key.snk
Normal file
BIN
src/Ben.Demystifier/key.snk
Normal file
Binary file not shown.
@ -0,0 +1,11 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>netcoreapp2.0;net462</TargetFrameworks>
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Ben.Demystifier\Ben.Demystifier.csproj" />
|
||||||
|
<PackageReference Include="BenchmarkDotNet" Version="0.10.12" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
18
test/Ben.Demystifier.Benchmarks/Exceptions.cs
Normal file
18
test/Ben.Demystifier.Benchmarks/Exceptions.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Attributes.Jobs;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Benchmarks
|
||||||
|
{
|
||||||
|
[ClrJob, CoreJob]
|
||||||
|
[Config(typeof(Config))]
|
||||||
|
public class ExceptionTests
|
||||||
|
{
|
||||||
|
[Benchmark(Baseline = true, Description = ".ToString()")]
|
||||||
|
public string Baseline() => new Exception().ToString();
|
||||||
|
|
||||||
|
[Benchmark(Description = "Demystify().ToString()")]
|
||||||
|
public string Demystify() => new Exception().Demystify().ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
48
test/Ben.Demystifier.Benchmarks/Program.cs
Normal file
48
test/Ben.Demystifier.Benchmarks/Program.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using BenchmarkDotNet.Configs;
|
||||||
|
using BenchmarkDotNet.Diagnosers;
|
||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Benchmarks
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
private const string BenchmarkSuffix = "Tests";
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var benchmarks = Assembly.GetEntryAssembly()
|
||||||
|
.DefinedTypes.Where(t => t.Name.EndsWith(BenchmarkSuffix))
|
||||||
|
.ToDictionary(t => t.Name.Substring(0, t.Name.Length - BenchmarkSuffix.Length), t => t, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (args.Length > 0 && args[0].Equals("all", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Running full benchmarks suite");
|
||||||
|
benchmarks.Select(pair => pair.Value).ToList().ForEach(action => BenchmarkRunner.Run(action));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 0 || !benchmarks.ContainsKey(args[0]))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Please, select benchmark, list of available:");
|
||||||
|
benchmarks
|
||||||
|
.Select(pair => pair.Key)
|
||||||
|
.ToList()
|
||||||
|
.ForEach(Console.WriteLine);
|
||||||
|
Console.WriteLine("All");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BenchmarkRunner.Run(benchmarks[args[0]]);
|
||||||
|
|
||||||
|
Console.Read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Config : ManualConfig
|
||||||
|
{
|
||||||
|
public Config() => Add(new MemoryDiagnoser());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,12 +2,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Demystify
|
namespace Ben.Demystifier.Test
|
||||||
{
|
{
|
||||||
public class AggregateException
|
public class AggregateException
|
||||||
{
|
{
|
||||||
@ -34,11 +33,11 @@ namespace Demystify
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var stackTrace = demystifiedException.ToString();
|
var stackTrace = demystifiedException.ToString();
|
||||||
stackTrace = ReplaceLineEndings.Replace(stackTrace, "");
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
var trace = string.Join("", stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
|
var trace = string.Join("", stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
// Remove items that vary between test runners
|
// Remove items that vary between test runners
|
||||||
.Where(s =>
|
.Where(s =>
|
||||||
s != " at Task Demystify.DynamicCompilation.DoesNotPreventStackTrace()+() => { }" &&
|
s != " at Task Ben.Demystifier.Test.DynamicCompilation.DoesNotPreventStackTrace()+() => { }" &&
|
||||||
!s.Contains("System.Threading.Tasks.Task.WaitAll")
|
!s.Contains("System.Threading.Tasks.Task.WaitAll")
|
||||||
)
|
)
|
||||||
.Skip(1)
|
.Skip(1)
|
||||||
@ -47,19 +46,19 @@ namespace Demystify
|
|||||||
.Replace("<---", "");
|
.Replace("<---", "");
|
||||||
|
|
||||||
var expected = string.Join("", new[] {
|
var expected = string.Join("", new[] {
|
||||||
" at async Task Demystify.AggregateException.Throw1()",
|
" at async Task Ben.Demystifier.Test.AggregateException.Throw1()",
|
||||||
" at async void Demystify.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
|
" at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
|
||||||
" --- End of inner exception stack trace ---",
|
" --- End of inner exception stack trace ---",
|
||||||
" at void Demystify.AggregateException.DemystifiesAggregateExceptions()",
|
" at void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()",
|
||||||
"---> (Inner Exception #0) System.ArgumentException: Value does not fall within the expected range.",
|
"---> (Inner Exception #0) System.ArgumentException: Value does not fall within the expected range.",
|
||||||
" at async Task Demystify.AggregateException.Throw1()",
|
" at async Task Ben.Demystifier.Test.AggregateException.Throw1()",
|
||||||
" at async void Demystify.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
|
" at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
|
||||||
"---> (Inner Exception #1) System.NullReferenceException: Object reference not set to an instance of an object.",
|
"---> (Inner Exception #1) System.NullReferenceException: Object reference not set to an instance of an object.",
|
||||||
" at async Task Demystify.AggregateException.Throw2()",
|
" at async Task Ben.Demystifier.Test.AggregateException.Throw2()",
|
||||||
" at async void Demystify.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
|
" at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
|
||||||
"---> (Inner Exception #2) System.InvalidOperationException: Operation is not valid due to the current state of the object.",
|
"---> (Inner Exception #2) System.InvalidOperationException: Operation is not valid due to the current state of the object.",
|
||||||
" at async Task Demystify.AggregateException.Throw3()",
|
" at async Task Ben.Demystifier.Test.AggregateException.Throw3()",
|
||||||
" at async void Demystify.AggregateException.DemystifiesAggregateExceptions()+(?) => { }"});
|
" at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }"});
|
||||||
|
|
||||||
Assert.Equal(expected, trace);
|
Assert.Equal(expected, trace);
|
||||||
}
|
}
|
||||||
@ -81,8 +80,5 @@ namespace Demystify
|
|||||||
await Task.Delay(1).ConfigureAwait(false);
|
await Task.Delay(1).ConfigureAwait(false);
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Regex ReplaceLineEndings = new Regex(" in [^\n\r]+");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.0;net46</TargetFrameworks>
|
||||||
|
|
||||||
@ -7,9 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170810-02" />
|
<PackageReference Include="xunit" Version="2.3.1" />
|
||||||
<PackageReference Include="xunit" Version="2.2.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Demystify
|
namespace Ben.Demystifier.Test
|
||||||
{
|
{
|
||||||
public class DynamicCompilation
|
public class DynamicCompilation
|
||||||
{
|
{
|
||||||
@ -37,12 +37,12 @@ namespace Demystify
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var stackTrace = demystifiedException.ToString();
|
var stackTrace = demystifiedException.ToString();
|
||||||
stackTrace = ReplaceLineEndings.Replace(stackTrace, "");
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
var trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
|
var trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
|
||||||
// Remove items that vary between test runners
|
// Remove items that vary between test runners
|
||||||
.Where(s =>
|
.Where(s =>
|
||||||
s != " at void System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, object state)" &&
|
s != " at void System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, object state)" &&
|
||||||
s != " at Task Demystify.DynamicCompilation.DoesNotPreventStackTrace()+() => { }"
|
s != " at Task Ben.Demystifier.Test.DynamicCompilation.DoesNotPreventStackTrace()+() => { }"
|
||||||
)
|
)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
@ -50,10 +50,8 @@ namespace Demystify
|
|||||||
new[] {
|
new[] {
|
||||||
"System.ArgumentException: Message",
|
"System.ArgumentException: Message",
|
||||||
" at void lambda_method(Closure)",
|
" at void lambda_method(Closure)",
|
||||||
" at async Task Demystify.DynamicCompilation.DoesNotPreventStackTrace()"},
|
" at async Task Ben.Demystifier.Test.DynamicCompilation.DoesNotPreventStackTrace()"},
|
||||||
trace);
|
trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Regex ReplaceLineEndings = new Regex(" in [^\n\r]+");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
test/Ben.Demystifier.Test/EnhancedStackTraceTests.cs
Normal file
31
test/Ben.Demystifier.Test/EnhancedStackTraceTests.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Test
|
||||||
|
{
|
||||||
|
public class EnhancedStackTraceTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(@"file://Sources\MySolution\Foo.cs", @"/MySolution/Foo.cs")]
|
||||||
|
[InlineData(@"d:\Public\Src\Foo.cs", @"d:/Public/Src/Foo.cs")]
|
||||||
|
// To be deterministic, the C# compiler can take a /pathmap command line option.
|
||||||
|
// This option force the compiler to emit the same bits even when their built from the
|
||||||
|
// differrent locations.
|
||||||
|
// The binaries built with the pathmap usually don't have an absolute path,
|
||||||
|
// but have some prefix like \.\.
|
||||||
|
// This test case makes sure that EhancedStackTrace can deal with such kind of paths.
|
||||||
|
[InlineData(@"\.\Public\Src\Foo.cs", @"/./Public/Src/Foo.cs")]
|
||||||
|
public void RelativePathIsConvertedToAnAbsolutePath(string original, string expected)
|
||||||
|
{
|
||||||
|
var converted = EnhancedStackTrace.TryGetFullPath(original);
|
||||||
|
Assert.Equal(expected, NormalizePath(converted));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used in tests to avoid platform-specific issues.
|
||||||
|
private static string NormalizePath(string path)
|
||||||
|
=> path.Replace("\\", "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
14
test/Ben.Demystifier.Test/LineEndingsHelper.cs
Normal file
14
test/Ben.Demystifier.Test/LineEndingsHelper.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Test
|
||||||
|
{
|
||||||
|
internal static class LineEndingsHelper
|
||||||
|
{
|
||||||
|
private static readonly Regex ReplaceLineEndings = new Regex(" in [^\n\r]+");
|
||||||
|
|
||||||
|
public static string RemoveLineEndings(string original)
|
||||||
|
{
|
||||||
|
return ReplaceLineEndings.Replace(original, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Demystify
|
namespace Ben.Demystifier.Test
|
||||||
{
|
{
|
||||||
public class MixedStack
|
public class MixedStack
|
||||||
{
|
{
|
||||||
@ -57,16 +57,16 @@ namespace Demystify
|
|||||||
|
|
||||||
static List<string> ExpectedCallStack = new List<string>()
|
static List<string> ExpectedCallStack = new List<string>()
|
||||||
{
|
{
|
||||||
"IEnumerable<string> Demystify.MixedStack.Iterator()+MoveNext()",
|
"IEnumerable<string> Ben.Demystifier.Test.MixedStack.Iterator()+MoveNext()",
|
||||||
"string string.Join(string separator, IEnumerable<string> values)",
|
"string string.Join(string separator, IEnumerable<string> values)",
|
||||||
"string Demystify.MixedStack+GenericClass<T>.GenericMethod<V>(ref V value)",
|
"string Ben.Demystifier.Test.MixedStack+GenericClass<T>.GenericMethod<V>(ref V value)",
|
||||||
"async Task<string> Demystify.MixedStack.MethodAsync(int value)",
|
"async Task<string> Ben.Demystifier.Test.MixedStack.MethodAsync(int value)",
|
||||||
"async Task<string> Demystify.MixedStack.MethodAsync<TValue>(TValue value)",
|
"async Task<string> Ben.Demystifier.Test.MixedStack.MethodAsync<TValue>(TValue value)",
|
||||||
"(string val, bool) Demystify.MixedStack.Method(string value)",
|
"(string val, bool) Ben.Demystifier.Test.MixedStack.Method(string value)",
|
||||||
"ref string Demystify.MixedStack.RefMethod(string value)",
|
"ref string Ben.Demystifier.Test.MixedStack.RefMethod(string value)",
|
||||||
"(string val, bool) Demystify.MixedStack.s_func(string s, bool b)",
|
"(string val, bool) Ben.Demystifier.Test.MixedStack.s_func(string s, bool b)",
|
||||||
"void Demystify.MixedStack.s_action(string s, bool b)",
|
"void Ben.Demystifier.Test.MixedStack.s_action(string s, bool b)",
|
||||||
"void Demystify.MixedStack.Start((string val, bool) param)"
|
"void Ben.Demystifier.Test.MixedStack.Start((string val, bool) param)"
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Demystify
|
namespace Ben.Demystifier.Test
|
||||||
{
|
{
|
||||||
public class NonThrownException
|
public class NonThrownException
|
||||||
{
|
{
|
||||||
@ -28,14 +28,14 @@ namespace Demystify
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var stackTrace = demystifiedException.ToString();
|
var stackTrace = demystifiedException.ToString();
|
||||||
stackTrace = ReplaceLineEndings.Replace(stackTrace, "");
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
var trace = stackTrace.Split(new[]{Environment.NewLine}, StringSplitOptions.None);
|
var trace = stackTrace.Split(new[]{Environment.NewLine}, StringSplitOptions.None);
|
||||||
|
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
new[] {
|
new[] {
|
||||||
"System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
|
"System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
|
||||||
" at Task Demystify.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
|
" at Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
|
||||||
" at async Task Demystify.NonThrownException.DoesNotPreventThrowStackTrace()",
|
" at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()",
|
||||||
" --- End of inner exception stack trace ---"},
|
" --- End of inner exception stack trace ---"},
|
||||||
trace);
|
trace);
|
||||||
|
|
||||||
@ -51,16 +51,16 @@ namespace Demystify
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
stackTrace = demystifiedException.ToString();
|
stackTrace = demystifiedException.ToString();
|
||||||
stackTrace = ReplaceLineEndings.Replace(stackTrace, "");
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||||
|
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
new[] {
|
new[] {
|
||||||
"System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
|
"System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
|
||||||
" at Task Demystify.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
|
" at Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
|
||||||
" at async Task Demystify.NonThrownException.DoesNotPreventThrowStackTrace()",
|
" at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()",
|
||||||
" --- End of inner exception stack trace ---",
|
" --- End of inner exception stack trace ---",
|
||||||
" at async Task Demystify.NonThrownException.DoesNotPreventThrowStackTrace()"
|
" at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()"
|
||||||
},
|
},
|
||||||
trace);
|
trace);
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ namespace Demystify
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var stackTrace = est.ToString();
|
var stackTrace = est.ToString();
|
||||||
stackTrace = ReplaceLineEndings.Replace(stackTrace, "");
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
var trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
|
var trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
|
||||||
// Remove Full framework entries
|
// Remove Full framework entries
|
||||||
.Where(s => !s.StartsWith(" at bool System.Threading._ThreadPoolWaitCallbac") &&
|
.Where(s => !s.StartsWith(" at bool System.Threading._ThreadPoolWaitCallbac") &&
|
||||||
@ -88,7 +88,5 @@ namespace Demystify
|
|||||||
" at bool System.Threading.ThreadPoolWorkQueue.Dispatch()"},
|
" at bool System.Threading.ThreadPoolWorkQueue.Dispatch()"},
|
||||||
trace);
|
trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Regex ReplaceLineEndings = new Regex(" in [^\n\r]+");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
test/Ben.Demystifier.Test/ReflectionHelperTests.cs
Normal file
27
test/Ben.Demystifier.Test/ReflectionHelperTests.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.Internal;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Test
|
||||||
|
{
|
||||||
|
public class ReflectionHelperTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IsValueTupleReturnsTrueForTupleWith1Element()
|
||||||
|
{
|
||||||
|
Assert.True(typeof(ValueTuple<int>).IsValueTuple());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsValueTupleReturnsTrueForTupleWith1ElementWithOpenedType()
|
||||||
|
{
|
||||||
|
Assert.True(typeof(ValueTuple<>).IsValueTuple());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsValueTupleReturnsTrueForTupleWith6ElementsWithOpenedType()
|
||||||
|
{
|
||||||
|
Assert.True(typeof(ValueTuple<,,,,,>).IsValueTuple());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
test/Ben.Demystifier.Test/ToDemystifiedStringTests.cs
Normal file
48
test/Ben.Demystifier.Test/ToDemystifiedStringTests.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Test
|
||||||
|
{
|
||||||
|
public sealed class ToDemystifiedStringTests
|
||||||
|
{
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
public ToDemystifiedStringTests(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DemystifyShouldNotAffectTheOriginalStackTrace()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SimpleMethodThatThrows().Wait();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
var original = e.ToString();
|
||||||
|
var stringDemystified = e.ToStringDemystified();
|
||||||
|
|
||||||
|
_output.WriteLine("Demystified: ");
|
||||||
|
_output.WriteLine(stringDemystified);
|
||||||
|
|
||||||
|
_output.WriteLine("Original: ");
|
||||||
|
var afterDemystified = e.ToString();
|
||||||
|
_output.WriteLine(afterDemystified);
|
||||||
|
|
||||||
|
Assert.Equal(original, afterDemystified);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task SimpleMethodThatThrows()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
test/Ben.Demystifier.Test/TuplesTests.cs
Normal file
76
test/Ben.Demystifier.Test/TuplesTests.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ben.Demystifier.Test
|
||||||
|
{
|
||||||
|
public class TuplesTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DemistifiesAsyncMethodWithTuples()
|
||||||
|
{
|
||||||
|
Exception demystifiedException = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AsyncThatReturnsTuple().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
demystifiedException = ex.Demystify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var stackTrace = demystifiedException.ToString();
|
||||||
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
|
var trace = string.Join("", stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
|
||||||
|
|
||||||
|
var expected = string.Join("", new[] {
|
||||||
|
"System.ArgumentException: Value does not fall within the expected range.",
|
||||||
|
" at async Task<(int left, int right)> Ben.Demystifier.Test.TuplesTests.AsyncThatReturnsTuple()",
|
||||||
|
" at void Ben.Demystifier.Test.TuplesTests.DemistifiesAsyncMethodWithTuples()"});
|
||||||
|
|
||||||
|
Assert.Equal(expected, trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DemistifiesListOfTuples()
|
||||||
|
{
|
||||||
|
Exception demystifiedException = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ListOfTuples();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
demystifiedException = ex.Demystify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var stackTrace = demystifiedException.ToString();
|
||||||
|
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
|
||||||
|
var trace = string.Join("", stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
|
||||||
|
|
||||||
|
var expected = string.Join("", new[] {
|
||||||
|
"System.ArgumentException: Value does not fall within the expected range.",
|
||||||
|
" at List<(int left, int right)> Ben.Demystifier.Test.TuplesTests.ListOfTuples()",
|
||||||
|
" at void Ben.Demystifier.Test.TuplesTests.DemistifiesListOfTuples()"});
|
||||||
|
|
||||||
|
Assert.Equal(expected, trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<(int left, int right)> AsyncThatReturnsTuple()
|
||||||
|
{
|
||||||
|
await Task.Delay(1).ConfigureAwait(false);
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<(int left, int right)> ListOfTuples()
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
version.json
Normal file
17
version.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": "0.1",
|
||||||
|
"publicReleaseRefSpec": [
|
||||||
|
"^refs/heads/master$", // we release out of master
|
||||||
|
"^refs/heads/dev$", // we release out of develop
|
||||||
|
"^refs/tags/v\\d+\\.\\d+" // we also release tags starting with vN.N
|
||||||
|
],
|
||||||
|
"nugetPackageVersion":{
|
||||||
|
"semVer": 2
|
||||||
|
},
|
||||||
|
"cloudBuild": {
|
||||||
|
"buildNumber": {
|
||||||
|
"enabled": true,
|
||||||
|
"setVersionVariables": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user