|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using Moq;
namespace System.Windows.Forms.Tests;
// NB: doesn't require thread affinity
public class ListBindingHelperTests
{
public static IEnumerable<object[]> GetList_Object_TestData()
{
yield return new object[] { null, null };
yield return new object[] { 1, 1 };
yield return new object[] { typeof(int), typeof(int) };
yield return new object[] { new int[] { 1 }, new int[] { 1 } };
yield return new object[] { typeof(int[]), typeof(int[]) };
Mock<IListSource> mockSource = new(MockBehavior.Strict);
mockSource
.Setup(s => s.GetList())
.Returns(new int[] { 1 });
yield return new object[] { mockSource.Object, new int[] { 1 } };
}
[Theory]
[MemberData(nameof(GetList_Object_TestData))]
public void ListBindingHelper_GetList_InvokeObject_ReturnsExpected(object list, object expected)
{
Assert.Equal(expected, ListBindingHelper.GetList(list));
}
public static IEnumerable<object[]> GetList_Object_String_TestData()
{
yield return new object[] { null, "dataMember", null };
yield return new object[] { new DataClass { Property = 1 }, "Property", 1 };
yield return new object[] { new DataClass { Property = 1 }, "property", 1 };
yield return new object[] { typeof(DataClass), "NoSuchProperty", typeof(DataClass) };
yield return new object[] { typeof(DataClass), "Property", typeof(DataClass) };
yield return new object[] { new ListDataClass(), "ListProperty", null };
yield return new object[] { new ListDataClass { ListProperty = [] }, "ListProperty", new List<DataClass>() };
yield return new object[] { new IEnumerableWrapper(new object[] { new DataClass { Property = 1 } }), "Property", 1 };
Mock<ICurrencyManagerProvider> nullCurrencyManagerProvider = new(MockBehavior.Strict);
nullCurrencyManagerProvider
.Setup(c => c.CurrencyManager)
.Returns((CurrencyManager)null);
yield return new object[] { nullCurrencyManagerProvider.Object, "CurrencyManager", null };
BindingSource emptySource = [];
Mock<ICurrencyManagerProvider> invalidCurrencyManagerProvider = new(MockBehavior.Strict);
invalidCurrencyManagerProvider
.Setup(c => c.CurrencyManager)
.Returns(emptySource.CurrencyManager);
yield return new object[] { invalidCurrencyManagerProvider.Object, "CurrencyManager", null };
BindingSource validSource = new(new List<CustomCurrencyManagerProvider> { new() { Property = 1 } }, null);
yield return new object[] { new CustomCurrencyManagerProvider { CurrencyManagerResult = validSource.CurrencyManager }, "Property", 1 };
}
[Theory]
[MemberData(nameof(GetList_Object_String_TestData))]
public void ListBindingHelper_GetList_InvokeObjectString_ReturnsExpected(object dataSource, string dataMember, object expected)
{
Assert.Equal(expected, ListBindingHelper.GetList(dataSource, dataMember));
}
public static IEnumerable<object[]> GetList_NoSuchDataMember_TestData()
{
yield return new object[] { new DataClass(), "NoSuchProperty" };
yield return new object[] { new IEnumerableWrapper(Array.Empty<object>()), "NoSuchProperty" };
yield return new object[] { new IEnumerableWrapper(Array.Empty<object>()), "Property" };
}
[Theory]
[MemberData(nameof(GetList_NoSuchDataMember_TestData))]
public void ListBindingHelper_GetList_NoSuchDataMember_ThrowsArgumentException(object dataSource, string dataMember)
{
Assert.Throws<ArgumentException>(() => ListBindingHelper.GetList(dataSource, dataMember));
}
public static IEnumerable<object[]> GetList_DataMember_TestData()
{
yield return new object[] { null, null, null };
yield return new object[] { null, string.Empty, null };
yield return new object[] { null, "reasonable", null };
yield return new object[] { 1, null, 1 };
yield return new object[] { 1, string.Empty, 1 };
yield return new object[] { typeof(int), null, typeof(int) };
yield return new object[] { typeof(int), string.Empty, typeof(int) };
yield return new object[] { typeof(int), "reasonable", typeof(int) };
Mock<IListSource> mockSource = new(MockBehavior.Strict);
mockSource
.Setup(s => s.GetList())
.Returns(new int[] { 1 });
yield return new object[] { mockSource.Object, null, new int[] { 1 } };
yield return new object[] { mockSource.Object, string.Empty, new int[] { 1 } };
}
[Theory]
[MemberData(nameof(GetList_DataMember_TestData))]
public void ListBindingHelper_GetList_InvokeDataMember_ReturnsExpected(object dataSource, string dataMember, object expected)
{
Assert.Equal(expected, ListBindingHelper.GetList(dataSource, dataMember));
}
public static IEnumerable<object[]> GetListItemType_Object_TestData()
{
yield return new object[] { null, null };
yield return new object[] { 1, typeof(int) };
yield return new object[] { typeof(int), typeof(int) };
yield return new object[] { new int[] { 1 }, typeof(int) };
yield return new object[] { typeof(int[]), typeof(int) };
yield return new object[] { new List<int> { 1 }, typeof(int) };
yield return new object[] { typeof(List<int>), typeof(int) };
yield return new object[] { typeof(IList<int>), typeof(int) };
yield return new object[] { typeof(IList), typeof(IList) };
yield return new object[] { typeof(ICollection<int>), typeof(ICollection<int>) };
yield return new object[] { typeof(IEnumerable), typeof(IEnumerable) };
yield return new object[] { new Dictionary<int, string> { { 1, "string" } }, typeof(KeyValuePair<int, string>) };
yield return new object[] { typeof(Dictionary<int, string>), typeof(Dictionary<int, string>) };
yield return new object[] { new ClassWithItem(), typeof(ClassWithItem) };
yield return new object[] { typeof(ClassWithItem), typeof(ClassWithItem) };
Mock<IListSource> genericMockSource = new(MockBehavior.Strict);
genericMockSource.Setup(s => s.GetList()).Returns(new GenericIListSourceClassWithItem());
yield return new object[] { genericMockSource.Object, typeof(int) };
Mock<IListSource> nonGenericMockSource = new(MockBehavior.Strict);
nonGenericMockSource.Setup(s => s.GetList()).Returns(new GenericIListSourceClassWithItem());
yield return new object[] { nonGenericMockSource.Object, typeof(int) };
yield return new object[] { new ITypedListClassWithItem(), typeof(string) };
yield return new object[] { new ITypedListClassWithCustomNamedItem(), typeof(string) };
yield return new object[] { new OnlyGenericIListClassWithItem(), typeof(string) };
yield return new object[] { new ClassWithIListSource(), typeof(int) };
yield return new object[] { typeof(ClassWithIListSource), typeof(int) };
yield return new object[] { new NullIListSource(), null };
yield return new object[] { typeof(NullIListSource), null };
// Type that wraps an enumerable but ONLY implements non-generic IEnumerable.
yield return new object[] { new IEnumerableWrapper(null), typeof(object) };
yield return new object[] { new IEnumerableWrapper(Array.Empty<object>()), typeof(object) };
yield return new object[] { new IEnumerableWrapper(new int[] { 1 }), typeof(int) };
yield return new object[] { new IEnumerableWrapper(new object[] { 1 }), typeof(int) };
yield return new object[] { new IEnumerableWrapper(new object[] { 1, string.Empty }), typeof(int) };
yield return new object[] { new IEnumerableWrapper(new object[] { null }), typeof(object) };
yield return new object[] { new NotSupportedIEnumerable(), typeof(object) };
// Type that only implements IList.
yield return new object[] { new ArrayList(), typeof(object) };
yield return new object[] { new ArrayList { 1 }, typeof(int) };
yield return new object[] { new ArrayList { 1, string.Empty }, typeof(int) };
yield return new object[] { new ArrayList { null }, typeof(object) };
}
[Theory]
[MemberData(nameof(GetListItemType_Object_TestData))]
public void ListBindingHelper_GetListItemType_InvokeObject_ReturnsExpected(object list, Type expected)
{
Assert.Equal(expected, ListBindingHelper.GetListItemType(list));
Assert.Equal(list is null ? typeof(object) : expected, ListBindingHelper.GetListItemType(list, null));
Assert.Equal(list is null ? typeof(object) : expected, ListBindingHelper.GetListItemType(list, string.Empty));
}
public static IEnumerable<object[]> GetListItemType_Object_String_TestData()
{
yield return new object[] { null, "dataMember", typeof(object) };
yield return new object[] { new NullIListSource(), "dataMember", typeof(object) };
yield return new object[] { new DataClass(), "noSuchMember", typeof(object) };
yield return new object[] { new DataClass { Property = 1 }, "Property", typeof(int) };
yield return new object[] { new DataClass { Property = 1 }, "property", typeof(int) };
yield return new object[] { new ICustomTypeDescriptorPropertyClass(), "Property", typeof(ICustomTypeDescriptor) };
Mock<ITypedList> nullMockTypedList = new(MockBehavior.Strict);
nullMockTypedList.Setup(t => t.GetItemProperties(null)).Returns((PropertyDescriptorCollection)null);
yield return new object[] { nullMockTypedList.Object, "dataMember", typeof(object) };
ICustomTypeDescriptorPropertyDescriptorClass instance = new();
Mock<ICustomTypeDescriptor> customTypeDescriptor = new(MockBehavior.Strict);
customTypeDescriptor
.Setup(t => t.GetProperties())
.Returns(new PropertyDescriptorCollection([new CustomPropertyDescriptor("Property", null)]));
Mock<TypeDescriptionProvider> customPropertyDescriptorProvider = new(MockBehavior.Strict);
customPropertyDescriptorProvider
.Setup(p => p.GetTypeDescriptor(instance.GetType(), instance))
.Returns(customTypeDescriptor.Object);
customPropertyDescriptorProvider
.Setup(p => p.GetCache(instance))
.Returns((IDictionary)null);
customPropertyDescriptorProvider
.Setup(p => p.GetExtendedTypeDescriptor(instance))
.Returns(customTypeDescriptor.Object);
TypeDescriptor.AddProvider(customPropertyDescriptorProvider.Object, instance);
yield return new object[] { instance, "Property", typeof(object) };
}
[Theory]
[MemberData(nameof(GetListItemType_Object_String_TestData))]
public void ListBindingHelper_GetListItemType_InvokeObjectString_ReturnsExpected(object list, string dataMember, Type expected)
{
Assert.Equal(expected, ListBindingHelper.GetListItemType(list, dataMember));
}
[Theory]
[InlineData(typeof(PrivateDefaultConstructor))]
[InlineData(typeof(NoDefaultConstructor))]
[InlineData(typeof(ThrowingDefaultConstructor))]
public void ListBindingHelper_GetListItemType_InvalidIListSourceType_ThrowsNotSupportedException(Type list)
{
Assert.Throws<NotSupportedException>(() => ListBindingHelper.GetListItemType(list));
Assert.Throws<NotSupportedException>(() => ListBindingHelper.GetListItemType(list, null));
Assert.Throws<NotSupportedException>(() => ListBindingHelper.GetListItemType(list, string.Empty));
Assert.Throws<NotSupportedException>(() => ListBindingHelper.GetListItemType(list, "dataMember"));
}
public static IEnumerable<object[]> GetListItemProperties_Object_TestData()
{
// Simple objects.
yield return new object[] { null, Array.Empty<string>() };
yield return new object[] { 1, Array.Empty<string>() };
yield return new object[] { typeof(int), Array.Empty<string>() };
yield return new object[] { string.Empty, new string[] { "Length" } };
yield return new object[] { typeof(string), new string[] { "Length" } };
yield return new object[] { new DataClass(), new string[] { "Property" } };
yield return new object[] { typeof(DataClass), new string[] { "Property" } };
// Lists.
yield return new object[] { new List<DataClass>(), new string[] { "Property" } };
yield return new object[] { typeof(IList<DataClass>), new string[] { "Property" } };
yield return new object[] { new List<ICustomTypeDescriptor>(), Array.Empty<string>() };
yield return new object[] { typeof(IList<ICustomTypeDescriptor>), new string[] { nameof(ICustomTypeDescriptor.RequireRegisteredTypes) } };
// Array.
yield return new object[] { Array.Empty<DataClass>(), new string[] { "Property" } };
yield return new object[] { typeof(DataClass[]), new string[] { "Property" } };
yield return new object[] { Array.Empty<object>(), Array.Empty<string>() };
yield return new object[] { new object[] { new DataClass() }, Array.Empty<string>() };
// Only implements IEnumerable.
yield return new object[] { new IEnumerableWrapper(Array.Empty<object>()), Array.Empty<string>() };
yield return new object[] { new IEnumerableWrapper(new object[] { new DataClass() }), new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new object[] { 1 }), new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new object[] { null }), Array.Empty<string>() };
// Only implements IList.
yield return new object[] { new ArrayList(), Array.Empty<string>() };
yield return new object[] { new ArrayList { new DataClass() }, new string[] { "Property" } };
yield return new object[] { new ArrayList { 1 }, Array.Empty<string>() };
yield return new object[] { new ArrayList { null }, Array.Empty<string>() };
// ITypedList.
Mock<ITypedList> mockTypedList = new(MockBehavior.Strict);
mockTypedList.Setup(t => t.GetItemProperties(null)).Returns(TypeDescriptor.GetProperties(typeof(DataClass)));
yield return new object[] { mockTypedList.Object, new string[] { "Property" } };
Mock<ITypedList> nullMockTypedList = new(MockBehavior.Strict);
nullMockTypedList.Setup(t => t.GetItemProperties(null)).Returns((PropertyDescriptorCollection)null);
yield return new object[] { nullMockTypedList.Object, null };
yield return new object[] { new EnumerableITypedListImplementor(), new string[] { "Property" } };
yield return new object[] { typeof(EnumerableITypedListImplementor), new string[] { "OtherProperty" } };
yield return new object[] { new NonEnumerableITypedListImplementor(), new string[] { "Property" } };
yield return new object[] { typeof(NonEnumerableITypedListImplementor), new string[] { "OtherProperty" } };
}
[Theory]
[MemberData(nameof(GetListItemProperties_Object_TestData))]
public void ListBindingHelper_GetListItemProperties_InvokeObject_ReturnsExpected(object list, string[] expected)
{
IEnumerable<PropertyDescriptor> properties = ListBindingHelper.GetListItemProperties(list)?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
// Null list accessors.
properties = ListBindingHelper.GetListItemProperties(list, null)?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
// Empty list accessors.
properties = ListBindingHelper.GetListItemProperties(list, Array.Empty<PropertyDescriptor>())?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
// Null data member.
properties = ListBindingHelper.GetListItemProperties(list, null, Array.Empty<PropertyDescriptor>())?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
// Empty data member.
properties = ListBindingHelper.GetListItemProperties(list, string.Empty, Array.Empty<PropertyDescriptor>())?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
}
public static IEnumerable<object[]> GetListItemProperties_Object_PropertyDescriptorArray_TestData()
{
yield return new object[] { null, TypeDescriptor.GetProperties(typeof(DataClass)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new DataClass(), TypeDescriptor.GetProperties(typeof(DataClass)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { typeof(DataClass), TypeDescriptor.GetProperties(typeof(DataClass)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new ListDataClass(), TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new ListDataClass() { ListProperty = [new()] }, TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { typeof(ListDataClass), TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new MultiListDataClass(), TypeDescriptor.GetProperties(typeof(MultiListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "ListProperty" } };
yield return new object[] { new MultiListDataClass { ParentListProperty = [new() { ListProperty = [new()] }] }, TypeDescriptor.GetProperties(typeof(MultiListDataClass)).Cast<PropertyDescriptor>().Take(0).ToArray(), new string[] { "ParentListProperty" } };
var inner = new PropertyDescriptor[] { TypeDescriptor.GetProperties(typeof(MultiListDataClass))[0], TypeDescriptor.GetProperties(typeof(ListDataClass))[0], TypeDescriptor.GetProperties(typeof(DataClass))[0] };
yield return new object[] { new MultiListDataClass { ParentListProperty = [new() { ListProperty = [new()] }] }, inner.Take(2).ToArray(), new string[] { "Property" } };
yield return new object[] { typeof(MultiListDataClass), TypeDescriptor.GetProperties(typeof(MultiListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "ListProperty" } };
yield return new object[] { typeof(DataClass), TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new DataClass(), new PropertyDescriptor[] { null }, Array.Empty<string>() };
yield return new object[] { typeof(DataClass), new PropertyDescriptor[] { null }, Array.Empty<string>() };
// Only implements IEnumerable.
PropertyDescriptor[] descriptors = TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray();
yield return new object[] { new IEnumerableWrapper(Array.Empty<object>()), descriptors, new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new object[] { new ListDataClass() }), descriptors, new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new object[] { new MultiListDataClass() }), inner.Take(2).ToArray(), new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new object[] { null }), descriptors, new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new ListDataClass[] { null }), descriptors, new string[] { "Property" } };
// Only implements IList.
yield return new object[] { new ArrayList(), descriptors, new string[] { "Property" } };
yield return new object[] { new ArrayList { new ListDataClass() }, descriptors, new string[] { "Property" } };
yield return new object[] { new ArrayList { new MultiListDataClass() }, inner.Take(0).ToArray(), new string[] { "ParentListProperty" } };
yield return new object[] { new ArrayList { new MultiListDataClass() }, inner.Take(2).ToArray(), new string[] { "Property" } };
yield return new object[] { new ArrayList { new MultiListDataClass { ParentListProperty = [] } }, inner.Take(0).ToArray(), new string[] { "ParentListProperty" } };
yield return new object[] { new ArrayList { new MultiListDataClass { ParentListProperty = [] } }, inner.Take(2).ToArray(), new string[] { "Property" } };
yield return new object[] { new ArrayList { new MultiListDataClass { ParentListProperty = [new() { ListProperty = [new()] }] } }, inner.Take(0).ToArray(), new string[] { "ParentListProperty" } };
yield return new object[] { new ArrayList { new MultiListDataClass { ParentListProperty = [new() { ListProperty = [new()] }] } }, inner.Take(2).ToArray(), new string[] { "Property" } };
yield return new object[] { new ArrayList { new MultiListDataClass { ParentListProperty = [new() { ListProperty = [new()] }] } }, inner.Take(3).ToArray(), Array.Empty<string>() };
yield return new object[] { new ArrayList { null }, descriptors, new string[] { "Property" } };
// ITypedList.
Mock<ITypedList> mockTypedList = new(MockBehavior.Strict);
mockTypedList.Setup(t => t.GetItemProperties(descriptors)).Returns(TypeDescriptor.GetProperties(typeof(DataClass)));
yield return new object[] { mockTypedList.Object, descriptors, new string[] { "Property" } };
Mock<ITypedList> nullMockTypedList = new(MockBehavior.Strict);
nullMockTypedList.Setup(t => t.GetItemProperties(descriptors)).Returns((PropertyDescriptorCollection)null);
yield return new object[] { nullMockTypedList.Object, descriptors, null };
yield return new object[] { new EnumerableITypedListImplementor(), descriptors, new string[] { "Property" } };
yield return new object[] { typeof(EnumerableITypedListImplementor), descriptors, new string[] { "Property" } };
yield return new object[] { new EnumerableITypedListImplementor[] { new() }, TypeDescriptor.GetProperties(typeof(EnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new List<EnumerableITypedListImplementor> { new() }, TypeDescriptor.GetProperties(typeof(EnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new ArrayList { new EnumerableITypedListImplementor() }, TypeDescriptor.GetProperties(typeof(EnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new IEnumerableWrapper(new object[] { new EnumerableITypedListImplementor() }), TypeDescriptor.GetProperties(typeof(EnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { typeof(EnumerableITypedListImplementor[]), descriptors, new string[] { "Property" } };
yield return new object[] { new NonEnumerableITypedListImplementor(), descriptors, new string[] { "Property" } };
yield return new object[] { typeof(NonEnumerableITypedListImplementor), descriptors, new string[] { "Property" } };
yield return new object[] { new NonEnumerableITypedListImplementor[] { new() }, TypeDescriptor.GetProperties(typeof(NonEnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new List<NonEnumerableITypedListImplementor> { new() }, TypeDescriptor.GetProperties(typeof(NonEnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new ArrayList { new NonEnumerableITypedListImplementor() }, TypeDescriptor.GetProperties(typeof(NonEnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { new IEnumerableWrapper(new object[] { new NonEnumerableITypedListImplementor() }), TypeDescriptor.GetProperties(typeof(NonEnumerableITypedListImplementor)).Cast<PropertyDescriptor>().ToArray(), Array.Empty<string>() };
yield return new object[] { typeof(NonEnumerableITypedListImplementor[]), descriptors, new string[] { "Property" } };
ITypedListDataClass typedListDataClass = new() { ListProperty = [new()] };
yield return new object[] { new ITypedListDataClass(), TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { typedListDataClass, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { typeof(ITypedListDataClass), TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new ITypedListDataClass[] { new() }, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new ITypedListDataClass[] { typedListDataClass }, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new List<ITypedListDataClass> { new() }, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new List<ITypedListDataClass> { typedListDataClass }, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new ArrayList { new ITypedListDataClass() }, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new ArrayList { typedListDataClass }, TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new IEnumerableWrapper(new object[] { new ITypedListDataClass() }), TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new IEnumerableWrapper(new object[] { typedListDataClass }), TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { typeof(ITypedListDataClass[]), TypeDescriptor.GetProperties(typeof(ITypedListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
ITypedListParent typedListParent = new() { ListProperty = new EnumerableITypedListImplementor() };
yield return new object[] { new ITypedListParent(), TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { typedListParent, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new ITypedListParent[] { new() }, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new ITypedListParent[] { typedListParent }, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new List<ITypedListParent> { new() }, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new List<ITypedListParent> { typedListParent }, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new ArrayList { new ITypedListParent() }, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new ArrayList { typedListParent }, TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { new IEnumerableWrapper(new object[] { new ITypedListParent() }), TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "OtherProperty" } };
yield return new object[] { new IEnumerableWrapper(new object[] { typedListParent }), TypeDescriptor.GetProperties(typeof(ITypedListParent)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
}
[Theory]
[MemberData(nameof(GetListItemProperties_Object_PropertyDescriptorArray_TestData))]
public void ListBindingHelper_GetListItemProperties_InvokeObjectPropertyDescriptorArray_ReturnsExpected(object list, PropertyDescriptor[] listAccessors, string[] expected)
{
IEnumerable<PropertyDescriptor> properties = ListBindingHelper.GetListItemProperties(list, listAccessors)?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
// Null data member.
properties = ListBindingHelper.GetListItemProperties(list, null, listAccessors)?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
// Empty data member.
properties = ListBindingHelper.GetListItemProperties(list, string.Empty, listAccessors)?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
}
public static IEnumerable<object[]> GetListItemProperties_Object_String_PropertyDescriptorArray_TestData()
{
yield return new object[] { new ListDataClass(), "ListProperty", null, new string[] { "Property" } };
yield return new object[] { new ListDataClass(), "listproperty", Array.Empty<PropertyDescriptor>(), new string[] { "Property" } };
yield return new object[] { new MultiListDataClass(), "ParentListProperty", TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
yield return new object[] { typeof(ListDataClass), "ListProperty", null, new string[] { "Property" } };
yield return new object[] { typeof(ListDataClass), "listproperty", Array.Empty<PropertyDescriptor>(), new string[] { "Property" } };
yield return new object[] { typeof(MultiListDataClass), "ParentListProperty", TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray(), new string[] { "Property" } };
}
[Theory]
[MemberData(nameof(GetListItemProperties_Object_String_PropertyDescriptorArray_TestData))]
public void ListBindingHelper_GetListItemProperties_InvokeObjectStringPropertyDescriptorArray_ReturnsExpected(object list, string dataMember, PropertyDescriptor[] listAccessors, string[] expected)
{
IEnumerable<PropertyDescriptor> properties = ListBindingHelper.GetListItemProperties(list, dataMember, listAccessors)?.Cast<PropertyDescriptor>();
Assert.Equal(expected, properties?.Select(p => p.Name));
}
public static IEnumerable<object[]> GetListItemProperties_NoSuchDataMember_TestData()
{
yield return new object[] { null, "name" };
yield return new object[] { typeof(DataClass), "NoSuchProperty" };
yield return new object[] { new DataClass(), "NoSuchProperty" };
}
[Theory]
[MemberData(nameof(GetListItemProperties_NoSuchDataMember_TestData))]
public void ListBindingHelper_GetListItemProperties_NoSuchDataMember_ThrowsArgumentException(object list, string dataMember)
{
Assert.Throws<ArgumentException>(() => ListBindingHelper.GetListItemProperties(list, dataMember, null));
}
[Fact]
public void ListBindingHelper_GetListItemProperties_InvalidListAccessors_ThrowsTargetException()
{
Assert.Throws<TargetInvocationException>(() => ListBindingHelper.GetListItemProperties(new DataClass(), TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray()));
Assert.Throws<TargetInvocationException>(() => ListBindingHelper.GetListItemProperties(new DataClass(), null, TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray()));
Assert.Throws<TargetInvocationException>(() => ListBindingHelper.GetListItemProperties(new DataClass(), string.Empty, TypeDescriptor.GetProperties(typeof(ListDataClass)).Cast<PropertyDescriptor>().ToArray()));
}
public static IEnumerable<object[]> GetListName_TestData()
{
yield return new object[] { null, null, string.Empty };
Mock<ITypedList> mockTypedList = new(MockBehavior.Strict);
mockTypedList.Setup(t => t.GetListName(null)).Returns("Name");
yield return new object[] { mockTypedList.Object, null, "Name" };
yield return new object[] { 1, null, "Int32" };
yield return new object[] { typeof(int), null, "Int32" };
yield return new object[] { 1, Array.Empty<PropertyDescriptor>(), "Int32" };
yield return new object[] { typeof(int), Array.Empty<PropertyDescriptor>(), "Int32" };
yield return new object[] { 1, new PropertyDescriptor[] { null }, "Int32" };
yield return new object[] { typeof(int), new PropertyDescriptor[] { null }, "Int32" };
yield return new object[] { Array.Empty<int>(), null, "Int32" };
yield return new object[] { typeof(int[]), null, "Int32" };
yield return new object[] { Array.Empty<object>(), null, "Object" };
yield return new object[] { typeof(object[]), null, "Object" };
yield return new object[] { new List<int>(), null, "Int32" };
yield return new object[] { typeof(List<int>), null, "Int32" };
yield return new object[] { new List<object>(), null, "List`1" };
yield return new object[] { typeof(List<object>), null, "List`1" };
yield return new object[] { typeof(Array), null, "Array" };
yield return new object[] { typeof(IList), null, "IList" };
yield return new object[] { typeof(IList<int>), null, "IList`1" };
yield return new object[] { typeof(IEnumerable), null, "IEnumerable" };
yield return new object[] { typeof(IEnumerable), new PropertyDescriptor[] { TypeDescriptor.GetProperties(typeof(DataClass))[0] }, "Int32" };
}
[Theory]
[MemberData(nameof(GetListName_TestData))]
public void ListBindingHelper_GetListName_Invoke_ReturnsExpected(object list, PropertyDescriptor[] listAccessors, string expected)
{
Assert.Equal(expected, ListBindingHelper.GetListName(list, listAccessors));
}
private class ClassWithItem
{
public string this[int i]
{
get => null;
}
}
private class CustomCurrencyManagerProvider : ICurrencyManagerProvider
{
public CurrencyManager CurrencyManagerResult { get; set; }
public CurrencyManager CurrencyManager => CurrencyManagerResult;
public CurrencyManager GetRelatedCurrencyManager(string dataMember)
{
throw new NotImplementedException();
}
public int Property { get; set; }
}
private class GenericIListSourceClassWithItem : List<int>, IListSource
{
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => throw new NotImplementedException();
}
private class NonGenericIListSourceClassWithItem : ArrayList, IListSource
{
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
throw new NotSupportedException();
}
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => throw new NotImplementedException();
}
private class ITypedListClassWithItem : ClassWithItem, ITypedList
{
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
throw new NotSupportedException();
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
throw new NotSupportedException();
}
}
private class ITypedListClassWithCustomNamedItem : ITypedList
{
[IndexerName("CustomName")]
public string this[int i]
{
get => null;
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
throw new NotSupportedException();
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
throw new NotSupportedException();
}
}
private class OnlyGenericIListClassWithItem : ClassWithItem, IList<int>
{
int IList<int>.this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int Count => throw new NotImplementedException();
public bool IsReadOnly => throw new NotImplementedException();
public void Add(int item) => throw new NotImplementedException();
public void Clear() => throw new NotImplementedException();
public bool Contains(int item) => throw new NotImplementedException();
public void CopyTo(int[] array, int arrayIndex) => throw new NotImplementedException();
public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
public int IndexOf(int item) => throw new NotImplementedException();
public void Insert(int index, int item) => throw new NotImplementedException();
public bool Remove(int item) => throw new NotImplementedException();
public void RemoveAt(int index) => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
}
private class PrivateDefaultConstructor : IListSource
{
private PrivateDefaultConstructor() { }
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => new int[] { 1 };
}
private class NoDefaultConstructor : IListSource
{
#pragma warning disable IDE0060 // Remove unused parameter
public NoDefaultConstructor(int i) { }
#pragma warning restore IDE0060
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => new int[] { 1 };
}
private class ThrowingDefaultConstructor : IListSource
{
public ThrowingDefaultConstructor()
{
throw new DivideByZeroException();
}
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => new int[] { 1 };
}
private class ClassWithIListSource : IListSource
{
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => new int[] { 1 };
}
private class NullIListSource : IListSource
{
public bool ContainsListCollection => throw new NotImplementedException();
public IList GetList() => null;
}
// Type that wraps an enumerable but ONLY implements non-generic IEnumerable.
private class IEnumerableWrapper : IEnumerable
{
public IEnumerableWrapper(IList list)
{
List = list;
}
private IList List { get; }
public IEnumerator GetEnumerator() => List?.GetEnumerator();
public int Property { get; set; }
}
private class NotSupportedIEnumerable : IEnumerable
{
public IEnumerator GetEnumerator() => throw new NotSupportedException();
}
private class DataClass
{
public int Property { get; set; }
}
private class ListDataClass
{
public List<DataClass> ListProperty { get; set; }
}
private class MultiListDataClass
{
public List<ListDataClass> ParentListProperty { get; set; }
}
private class ITypedListDataClass
{
public List<EnumerableITypedListImplementor> ListProperty { get; set; }
}
private class ITypedListParent
{
public EnumerableITypedListImplementor ListProperty { get; set; }
}
private class NonEnumerableITypedListImplementor : ITypedList
{
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return TypeDescriptor.GetProperties(typeof(DataClass));
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
throw new NotImplementedException();
}
public int OtherProperty { get; set; }
}
private class EnumerableITypedListImplementor : ITypedList, IEnumerable
{
public IEnumerator GetEnumerator()
{
return null;
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return TypeDescriptor.GetProperties(typeof(DataClass));
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
throw new NotImplementedException();
}
public int OtherProperty { get; set; }
}
private class ICustomTypeDescriptorPropertyClass
{
public ICustomTypeDescriptor Property { get; set; }
}
private class ICustomTypeDescriptorPropertyDescriptorClass
{
public int Property { get; set; }
}
private class CustomPropertyDescriptor : PropertyDescriptor
{
public CustomPropertyDescriptor(string name, Attribute[] attrs) : base(name, attrs)
{
}
public override Type PropertyType => new CustomTypeDescriptorType();
public override Type ComponentType => throw new NotImplementedException();
public override bool IsReadOnly => throw new NotImplementedException();
public override bool CanResetValue(object component) => throw new NotImplementedException();
public override object GetValue(object component) => throw new NotImplementedException();
public override void ResetValue(object component) => throw new NotImplementedException();
public override void SetValue(object component, object value)
{
throw new NotImplementedException();
}
public override bool ShouldSerializeValue(object component)
{
throw new NotImplementedException();
}
}
public class CustomTypeDescriptorType : Type, ICustomTypeDescriptor
{
public override Guid GUID => throw new NotImplementedException();
public override Module Module => throw new NotImplementedException();
public override Assembly Assembly => throw new NotImplementedException();
public override string FullName => throw new NotImplementedException();
public override string Namespace => throw new NotImplementedException();
public override string AssemblyQualifiedName => throw new NotImplementedException();
public override Type BaseType => throw new NotImplementedException();
public override Type UnderlyingSystemType => throw new NotImplementedException();
public override string Name => throw new NotImplementedException();
public AttributeCollection GetAttributes()
{
throw new NotImplementedException();
}
public string GetClassName()
{
throw new NotImplementedException();
}
public string GetComponentName()
{
throw new NotImplementedException();
}
public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public TypeConverter GetConverter()
{
throw new NotImplementedException();
}
public override object[] GetCustomAttributes(bool inherit)
{
throw new NotImplementedException();
}
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
throw new NotImplementedException();
}
public EventDescriptor GetDefaultEvent()
{
throw new NotImplementedException();
}
public PropertyDescriptor GetDefaultProperty()
{
throw new NotImplementedException();
}
public object GetEditor(Type editorBaseType)
{
throw new NotImplementedException();
}
public override Type GetElementType() => throw new NotImplementedException();
public override EventInfo GetEvent(string name, BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override EventInfo[] GetEvents(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
throw new NotImplementedException();
}
public override FieldInfo GetField(string name, BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override Type GetInterface(string name, bool ignoreCase)
{
throw new NotImplementedException();
}
public override Type[] GetInterfaces() => throw new NotImplementedException();
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override Type GetNestedType(string name, BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override Type[] GetNestedTypes(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
throw new NotImplementedException();
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
throw new NotImplementedException();
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
throw new NotImplementedException();
}
public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
throw new NotImplementedException();
}
public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException();
protected override TypeAttributes GetAttributeFlagsImpl() => throw new NotImplementedException();
protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
{
throw new NotImplementedException();
}
protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
{
throw new NotImplementedException();
}
protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
{
throw new NotImplementedException();
}
protected override bool HasElementTypeImpl() => throw new NotImplementedException();
protected override bool IsArrayImpl() => throw new NotImplementedException();
protected override bool IsByRefImpl() => throw new NotImplementedException();
protected override bool IsCOMObjectImpl() => throw new NotImplementedException();
protected override bool IsPointerImpl() => throw new NotImplementedException();
protected override bool IsPrimitiveImpl() => throw new NotImplementedException();
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
throw new NotImplementedException();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
throw new NotImplementedException();
}
}
}
|