using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Web.TestUtil { public static class MemberHelper { private static ConstructorInfo GetConstructorInfo(object instance, Type[] parameterTypes) { if (instance == null) { throw new ArgumentNullException("instance"); } ConstructorInfo constructorInfo = instance.GetType().GetConstructor(parameterTypes); if (constructorInfo == null) { throw new ArgumentException(String.Format( "A matching constructor on type '{0}' could not be found.", instance.GetType().FullName)); } return constructorInfo; } private static EventInfo GetEventInfo(object instance, string eventName) { if (instance == null) { throw new ArgumentNullException("instance"); } if (String.IsNullOrEmpty(eventName)) { throw new ArgumentException("An event must be specified.", "eventName"); } EventInfo eventInfo = instance.GetType().GetEvent(eventName); if (eventInfo == null) { throw new ArgumentException(String.Format( "An event named '{0}' on type '{1}' could not be found.", eventName, instance.GetType().FullName)); } return eventInfo; } private static MethodInfo GetMethodInfo(object instance, string methodName, Type[] types = null, MethodAttributes attrs = MethodAttributes.Public) { if (instance == null) { throw new ArgumentNullException("instance"); } if (String.IsNullOrEmpty(methodName)) { throw new ArgumentException("A method must be specified.", "methodName"); } MethodInfo methodInfo; if (types != null) { methodInfo = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); } else { methodInfo = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (methodInfo == null) { throw new ArgumentException(String.Format( "A method named '{0}' on type '{1}' could not be found.", methodName, instance.GetType().FullName)); } if ((methodInfo.Attributes & attrs) != attrs) { throw new ArgumentException(String.Format( "Method '{0}' on type '{1}' with attributes '{2}' does not match the attributes '{3}'.", methodName, instance.GetType().FullName, methodInfo.Attributes, attrs)); } return methodInfo; } private static PropertyInfo GetPropertyInfo(object instance, string propertyName) { if (instance == null) { throw new ArgumentNullException("instance"); } if (String.IsNullOrEmpty(propertyName)) { throw new ArgumentException("A property must be specified.", "propertyName"); } PropertyInfo propInfo = instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (propInfo == null) { throw new ArgumentException(String.Format( "A property named '{0}' on type '{1}' could not be found.", propertyName, instance.GetType().FullName)); } return propInfo; } private static void TestAttribute(MemberInfo memberInfo, TAttribute attributeValue) where TAttribute : Attribute { object[] attrs = memberInfo.GetCustomAttributes(typeof(TAttribute), true); if (attributeValue == null) { Assert.True(attrs.Length == 0, "Member should not have an attribute of type " + typeof(TAttribute)); } else { Assert.True(attrs != null && attrs.Length > 0, "Member does not have an attribute of type " + typeof(TAttribute)); Assert.Equal(attributeValue, attrs[0]); } } public static void TestBooleanProperty(object instance, string propertyName, bool initialValue, bool testDefaultValue) { // Assert initial value TestGetPropertyValue(instance, propertyName, initialValue); if (testDefaultValue) { // Assert DefaultValue attribute matches inital value TestDefaultValue(instance, propertyName); } TestPropertyValue(instance, propertyName, true); TestPropertyValue(instance, propertyName, false); } public static void TestDefaultValue(object instance, string propertyName) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); object initialValue = propInfo.GetValue(instance, null); TestAttribute(propInfo, new DefaultValueAttribute(initialValue)); } public static void TestEvent(object instance, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs { EventInfo eventInfo = GetEventInfo(instance, eventName); // Assert category "Action" TestAttribute(eventInfo, new CategoryAttribute("Action")); // Call protected method with no event handlers, assert no error MethodInfo methodInfo = GetMethodInfo(instance, "On" + eventName, attrs: MethodAttributes.Family | MethodAttributes.Virtual); methodInfo.Invoke(instance, new object[] { eventArgs }); // Attach handler, call method, assert fires once List eventHandlerArgs = new List(); EventHandler handler = new EventHandler(delegate(object sender, TEventArgs t) { eventHandlerArgs.Add(sender); eventHandlerArgs.Add(t); }); eventInfo.AddEventHandler(instance, handler); methodInfo.Invoke(instance, new object[] { eventArgs }); Assert.Equal(new[] { instance, eventArgs }, eventHandlerArgs.ToArray()); // Detach handler, call method, assert not fired eventHandlerArgs = new List(); eventInfo.RemoveEventHandler(instance, handler); methodInfo.Invoke(instance, new object[] { eventArgs }); Assert.Empty(eventHandlerArgs); } public static void TestGetPropertyValue(object instance, string propertyName, object valueToCheck) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); object value = propInfo.GetValue(instance, null); Assert.Equal(valueToCheck, value); } public static void TestEnumProperty(object instance, string propertyName, TEnumValue initialValue, bool testDefaultValue) { // Assert initial value TestGetPropertyValue(instance, propertyName, initialValue); if (testDefaultValue) { // Assert DefaultValue attribute matches inital value TestDefaultValue(instance, propertyName); } PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); // Values are sorted numerically TEnumValue[] values = (TEnumValue[])Enum.GetValues(propInfo.PropertyType); // Assert get/set works for all valid enum values foreach (TEnumValue value in values) { TestPropertyValue(instance, propertyName, value); } // Assert ArgumentOutOfRangeException is thrown for value one less than smallest // enum value, and one more than largest enum value var targetException = Assert.Throws(() => propInfo.SetValue(instance, Convert.ToInt32(values[0]) - 1, null)); Assert.IsType(targetException.InnerException); targetException = Assert.Throws(() => propInfo.SetValue(instance, Convert.ToInt32(values[values.Length - 1]) + 1, null)); Assert.IsType(targetException.InnerException); } public static void TestInt32Property(object instance, string propertyName, int value1, int value2) { TestPropertyValue(instance, propertyName, value1); TestPropertyValue(instance, propertyName, value2); } public static void TestPropertyWithDefaultInstance(object instance, string propertyName, object valueToSet) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); // Set to explicit property propInfo.SetValue(instance, valueToSet, null); object value = propInfo.GetValue(instance, null); Assert.Equal(valueToSet, value); // Set to null propInfo.SetValue(instance, null, null); value = propInfo.GetValue(instance, null); Assert.IsAssignableFrom(propInfo.PropertyType, value); } public static void TestPropertyWithDefaultInstance(object instance, string propertyName, object valueToSet, object defaultValue) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); // Set to explicit property propInfo.SetValue(instance, valueToSet, null); object value = propInfo.GetValue(instance, null); Assert.Same(valueToSet, value); // Set to null propInfo.SetValue(instance, null, null); value = propInfo.GetValue(instance, null); Assert.Equal(defaultValue, value); } public static void TestPropertyValue(object instance, string propertyName, object value) { TestPropertyValue(instance, propertyName, value, value); } public static void TestPropertyValue(object instance, string propertyName, object valueToSet, object valueToCheck) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); propInfo.SetValue(instance, valueToSet, null); object value = propInfo.GetValue(instance, null); Assert.Equal(valueToCheck, value); } public static void TestStringParams(object instance, Type[] constructorParameterTypes, object[] parameters) { ConstructorInfo ctor = GetConstructorInfo(instance, constructorParameterTypes); TestStringParams(ctor, instance, parameters); } public static void TestStringParams(object instance, string methodName, object[] parameters) { TestStringParams(instance, methodName, null, parameters); } public static void TestStringParams(object instance, string methodName, Type[] types, object[] parameters) { MethodInfo method = GetMethodInfo(instance, methodName, types); TestStringParams(method, instance, parameters); } private static void TestStringParams(MethodBase method, object instance, object[] parameters) { ParameterInfo[] parameterInfos = method.GetParameters(); foreach (ParameterInfo parameterInfo in parameterInfos) { if (parameterInfo.ParameterType == typeof(String)) { object originalParameter = parameters[parameterInfo.Position]; parameters[parameterInfo.Position] = null; Assert.ThrowsArgumentNullOrEmpty( delegate() { try { method.Invoke(instance, parameters); } catch (TargetInvocationException e) { throw e.InnerException; } }, parameterInfo.Name); parameters[parameterInfo.Position] = String.Empty; Assert.ThrowsArgumentNullOrEmpty( delegate() { try { method.Invoke(instance, parameters); } catch (TargetInvocationException e) { throw e.InnerException; } }, parameterInfo.Name); parameters[parameterInfo.Position] = originalParameter; } } } public static void TestStringProperty(object instance, string propertyName, string initialValue, bool testDefaultValueAttribute = false, bool allowNullAndEmpty = true, string nullAndEmptyReturnValue = "") { // Assert initial value TestGetPropertyValue(instance, propertyName, initialValue); if (testDefaultValueAttribute) { // Assert DefaultValue attribute matches inital value TestDefaultValue(instance, propertyName); } if (allowNullAndEmpty) { // Assert get/set works for null TestPropertyValue(instance, propertyName, null, nullAndEmptyReturnValue); // Assert get/set works for String.Empty TestPropertyValue(instance, propertyName, String.Empty, nullAndEmptyReturnValue); } else { Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, propertyName, null); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, propertyName, String.Empty); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); } // Assert get/set works for arbitrary value TestPropertyValue(instance, propertyName, "TestValue"); } } }