using NUnit.Framework; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Reflection.Emit; namespace OpenTap.UnitTests { public class TypeDataTest { public class IntObject { public int SomeInt { get; set; } } [Browsable(false)] class Class1 { } class Class2 : Class1 { } [Test] public void InheritedAttributesTest() { var type1 = TypeData.FromType(typeof(Class1)); var type2 = TypeData.FromType(typeof(Class2)); Assert.IsEmpty(type2.Attributes); Assert.IsTrue(type1.HasAttribute()); Assert.IsTrue(type2.BaseType.HasAttribute()); } [Test] public void SettingPropertyToInvalidType() { var obj = new IntObject(); var typeData = TypeData.GetTypeData(obj); var memberData = typeData.GetMember(nameof(IntObject.SomeInt)); Assert.DoesNotThrow(() => memberData.SetValue(obj, 123)); // PropertyInfo.SetValue(xx, null) does not throw an exception for value types. // https://docs.microsoft.com/de-de/dotnet/api/system.reflection.propertyinfo.setvalue Assert.DoesNotThrow(() => memberData.SetValue(obj, null)); } [Test] public void ValueTypeCanCreateInstanceTest() { var types = new[] { typeof(int), typeof(double), typeof(DateTime), typeof(TimeSpan), typeof(KeyValuePair), }; foreach (var type in types) { var td = TypeData.FromType(type); Assert.AreEqual(type.IsValueType, td.IsValueType); Assert.IsTrue(td.CanCreateInstance, $"Expected type {type.FullName} to be constructable."); Assert.AreEqual(Activator.CreateInstance(type), td.CreateInstance()); } } [Test] public void DynamicTypeBrowsableFalse() { // Create an empty dynamic type which is public. var assemblyName = "TestAssembly"; var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("TestModule"); TypeBuilder typeBuilder = moduleBuilder.DefineType("TestType", TypeAttributes.Public | TypeAttributes.Class, typeof(object),Array.Empty()); var type = typeBuilder.CreateType(); var td = TypeData.FromType(type); // A problem caused these kinds of type data to have IsBrowsable set to false, even though it does not // even have a BrowsableAttribute assigned. Assert.IsTrue(td.IsBrowsable); foreach (var browsable in new[] { true, false }) { TypeBuilder typeBuilder2 = moduleBuilder.DefineType("TestType2_" + browsable, TypeAttributes.Public | TypeAttributes.Class, typeof(object), Array.Empty()); CustomAttributeBuilder attrBuilder = new CustomAttributeBuilder( typeof(BrowsableAttribute).GetConstructor(new[] { typeof(bool) }), new object[] { browsable }); typeBuilder2.SetCustomAttribute(attrBuilder); var td2 = TypeData.FromType(typeBuilder2.CreateType()); Assert.AreEqual(browsable, td2.IsBrowsable); } } class MultipleEnumerableInterfacesList : List, IEnumerable { IEnumerator IEnumerable.GetEnumerator() { foreach (var value in this) yield return (int)value; } } class MultipleEnumerableInterfacesList2 : IEnumerable, IEnumerable, IEnumerable { IEnumerator IEnumerable.GetEnumerator() { yield return "1"; } IEnumerator IEnumerable.GetEnumerator() { yield return 1; } IEnumerator IEnumerable.GetEnumerator() { yield return 1.0; } IEnumerator IEnumerable.GetEnumerator() { yield return 1.0; } } [Test] public void TestMultipleEnumerableInterfaceImplementations() { Assert.IsNull(TypeData.FromType(typeof(MultipleEnumerableInterfacesList)).ElementType); Assert.IsNull(TypeData.FromType(typeof(MultipleEnumerableInterfacesList2)).ElementType); Assert.AreEqual(TypeData.FromType(typeof(List)).AsTypeData().ElementType.Type, typeof(double)); Assert.AreEqual(TypeData.FromType(typeof(IEnumerable)).AsTypeData().ElementType.Type, typeof(double)); } } }