More performance/memory improvements and protections in ArgumentFormatter
This commit is contained in:
Родитель
cb0224191d
Коммит
d002a821ad
|
@ -3,6 +3,7 @@ using System.Collections;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Xunit.Sdk
|
||||
{
|
||||
|
@ -11,6 +12,10 @@ namespace Xunit.Sdk
|
|||
/// </summary>
|
||||
public static class ArgumentFormatter
|
||||
{
|
||||
const int MAX_ENUMERABLE_LENGTH = 5;
|
||||
const int MAX_OBJECT_DEPTH = 3;
|
||||
const int MAX_STRING_LENGTH = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Format the value for presentation.
|
||||
/// </summary>
|
||||
|
@ -36,21 +41,21 @@ namespace Xunit.Sdk
|
|||
string stringParameter = value as string;
|
||||
if (stringParameter != null)
|
||||
{
|
||||
if (stringParameter.Length > 50)
|
||||
return String.Format("\"{0}\"...", stringParameter.Substring(0, 50));
|
||||
if (stringParameter.Length > MAX_STRING_LENGTH)
|
||||
return String.Format("\"{0}\"...", stringParameter.Substring(0, MAX_STRING_LENGTH));
|
||||
|
||||
return String.Format("\"{0}\"", stringParameter);
|
||||
}
|
||||
|
||||
var enumerable = value as IEnumerable;
|
||||
if (enumerable != null)
|
||||
return String.Format("[{0}]", String.Join(", ", enumerable.Cast<object>().Select(x => Format(x, depth + 1))));
|
||||
return FormatEnumerable(enumerable.Cast<object>(), depth);
|
||||
|
||||
var type = value.GetType();
|
||||
if (type.GetTypeInfo().IsValueType)
|
||||
return Convert.ToString(value, CultureInfo.CurrentCulture);
|
||||
|
||||
if (depth == 3)
|
||||
if (depth == MAX_OBJECT_DEPTH)
|
||||
return String.Format("{0} {{ ... }}", type.Name);
|
||||
|
||||
var fields = type.GetRuntimeFields()
|
||||
|
@ -70,6 +75,20 @@ namespace Xunit.Sdk
|
|||
return String.Format("{0} {1}", type.Name, parameterValues);
|
||||
}
|
||||
|
||||
private static string FormatEnumerable(IEnumerable<object> enumerableValues, int depth)
|
||||
{
|
||||
if (depth == MAX_OBJECT_DEPTH)
|
||||
return "[...]";
|
||||
|
||||
var values = enumerableValues.Take(MAX_ENUMERABLE_LENGTH + 1).ToList();
|
||||
var printedValues = String.Join(", ", values.Take(MAX_ENUMERABLE_LENGTH).Select(x => Format(x, depth + 1)));
|
||||
|
||||
if (values.Count > MAX_ENUMERABLE_LENGTH)
|
||||
printedValues += ", ...";
|
||||
|
||||
return String.Format("[{0}]", printedValues);
|
||||
}
|
||||
|
||||
private static string WrapAndGetFormattedValue(Func<object> getter, int depth)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -1,125 +1,151 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
public class ArgumentFormatterTests
|
||||
{
|
||||
[Fact]
|
||||
public static void NullValue()
|
||||
public class SimpleValues
|
||||
{
|
||||
Assert.Equal("null", ArgumentFormatter.Format(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void StringValue()
|
||||
{
|
||||
Assert.Equal("\"Hello, world!\"", ArgumentFormatter.Format("Hello, world!"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void StringValueTruncatedPast50Characters()
|
||||
{
|
||||
Assert.Equal("\"----|----1----|----2----|----3----|----4----|----5\"...", ArgumentFormatter.Format("----|----1----|----2----|----3----|----4----|----5-"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void CharacterValue()
|
||||
{
|
||||
Assert.Equal("'a'", ArgumentFormatter.Format('a'));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DecimalValue()
|
||||
{
|
||||
Assert.Equal(123.45M.ToString(CultureInfo.CurrentCulture), ArgumentFormatter.Format(123.45M));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void EnumerableValue()
|
||||
{
|
||||
var expected = String.Format("[1, {0}, \"Hello, world!\"]", 2.3M.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
Assert.Equal(expected, ArgumentFormatter.Format(new object[] { 1, 2.3M, "Hello, world!" }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TypeValue()
|
||||
{
|
||||
Assert.Equal("typeof(System.String)", ArgumentFormatter.Format(typeof(string)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ComplexTypeReturnsValuesInAlphabeticalOrder()
|
||||
{
|
||||
var expected = String.Format("MyComplexType {{ MyPublicField = 42, MyPublicProperty = {0} }}", 21.12M.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
Assert.Equal(expected, ArgumentFormatter.Format(new MyComplexType()));
|
||||
}
|
||||
|
||||
public class MyComplexType
|
||||
{
|
||||
#pragma warning disable 414
|
||||
private string MyPrivateField = "Hello, world";
|
||||
#pragma warning restore 414
|
||||
|
||||
public static int MyPublicStaticField = 2112;
|
||||
|
||||
public decimal MyPublicProperty { get; private set; }
|
||||
|
||||
public int MyPublicField = 42;
|
||||
|
||||
public MyComplexType()
|
||||
[Fact]
|
||||
public static void NullValue()
|
||||
{
|
||||
MyPublicProperty = 21.12M;
|
||||
Assert.Equal("null", ArgumentFormatter.Format(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void StringValue()
|
||||
{
|
||||
Assert.Equal("\"Hello, world!\"", ArgumentFormatter.Format("Hello, world!"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void StringValueTruncated()
|
||||
{
|
||||
Assert.Equal("\"----|----1----|----2----|----3----|----4----|----5\"...", ArgumentFormatter.Format("----|----1----|----2----|----3----|----4----|----5-"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void CharacterValue()
|
||||
{
|
||||
Assert.Equal("'a'", ArgumentFormatter.Format('a'));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void DecimalValue()
|
||||
{
|
||||
Assert.Equal(123.45M.ToString(CultureInfo.CurrentCulture), ArgumentFormatter.Format(123.45M));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TypeValue()
|
||||
{
|
||||
Assert.Equal("typeof(System.String)", ArgumentFormatter.Format(typeof(string)));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ComplexTypeInsideComplexType()
|
||||
public class Enumerables
|
||||
{
|
||||
var expected = String.Format("MyComplexTypeWrapper {{ c = 'A', s = \"Hello, world!\", t = MyComplexType {{ MyPublicField = 42, MyPublicProperty = {0} }} }}", 21.12M.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
Assert.Equal(expected, ArgumentFormatter.Format(new MyComplexTypeWrapper()));
|
||||
}
|
||||
|
||||
public class MyComplexTypeWrapper
|
||||
{
|
||||
public MyComplexType t = new MyComplexType();
|
||||
public char c = 'A';
|
||||
public string s = "Hello, world!";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void EmptyComplexType()
|
||||
{
|
||||
Assert.Equal("Object { }", ArgumentFormatter.Format(new object()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ComplexTypeWithThrowingPropertyGetter()
|
||||
{
|
||||
Assert.Equal("ThrowingGetter { MyThrowingProperty = (throws NotImplementedException) }", ArgumentFormatter.Format(new ThrowingGetter()));
|
||||
}
|
||||
|
||||
public class ThrowingGetter
|
||||
{
|
||||
public string MyThrowingProperty { get { throw new NotImplementedException(); } }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TypesAreRenderedWithMaximumDepthToPreventInfiniteRecursion()
|
||||
{
|
||||
Assert.Equal("Looping { Me = Looping { Me = Looping { ... } } }", ArgumentFormatter.Format(new Looping()));
|
||||
}
|
||||
|
||||
public class Looping
|
||||
{
|
||||
public Looping Me;
|
||||
|
||||
public Looping()
|
||||
[Fact]
|
||||
public static void EnumerableValue()
|
||||
{
|
||||
Me = this;
|
||||
var expected = String.Format("[1, {0}, \"Hello, world!\"]", 2.3M.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
Assert.Equal(expected, ArgumentFormatter.Format(new object[] { 1, 2.3M, "Hello, world!" }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void OnlyFirstFewValuesOfEnumerableAreRendered()
|
||||
{
|
||||
Assert.Equal("[0, 1, 2, 3, 4, ...]", ArgumentFormatter.Format(Enumerable.Range(0, Int32.MaxValue)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void EnumerablesAreRenderedWithMaximumDepthToPreventInfiniteRecursion()
|
||||
{
|
||||
object[] looping = new object[2];
|
||||
looping[0] = 42;
|
||||
looping[1] = looping;
|
||||
|
||||
Assert.Equal("[42, [42, [...]]]", ArgumentFormatter.Format(looping));
|
||||
}
|
||||
}
|
||||
|
||||
public class ComplexTypes
|
||||
{
|
||||
[Fact]
|
||||
public static void ComplexTypeReturnsValuesInAlphabeticalOrder()
|
||||
{
|
||||
var expected = String.Format("MyComplexType {{ MyPublicField = 42, MyPublicProperty = {0} }}", 21.12M.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
Assert.Equal(expected, ArgumentFormatter.Format(new MyComplexType()));
|
||||
}
|
||||
|
||||
public class MyComplexType
|
||||
{
|
||||
#pragma warning disable 414
|
||||
private string MyPrivateField = "Hello, world";
|
||||
#pragma warning restore 414
|
||||
|
||||
public static int MyPublicStaticField = 2112;
|
||||
|
||||
public decimal MyPublicProperty { get; private set; }
|
||||
|
||||
public int MyPublicField = 42;
|
||||
|
||||
public MyComplexType()
|
||||
{
|
||||
MyPublicProperty = 21.12M;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ComplexTypeInsideComplexType()
|
||||
{
|
||||
var expected = String.Format("MyComplexTypeWrapper {{ c = 'A', s = \"Hello, world!\", t = MyComplexType {{ MyPublicField = 42, MyPublicProperty = {0} }} }}", 21.12M.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
Assert.Equal(expected, ArgumentFormatter.Format(new MyComplexTypeWrapper()));
|
||||
}
|
||||
|
||||
public class MyComplexTypeWrapper
|
||||
{
|
||||
public MyComplexType t = new MyComplexType();
|
||||
public char c = 'A';
|
||||
public string s = "Hello, world!";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void EmptyComplexType()
|
||||
{
|
||||
Assert.Equal("Object { }", ArgumentFormatter.Format(new object()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void ComplexTypeWithThrowingPropertyGetter()
|
||||
{
|
||||
Assert.Equal("ThrowingGetter { MyThrowingProperty = (throws NotImplementedException) }", ArgumentFormatter.Format(new ThrowingGetter()));
|
||||
}
|
||||
|
||||
public class ThrowingGetter
|
||||
{
|
||||
public string MyThrowingProperty { get { throw new NotImplementedException(); } }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TypesAreRenderedWithMaximumDepthToPreventInfiniteRecursion()
|
||||
{
|
||||
Assert.Equal("Looping { Me = Looping { Me = Looping { ... } } }", ArgumentFormatter.Format(new Looping()));
|
||||
}
|
||||
|
||||
public class Looping
|
||||
{
|
||||
public Looping Me;
|
||||
|
||||
public Looping()
|
||||
{
|
||||
Me = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче