-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Aggregate Inner Exception Messages and Stacktraces #3784
base: main
Are you sure you want to change the base?
Changes from all commits
cbde275
2df9965
822d400
b9270e2
8c63809
c08dd26
cc3c846
64a0033
62e63b5
025ce48
629bf3f
db8d816
3cd8cbd
5814aca
7b53b40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -392,6 +392,10 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella | |
return; | ||
} | ||
|
||
Exception[] exceptions = []; | ||
IEnumerable<string> exceptionMessages = []; | ||
IEnumerable<string> exceptionStacktraces = []; | ||
|
||
switch (value) | ||
{ | ||
case TestNodeUpdateMessage testNodeStateChanged: | ||
|
@@ -404,57 +408,73 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella | |
break; | ||
|
||
case ErrorTestNodeStateProperty errorState: | ||
exceptions = GetExceptionsInThrownOrder(errorState.Exception); | ||
exceptionMessages = GetExceptionMessages(exceptions, errorState.Explanation); | ||
exceptionStacktraces = GetExceptionStackTraces(exceptions); | ||
|
||
_terminalTestReporter.TestCompleted( | ||
_assemblyName, | ||
_targetFramework, | ||
_shortArchitecture, | ||
testNodeStateChanged.TestNode.DisplayName, | ||
TestOutcome.Error, | ||
duration, | ||
errorMessage: errorState.Exception?.Message ?? errorState.Explanation, | ||
errorState.Exception?.StackTrace, | ||
errorMessage: string.Join(_environment.NewLine, exceptionMessages), | ||
errorStackTrace: string.Join(_environment.NewLine, exceptionStacktraces), | ||
expected: null, | ||
actual: null); | ||
break; | ||
|
||
case FailedTestNodeStateProperty failedState: | ||
exceptions = GetExceptionsInThrownOrder(failedState.Exception); | ||
exceptionMessages = GetExceptionMessages(exceptions, failedState.Explanation); | ||
exceptionStacktraces = GetExceptionStackTraces(exceptions); | ||
|
||
_terminalTestReporter.TestCompleted( | ||
_assemblyName, | ||
_targetFramework, | ||
_shortArchitecture, | ||
testNodeStateChanged.TestNode.DisplayName, | ||
TestOutcome.Fail, | ||
duration, | ||
errorMessage: failedState.Exception?.Message ?? failedState.Explanation, | ||
failedState.Exception?.StackTrace, | ||
errorMessage: string.Join(_environment.NewLine, exceptionMessages), | ||
errorStackTrace: string.Join(_environment.NewLine, exceptionStacktraces), | ||
expected: failedState.Exception?.Data["assert.expected"] as string, | ||
actual: failedState.Exception?.Data["assert.actual"] as string); | ||
break; | ||
|
||
case TimeoutTestNodeStateProperty timeoutState: | ||
exceptions = GetExceptionsInThrownOrder(timeoutState.Exception); | ||
exceptionMessages = GetExceptionMessages(exceptions, timeoutState.Explanation); | ||
exceptionStacktraces = GetExceptionStackTraces(exceptions); | ||
|
||
_terminalTestReporter.TestCompleted( | ||
_assemblyName, | ||
_targetFramework, | ||
_shortArchitecture, | ||
testNodeStateChanged.TestNode.DisplayName, | ||
TestOutcome.Timeout, | ||
duration, | ||
errorMessage: timeoutState.Exception?.Message ?? timeoutState.Explanation, | ||
timeoutState.Exception?.StackTrace, | ||
errorMessage: string.Join(_environment.NewLine, exceptionMessages), | ||
errorStackTrace: string.Join(_environment.NewLine, exceptionStacktraces), | ||
expected: null, | ||
actual: null); | ||
break; | ||
|
||
case CancelledTestNodeStateProperty cancelledState: | ||
exceptions = GetExceptionsInThrownOrder(cancelledState.Exception); | ||
exceptionMessages = GetExceptionMessages(exceptions, cancelledState.Explanation); | ||
exceptionStacktraces = GetExceptionStackTraces(exceptions); | ||
|
||
_terminalTestReporter.TestCompleted( | ||
_assemblyName, | ||
_targetFramework, | ||
_shortArchitecture, | ||
testNodeStateChanged.TestNode.DisplayName, | ||
TestOutcome.Canceled, | ||
duration, | ||
errorMessage: cancelledState.Exception?.Message ?? cancelledState.Explanation, | ||
cancelledState.Exception?.StackTrace, | ||
errorMessage: string.Join(_environment.NewLine, exceptionMessages), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the receiving api can be changed to take array of strings, (we use it both here and behind a remote call in That will allow us to render the erros and stack traces under each other, so you don't have to jump back and forth. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. Do you want to take over this pr because I can't actually build it locally so it makes Dev a bit harder There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
errorStackTrace: string.Join(_environment.NewLine, exceptionStacktraces), | ||
expected: null, | ||
actual: null); | ||
break; | ||
|
@@ -536,6 +556,62 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella | |
} | ||
} | ||
|
||
private static IEnumerable<string> GetExceptionMessages(Exception[] exceptions, string? fallback) | ||
{ | ||
if (exceptions.Length == 0) | ||
{ | ||
if (fallback != null) | ||
{ | ||
yield return fallback; | ||
} | ||
|
||
yield break; | ||
} | ||
|
||
for (int index = 0; index < exceptions.Length; index++) | ||
{ | ||
Exception exception = exceptions[index]; | ||
|
||
if (exception.StackTrace == null) | ||
{ | ||
continue; | ||
} | ||
|
||
string prefix = index == 0 ? string.Empty : " ---> "; | ||
yield return $"{prefix}{exception.GetType().FullName}: {exception.Message}"; | ||
} | ||
} | ||
|
||
private IEnumerable<string> GetExceptionStackTraces(Exception[] exceptions) | ||
{ | ||
for (int index = 0; index < exceptions.Length; index++) | ||
{ | ||
Exception exception = exceptions[index]; | ||
|
||
if (exception.StackTrace == null) | ||
{ | ||
continue; | ||
} | ||
|
||
string suffix = index != exceptions.Length - 1 | ||
? $"{_environment.NewLine} --- End of inner exception stack trace ---" | ||
: string.Empty; | ||
|
||
yield return $"{exception.StackTrace.Trim()}{suffix}"; | ||
} | ||
} | ||
|
||
private static Exception[] GetExceptionsInThrownOrder(Exception? exception) => GetExceptions(exception).Reverse().ToArray(); | ||
|
||
private static IEnumerable<Exception> GetExceptions(Exception? exception) | ||
{ | ||
while (exception is not null) | ||
{ | ||
yield return exception; | ||
exception = exception.InnerException; | ||
} | ||
} | ||
|
||
public void Dispose() | ||
=> _terminalTestReporter?.Dispose(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should always be exactly 3 spaces in the stack frame, is this change needed or just done to make the regex more regexy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When testing I think the first line of inner exceptions weren't rendering properly without this change. Might be that I messed up formatting then