// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System; using System.Linq.Expressions; using System.Reflection; using Assert = Microsoft.TestCommon.AssertEx; namespace Microsoft.TestCommon { public class ReflectionAssert { private static PropertyInfo GetPropertyInfo(Expression> property) { if (property.Body is MemberExpression) { return (PropertyInfo)((MemberExpression)property.Body).Member; } else if (property.Body is UnaryExpression && property.Body.NodeType == ExpressionType.Convert) { return (PropertyInfo)((MemberExpression)((UnaryExpression)property.Body).Operand).Member; } else { throw new InvalidOperationException("Could not determine property from lambda expression."); } } private static void TestPropertyValue(TInstance instance, Func getFunc, Action setFunc, TValue valueToSet, TValue valueToCheck) { setFunc(instance, valueToSet); TValue newValue = getFunc(instance); Assert.Equal(valueToCheck, newValue); } private static void TestPropertyValue(TInstance instance, Func getFunc, Action setFunc, TValue value) { TestPropertyValue(instance, getFunc, setFunc, value, value); } public void Property(T instance, Expression> propertyGetter, TResult expectedDefaultValue, bool allowNull = false, TResult roundTripTestValue = null) where TResult : class { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); if (allowNull) { TestPropertyValue(instance, getFunc, setFunc, null); } else { Assert.ThrowsArgumentNull(() => { setFunc(instance, null); }, "value"); } if (roundTripTestValue != null) { TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } } public void IntegerProperty(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult? minLegalValue, TResult? illegalLowerValue, TResult? maxLegalValue, TResult? illegalUpperValue, TResult roundTripTestValue) where TResult : struct { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); if (minLegalValue.HasValue) { TestPropertyValue(instance, getFunc, setFunc, minLegalValue.Value); } if (maxLegalValue.HasValue) { TestPropertyValue(instance, getFunc, setFunc, maxLegalValue.Value); } if (illegalLowerValue.HasValue) { Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", minLegalValue.Value.ToString(), illegalLowerValue.Value); } if (illegalUpperValue.HasValue) { Assert.ThrowsArgumentLessThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", maxLegalValue.Value.ToString(), illegalUpperValue.Value); } TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } public void BooleanProperty(T instance, Expression> propertyGetter, bool expectedDefaultValue) { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (bool)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); TestPropertyValue(instance, getFunc, setFunc, !expectedDefaultValue); } public void EnumProperty(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult illegalValue, TResult roundTripTestValue) where TResult : struct { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); Assert.ThrowsInvalidEnumArgument(() => { setFunc(instance, illegalValue); }, "value", Convert.ToInt32(illegalValue), typeof(TResult)); TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } public void StringProperty(T instance, Expression> propertyGetter, string expectedDefaultValue, bool allowNullAndEmpty = true, string nullAndEmptyReturnValue = "") { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (string)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); if (allowNullAndEmpty) { // Assert get/set works for null TestPropertyValue(instance, getFunc, setFunc, null, nullAndEmptyReturnValue); // Assert get/set works for String.Empty TestPropertyValue(instance, getFunc, setFunc, String.Empty, nullAndEmptyReturnValue); } else { Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, getFunc, setFunc, null); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, getFunc, setFunc, String.Empty); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); } // Assert get/set works for arbitrary value TestPropertyValue(instance, getFunc, setFunc, "TestValue"); } } }