a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
731 lines
22 KiB
C#
731 lines
22 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Dynamic;
|
|
using System.Linq;
|
|
using System.Web.TestUtil;
|
|
using Xunit;
|
|
using Assert = Microsoft.TestCommon.AssertEx;
|
|
|
|
namespace System.Web.Helpers.Test
|
|
{
|
|
public class ObjectInfoTest
|
|
{
|
|
[Fact]
|
|
public void PrintWithNegativeDepthThrows()
|
|
{
|
|
// Act & Assert
|
|
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => ObjectInfo.Print(null, depth: -1), "depth", "0");
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithInvalidEnumerationLength()
|
|
{
|
|
// Act & Assert
|
|
Assert.ThrowsArgumentGreaterThan(() => ObjectInfo.Print(null, enumerationLength: -1), "enumerationLength", "0");
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithNull()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
|
|
// Act
|
|
visitor.Print(null);
|
|
|
|
// Assert
|
|
Assert.Equal(1, visitor.Values.Count);
|
|
Assert.Equal("null", visitor.Values[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithEmptyString()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
|
|
// Act
|
|
visitor.Print(String.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(1, visitor.Values.Count);
|
|
Assert.Equal(String.Empty, visitor.Values[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithInt()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
|
|
// Act
|
|
visitor.Print(404);
|
|
|
|
// Assert
|
|
Assert.Equal(1, visitor.Values.Count);
|
|
Assert.Equal("404", visitor.Values[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithIDictionary()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
IDictionary dict = new OrderedDictionary();
|
|
dict.Add("foo", "bar");
|
|
dict.Add("abc", 500);
|
|
|
|
// Act
|
|
visitor.Print(dict);
|
|
|
|
// Assert
|
|
Assert.Equal("foo = bar", visitor.KeyValuePairs[0]);
|
|
Assert.Equal("abc = 500", visitor.KeyValuePairs[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithIEnumerable()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var values = Enumerable.Range(0, 10);
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
foreach (var num in values)
|
|
{
|
|
Assert.True(visitor.Values.Contains(num.ToString()));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithGenericIListPrintsIndex()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var values = Enumerable.Range(0, 10).ToList();
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
for (int i = 0; i < values.Count; i++)
|
|
{
|
|
Assert.True(visitor.Values.Contains(values[i].ToString()));
|
|
Assert.True(visitor.Indexes.Contains(i));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithArrayPrintsIndex()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var values = Enumerable.Range(0, 10).ToArray();
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
Assert.True(visitor.Values.Contains(values[i].ToString()));
|
|
Assert.True(visitor.Indexes.Contains(i));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintNameValueCollectionPrintsKeysAndValues()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var values = new NameValueCollection();
|
|
values["a"] = "1";
|
|
values["b"] = null;
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
Assert.Equal("a = 1", visitor.KeyValuePairs[0]);
|
|
Assert.Equal("b = null", visitor.KeyValuePairs[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintDateTime()
|
|
{
|
|
using (new CultureReplacer())
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var dt = new DateTime(2001, 11, 20, 10, 30, 1);
|
|
|
|
// Act
|
|
visitor.Print(dt);
|
|
|
|
// Assert
|
|
Assert.Equal("11/20/2001 10:30:01 AM", visitor.Values[0]);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintCustomObjectPrintsMembers()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var person = new Person
|
|
{
|
|
Name = "David",
|
|
Age = 23.3,
|
|
Dob = new DateTime(1986, 11, 19),
|
|
LongType = 1000000000,
|
|
Type = 1
|
|
};
|
|
|
|
using (new CultureReplacer())
|
|
{
|
|
// Act
|
|
visitor.Print(person);
|
|
|
|
// Assert
|
|
Assert.Equal(9, visitor.Members.Count);
|
|
Assert.True(visitor.Members.Contains("double Age = 23.3"));
|
|
Assert.True(visitor.Members.Contains("string Name = David"));
|
|
Assert.True(visitor.Members.Contains("DateTime Dob = 11/19/1986 12:00:00 AM"));
|
|
Assert.True(visitor.Members.Contains("short Type = 1"));
|
|
Assert.True(visitor.Members.Contains("float Float = 0"));
|
|
Assert.True(visitor.Members.Contains("byte Byte = 0"));
|
|
Assert.True(visitor.Members.Contains("decimal Decimal = 0"));
|
|
Assert.True(visitor.Members.Contains("bool Bool = False"));
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintShowsVisitedWhenCircularReferenceInObjectGraph()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
PersonNode node = new PersonNode
|
|
{
|
|
Person = new Person
|
|
{
|
|
Name = "David",
|
|
Age = 23.3
|
|
}
|
|
};
|
|
node.Next = node;
|
|
|
|
// Act
|
|
visitor.Print(node);
|
|
|
|
// Assert
|
|
Assert.True(visitor.Members.Contains("string Name = David"));
|
|
Assert.True(visitor.Members.Contains("double Age = 23.3"));
|
|
Assert.True(visitor.Members.Contains("PersonNode Next = Visited"));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintShowsVisitedWhenCircularReferenceIsIEnumerable()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
List<object> values = new List<object>();
|
|
values.Add(values);
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
Assert.Equal("Visited", visitor.Values[0]);
|
|
Assert.Equal("Visited " + values.GetHashCode(), visitor.Visited[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintShowsVisitedWhenCircularReferenceIsIDictionary()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
OrderedDictionary values = new OrderedDictionary();
|
|
values[values] = values;
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
Assert.Equal("Visited", visitor.Values[0]);
|
|
Assert.Equal("Visited " + values.GetHashCode(), visitor.Visited[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintShowsVisitedWhenCircularReferenceIsNameValueCollection()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
NameValueCollection nameValues = new NameValueCollection();
|
|
nameValues["id"] = "1";
|
|
List<NameValueCollection> values = new List<NameValueCollection>();
|
|
values.Add(nameValues);
|
|
values.Add(nameValues);
|
|
|
|
// Act
|
|
visitor.Print(values);
|
|
|
|
// Assert
|
|
Assert.True(visitor.Values.Contains("Visited"));
|
|
Assert.True(visitor.Visited.Contains("Visited " + nameValues.GetHashCode()));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintExcludesWriteOnlyProperties()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
ClassWithWriteOnlyProperty cls = new ClassWithWriteOnlyProperty();
|
|
|
|
// Act
|
|
visitor.Print(cls);
|
|
|
|
// Assert
|
|
Assert.Equal(0, visitor.Members.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWritesEnumeratedElementsUntilLimitIsHit()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var enumeration = Enumerable.Range(0, 2000);
|
|
|
|
// Act
|
|
visitor.Print(enumeration);
|
|
|
|
// Assert
|
|
for (int i = 0; i <= 2000; i++)
|
|
{
|
|
if (i < 1000)
|
|
{
|
|
Assert.True(visitor.Values.Contains(i.ToString()));
|
|
}
|
|
else
|
|
{
|
|
Assert.False(visitor.Values.Contains(i.ToString()));
|
|
}
|
|
}
|
|
Assert.True(visitor.Values.Contains("Limit Exceeded"));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintWithAnonymousType()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
var value = new { Name = "John", X = 1 };
|
|
|
|
// Act
|
|
visitor.Print(value);
|
|
|
|
// Assert
|
|
Assert.True(visitor.Members.Contains("string Name = John"));
|
|
Assert.True(visitor.Members.Contains("int X = 1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintClassWithPublicFields()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
ClassWithFields value = new ClassWithFields();
|
|
value.Foo = "John";
|
|
value.Bar = 1;
|
|
|
|
// Actt
|
|
visitor.Print(value);
|
|
|
|
// Assert
|
|
Assert.True(visitor.Members.Contains("string Foo = John"));
|
|
Assert.True(visitor.Members.Contains("int Bar = 1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintClassWithDynamicMembersPrintsMembersIfGetDynamicMemberNamesIsImplemented()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
dynamic d = new DynamicDictionary();
|
|
d.Cycle = d;
|
|
d.Name = "Foo";
|
|
d.Value = null;
|
|
|
|
// Act
|
|
visitor.Print(d);
|
|
|
|
// Assert
|
|
Assert.True(visitor.Members.Contains("DynamicDictionary Cycle = Visited"));
|
|
Assert.True(visitor.Members.Contains("string Name = Foo"));
|
|
Assert.True(visitor.Members.Contains("Value = null"));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintClassWithDynamicMembersReturningNullPrintsNoMembers()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
dynamic d = new ClassWithDynamicAnNullMemberNames();
|
|
d.Cycle = d;
|
|
d.Name = "Foo";
|
|
d.Value = null;
|
|
|
|
// Act
|
|
visitor.Print(d);
|
|
|
|
// Assert
|
|
Assert.False(visitor.Members.Any());
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintUsesToStringOfIConvertibleObjects()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
ConvertibleClass cls = new ConvertibleClass();
|
|
|
|
// Act
|
|
visitor.Print(cls);
|
|
|
|
// Assert
|
|
Assert.Equal("Test", visitor.Values[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintConvertsTypeToString()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
|
|
// Act
|
|
visitor.Print(typeof(string));
|
|
|
|
// Assert
|
|
Assert.Equal("typeof(string)", visitor.Values[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintClassWithPropertyThatThrowsExceptionPrintsException()
|
|
{
|
|
// Arrange
|
|
MockObjectVisitor visitor = CreateObjectVisitor();
|
|
ClassWithPropertyThatThrowsException value = new ClassWithPropertyThatThrowsException();
|
|
|
|
// Act
|
|
visitor.Print(value);
|
|
|
|
// Assert
|
|
Assert.Equal("int MyProperty = Property accessor 'MyProperty' on object 'System.Web.Helpers.Test.ObjectInfoTest+ClassWithPropertyThatThrowsException' threw the following exception:'Property that shows an exception'", visitor.Members[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConvertEscapeSequencesPrintsStringEscapeSequencesAsLiterals()
|
|
{
|
|
// Act
|
|
string value = HtmlObjectPrinter.ConvertEscapseSequences("\\\'\"\0\a\b\f\n\r\t\v");
|
|
|
|
// Assert
|
|
Assert.Equal("\\\\'\\\"\\0\\a\\b\\f\\n\\r\\t\\v", value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ConvertEscapeSequencesDoesNotEscapeUnicodeSequences()
|
|
{
|
|
// Act
|
|
string value = HtmlObjectPrinter.ConvertEscapseSequences("\u1023\x2045");
|
|
|
|
// Assert
|
|
Assert.Equal("\u1023\x2045", value);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintCharPrintsQuotedString()
|
|
{
|
|
// Arrange
|
|
HtmlObjectPrinter printer = new HtmlObjectPrinter(100, 100);
|
|
HtmlElement element = new HtmlElement("span");
|
|
printer.PushElement(element);
|
|
|
|
// Act
|
|
printer.VisitConvertedValue('x', "x");
|
|
|
|
// Assert
|
|
Assert.Equal(1, element.Children.Count);
|
|
HtmlElement child = element.Children[0];
|
|
Assert.Equal("'x'", child.InnerText);
|
|
Assert.Equal("quote", child["class"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrintEscapeCharPrintsEscapedCharAsLiteral()
|
|
{
|
|
// Arrange
|
|
HtmlObjectPrinter printer = new HtmlObjectPrinter(100, 100);
|
|
HtmlElement element = new HtmlElement("span");
|
|
printer.PushElement(element);
|
|
|
|
// Act
|
|
printer.VisitConvertedValue('\t', "\t");
|
|
|
|
// Assert
|
|
Assert.Equal(1, element.Children.Count);
|
|
HtmlElement child = element.Children[0];
|
|
Assert.Equal("'\\t'", child.InnerText);
|
|
Assert.Equal("quote", child["class"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetTypeNameConvertsGenericTypesToCsharpSyntax()
|
|
{
|
|
// Act
|
|
string value = ObjectVisitor.GetTypeName(typeof(Func<Func<Func<int, int, object>, Action<int>>>));
|
|
|
|
// Assert
|
|
Assert.Equal("Func<Func<Func<int, int, object>, Action<int>>>", value);
|
|
}
|
|
|
|
private class ConvertibleClass : IConvertible
|
|
{
|
|
public TypeCode GetTypeCode()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public bool ToBoolean(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public byte ToByte(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public char ToChar(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public DateTime ToDateTime(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public decimal ToDecimal(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public double ToDouble(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public short ToInt16(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public int ToInt32(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public long ToInt64(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public sbyte ToSByte(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public float ToSingle(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string ToString(IFormatProvider provider)
|
|
{
|
|
return "Test";
|
|
}
|
|
|
|
public object ToType(Type conversionType, IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public ushort ToUInt16(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public uint ToUInt32(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public ulong ToUInt64(IFormatProvider provider)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
private class ClassWithPropertyThatThrowsException
|
|
{
|
|
public int MyProperty
|
|
{
|
|
get { throw new InvalidOperationException("Property that shows an exception"); }
|
|
}
|
|
}
|
|
|
|
private class ClassWithDynamicAnNullMemberNames : DynamicObject
|
|
{
|
|
public override IEnumerable<string> GetDynamicMemberNames()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
|
{
|
|
result = null;
|
|
return true;
|
|
}
|
|
|
|
public override bool TrySetMember(SetMemberBinder binder, object value)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private class Person
|
|
{
|
|
public string Name { get; set; }
|
|
public double Age { get; set; }
|
|
public DateTime Dob { get; set; }
|
|
public short Type { get; set; }
|
|
public long LongType { get; set; }
|
|
public float Float { get; set; }
|
|
public byte Byte { get; set; }
|
|
public decimal Decimal { get; set; }
|
|
public bool Bool { get; set; }
|
|
}
|
|
|
|
private class ClassWithFields
|
|
{
|
|
public string Foo;
|
|
public int Bar = 13;
|
|
}
|
|
|
|
private class ClassWithWriteOnlyProperty
|
|
{
|
|
public int Value
|
|
{
|
|
set { }
|
|
}
|
|
}
|
|
|
|
private class PersonNode
|
|
{
|
|
public Person Person { get; set; }
|
|
public PersonNode Next { get; set; }
|
|
}
|
|
|
|
private MockObjectVisitor CreateObjectVisitor(int recursionLimit = 10, int enumerationLimit = 1000)
|
|
{
|
|
return new MockObjectVisitor(recursionLimit, enumerationLimit);
|
|
}
|
|
|
|
private class MockObjectVisitor : ObjectVisitor
|
|
{
|
|
public MockObjectVisitor(int recursionLimit, int enumerationLimit)
|
|
: base(recursionLimit, enumerationLimit)
|
|
{
|
|
Values = new List<string>();
|
|
KeyValuePairs = new List<string>();
|
|
Members = new List<string>();
|
|
Indexes = new List<int>();
|
|
Visited = new List<string>();
|
|
}
|
|
|
|
public List<string> Values { get; set; }
|
|
public List<string> KeyValuePairs { get; set; }
|
|
public List<string> Members { get; set; }
|
|
public List<int> Indexes { get; set; }
|
|
public List<string> Visited { get; set; }
|
|
|
|
public void Print(object value)
|
|
{
|
|
Visit(value, 0);
|
|
}
|
|
|
|
public override void VisitObjectVisitorException(ObjectVisitorException exception)
|
|
{
|
|
Values.Add(exception.InnerException.Message);
|
|
}
|
|
|
|
public override void VisitStringValue(string stringValue)
|
|
{
|
|
Values.Add(stringValue);
|
|
base.VisitStringValue(stringValue);
|
|
}
|
|
|
|
public override void VisitVisitedObject(string id, object value)
|
|
{
|
|
Visited.Add(String.Format("Visited {0}", id));
|
|
Values.Add("Visited");
|
|
base.VisitVisitedObject(id, value);
|
|
}
|
|
|
|
public override void VisitIndexedEnumeratedValue(int index, object item, int depth)
|
|
{
|
|
Indexes.Add(index);
|
|
base.VisitIndexedEnumeratedValue(index, item, depth);
|
|
}
|
|
|
|
public override void VisitEnumeratonLimitExceeded()
|
|
{
|
|
Values.Add("Limit Exceeded");
|
|
base.VisitEnumeratonLimitExceeded();
|
|
}
|
|
|
|
public override void VisitMember(string name, Type type, object value, int depth)
|
|
{
|
|
base.VisitMember(name, type, value, depth);
|
|
type = type ?? (value != null ? value.GetType() : null);
|
|
if (type == null)
|
|
{
|
|
Members.Add(String.Format("{0} = null", name));
|
|
}
|
|
else
|
|
{
|
|
Members.Add(String.Format("{0} {1} = {2}", GetTypeName(type), name, Values.Last()));
|
|
}
|
|
}
|
|
|
|
public override void VisitNull()
|
|
{
|
|
Values.Add("null");
|
|
base.VisitNull();
|
|
}
|
|
|
|
public override void VisitKeyValue(object key, object value, int depth)
|
|
{
|
|
base.VisitKeyValue(key, value, depth);
|
|
KeyValuePairs.Add(String.Format("{0} = {1}", Values[Values.Count - 2], Values[Values.Count - 1]));
|
|
}
|
|
}
|
|
}
|
|
}
|