Nested exceptions in xunit1 class start/finish

This commit is contained in:
Matt Ellis 2014-04-24 16:35:35 +01:00 коммит произвёл Brad Wilson
Родитель 336dd8a511
Коммит 9759e69ae6
4 изменённых файлов: 188 добавлений и 9 удалений

Просмотреть файл

@ -50,17 +50,14 @@ namespace Xunit
SendTestCaseMessagesWhenAppropriate(null);
bool @continue = true;
XmlNode failure;
if ((failure = xml.SelectSingleNode("failure")) != null)
XmlNode failureNode;
if ((failureNode = xml.SelectSingleNode("failure")) != null)
{
// exception-type was added in xunit 1.1
var attribute = failure.Attributes["exception-type"];
var exceptionType = attribute != null ? attribute.Value : null;
var message = failure.SelectSingleNode("message").InnerText;
var stackTrace = failure.SelectSingleNode("stack-trace").InnerText;
var failureInformation = Xunit1ExceptionUtility.ConvertToFailureInformation(failureNode);
var errorMessage = new ErrorMessage(new[] {exceptionType}, new[] {message}, new[] {stackTrace},
new[] {-1});
var errorMessage = new ErrorMessage(failureInformation.ExceptionTypes,
failureInformation.Messages, failureInformation.StackTraces,
failureInformation.ExceptionParentIndices);
@continue = messageSink.OnMessage(errorMessage);
}

Просмотреть файл

@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using Xunit.Abstractions;
namespace Xunit
{
internal static class Xunit1ExceptionUtility
{
static readonly Regex NestedMessagesRegex = new Regex(@"-*\s*(?<type>.*?) :\s*(?<message>.*?)((\r\n-)|\z)",
RegexOptions.ExplicitCapture | RegexOptions.Multiline | RegexOptions.Singleline);
static readonly Regex NestedStackTracesRegex = new Regex(@"\r*\n----- Inner Stack Trace -----\r*\n", RegexOptions.Compiled);
public static IFailureInformation ConvertToFailureInformation(XmlNode failureNode)
{
var exceptionTypeAttribute = failureNode.Attributes["exception-type"];
var exceptionType = exceptionTypeAttribute != null ? exceptionTypeAttribute.Value : string.Empty;
var message = failureNode.SelectSingleNode("message").InnerText;
var stackTrace = failureNode.SelectSingleNode("stack-trace").InnerText;
return ConvertToFailureInformation(exceptionType, message, stackTrace);
}
static IFailureInformation ConvertToFailureInformation(string outermostExceptionType, string nestedExceptionMessages, string nestedStackTraces)
{
var failureInformation = new FailureInformation();
var exceptionTypes = new List<string>();
var messages = new List<string>();
var match = NestedMessagesRegex.Match(nestedExceptionMessages);
for (int i = 0; match.Success; i++, match = match.NextMatch())
{
exceptionTypes.Add(match.Groups["type"].Value);
messages.Add(match.Groups["message"].Value);
}
if (exceptionTypes.Count == 0)
{
exceptionTypes.Add(outermostExceptionType);
messages.Add(nestedExceptionMessages);
}
failureInformation.ExceptionTypes = exceptionTypes.ToArray();
failureInformation.Messages = messages.ToArray();
failureInformation.StackTraces = NestedStackTracesRegex.Split(nestedStackTraces);
failureInformation.ExceptionParentIndices = new int[failureInformation.StackTraces.Length];
for (int i = 0; i < failureInformation.ExceptionParentIndices.Length; i++)
failureInformation.ExceptionParentIndices[i] = i - 1;
return failureInformation;
}
class FailureInformation : IFailureInformation
{
public string[] ExceptionTypes { get; set; }
public string[] Messages { get; set; }
public string[] StackTraces { get; set; }
public int[] ExceptionParentIndices { get; set; }
}
}
}

Просмотреть файл

@ -142,6 +142,7 @@
<Link>Frameworks\Messages\TestStarting.cs</Link>
</Compile>
<Compile Include="Frameworks\TestFrameworkOptions.cs" />
<Compile Include="Frameworks\v1\Xunit1ExceptionUtility.cs" />
<Compile Include="Frameworks\v2\XunitDiscoveryOptions.cs" />
<Compile Include="Frameworks\v2\XunitExecutionOptions.cs" />
<Compile Include="Project\XunitFilters.cs" />

Просмотреть файл

@ -467,6 +467,124 @@ public class Xunit1Tests
Assert.Equal("Cannot use a test class as its own fixture data", errorMessage.Messages.Single());
Assert.Equal(exception.StackTrace, errorMessage.StackTraces.Single());
}
[Fact]
public void NestedExceptionsThrownDuringClassStart_ResultsInErrorMessage()
{
var testCollection = new Xunit1TestCollection("AssemblyName.dll");
var testCases = new[] {
new Xunit1TestCase("assembly", "failingtype", "passingmethod", "failingtype.passingmethod") { TestCollection = testCollection }
};
var exception = GetNestedExceptions();
var xunit1 = new TestableXunit1("AssemblyName.dll", "ConfigFile.config");
xunit1.Executor.TestFrameworkDisplayName.Returns("Test framework display name");
xunit1.Executor
.When(x => x.RunTests("failingtype", Arg.Any<List<string>>(),
Arg.Any<ICallbackEventHandler>()))
.Do(callInfo =>
{
var callback = callInfo.Arg<ICallbackEventHandler>();
callback.RaiseCallbackEvent("<start name='failingtype.passingmethod' type='failingtype' method='passingmethod'/>");
callback.RaiseCallbackEvent("<test name='failingtype.passingmethod' type='failingtype' method='passingmethod' result='Pass' time='1.000'/>");
callback.RaiseCallbackEvent(string.Format("<class name='failingtype' time='0.000' total='0' passed='1' failed='1' skipped='0'><failure exception-type='System.InvalidOperationException'><message>{0}</message><stack-trace><![CDATA[{1}]]></stack-trace></failure></class>", GetMessage(exception), GetStackTrace(exception)));
});
var sink = new SpyMessageSink<ITestAssemblyFinished>();
xunit1.Run(testCases, sink);
sink.Finished.WaitOne();
var errorMessage = Assert.Single(sink.Messages.OfType<IErrorMessage>());
Assert.Equal(exception.GetType().FullName, errorMessage.ExceptionTypes[0]);
Assert.Equal(exception.InnerException.GetType().FullName, errorMessage.ExceptionTypes[1]);
Assert.Equal(exception.Message, errorMessage.Messages[0]);
Assert.Equal(exception.InnerException.Message, errorMessage.Messages[1]);
Assert.Equal(exception.StackTrace, errorMessage.StackTraces[0]);
Assert.Equal(exception.InnerException.StackTrace, errorMessage.StackTraces[1]);
}
private Exception GetNestedExceptions()
{
try
{
ThrowOuterException();
return null;
}
catch (Exception e)
{
return e;
}
}
private void ThrowOuterException()
{
try
{
ThrowInnerException();
}
catch (Exception e)
{
throw new InvalidOperationException("Message from outer exception", e);
}
}
private void ThrowInnerException()
{
throw new DivideByZeroException();
}
// From xunit1's ExceptionUtility
private static string GetMessage(Exception ex)
{
return GetMessage(ex, 0);
}
static string GetMessage(Exception ex, int level)
{
string result = "";
if (level > 0)
{
for (int idx = 0; idx < level; idx++)
result += "----";
result += " ";
}
// We won't have any AssertExceptions - we're testing failure in class start/finish
//if (!(ex is AssertException))
result += ex.GetType().FullName + " : ";
result += ex.Message;
if (ex.InnerException != null)
result = result + Environment.NewLine + GetMessage(ex.InnerException, level + 1);
return result;
}
const string RETHROW_MARKER = "$$RethrowMarker$$";
private static string GetStackTrace(Exception ex)
{
if (ex == null)
return "";
string result = ex.StackTrace;
if (result != null)
{
int idx = result.IndexOf(RETHROW_MARKER);
if (idx >= 0)
result = result.Substring(0, idx);
}
if (ex.InnerException != null)
result = result + Environment.NewLine +
"----- Inner Stack Trace -----" + Environment.NewLine +
GetStackTrace(ex.InnerException);
return result;
}
}
class TestableXunit1 : Xunit1