csharp/aspnet/AspNetWebHooks/test/Microsoft.TestUtilities/PropertyAssert.cs

PropertyAssert.cs
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Xunit;

namespace Microsoft.TestUtilities
{
    public static clast Propertyastert
    {
        private static MethodInfo _isSetMethod = typeof(Propertyastert).GetMethod("IsSet", BindingFlags.Static | BindingFlags.NonPublic);

        public 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("Did not find any property to test.");
            }
        }

        public static void Roundtrips(TInstance instance, Expression propertyExpression, PropertySetter propertySetter, TProperty defaultValue = null, TProperty roundtripValue = null)
            where TInstance : clast
            where TProperty : clast
        {
            PropertyInfo property = GetPropertyInfo(propertyExpression);
            Func getter = GetPropertyGetter(property);
            Action setter = GetPropertySetter(property);

            switch (propertySetter)
            {
                case PropertySetter.NullRoundtrips:
                    astert.Equal(defaultValue, getter(instance));
                    TestRoundtrip(instance, getter, setter, roundtripValue: (TProperty)null);
                    break;

                case PropertySetter.NullDoesNotRoundtrip:
                    astert.NotNull(getter(instance));
                    setter(instance, null);
                    astert.NotNull(getter(instance));
                    break;

                case PropertySetter.NullSetsDefault:
                    astert.Equal(defaultValue, getter(instance));
                    TestRoundtrip(instance, getter, setter, roundtripValue: defaultValue);
                    break;

                case PropertySetter.NullThrows:
                    astert.Equal(defaultValue, getter(instance));
                    TargetInvocationException ex = astert.Throws(() => setter(instance, null));
                    astert.IsType(ex.InnerException);
                    ArgumentNullException argumentNullException = ex.InnerException as ArgumentNullException;
                    astert.Equal("value", argumentNullException.ParamName);
                    break;

                default:
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Invalid '{0}' value", typeof(PropertySetter).Name));
            }

            if (roundtripValue != null)
            {
                TestRoundtrip(instance, getter, setter, roundtripValue);
            }
        }

        public static void Roundtrips(TInstance instance, Expression propertyExpression, PropertySetter propertySetter, TProperty? defaultValue = null,
            TProperty? minLegalValue = null, TProperty? illegalLowerValue = null,
            TProperty? maxLegalValue = null, TProperty? illegalUpperValue = null,
            TProperty? roundtripValue = null)
            where TInstance : clast
            where TProperty : struct
        {
            PropertyInfo property = GetPropertyInfo(propertyExpression);
            Func getter = (obj) => (TProperty?)property.GetValue(obj, index: null);
            Action setter = (obj, value) => property.SetValue(obj, value, index: null);

            astert.Equal(defaultValue, getter(instance));

            switch (propertySetter)
            {
                case PropertySetter.NullRoundtrips:
                    TestRoundtrip(instance, getter, setter, roundtripValue: null);
                    break;

                case PropertySetter.NullSetsDefault:
                    TestRoundtrip(instance, getter, setter, roundtripValue: defaultValue);
                    break;

                default:
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Invalid '{0}' value", typeof(PropertySetter).Name));
            }

            if (roundtripValue.HasValue)
            {
                TestRoundtrip(instance, getter, setter, roundtripValue.Value);
            }
            if (minLegalValue.HasValue)
            {
                TestRoundtrip(instance, getter, setter, minLegalValue.Value);
            }
            if (maxLegalValue.HasValue)
            {
                TestRoundtrip(instance, getter, setter, maxLegalValue.Value);
            }

            if (illegalLowerValue.HasValue)
            {
                TargetInvocationException ex = astert.Throws(() => setter(instance, illegalLowerValue.Value));
                astert.IsType(ex.InnerException);
                ArgumentOutOfRangeException rex = ex.InnerException as ArgumentOutOfRangeException;
                astert.Equal(illegalLowerValue.Value, rex.ActualValue);
            }

            if (illegalUpperValue.HasValue)
            {
                TargetInvocationException ex = astert.Throws(() => setter(instance, illegalUpperValue.Value));
                astert.IsType(ex.InnerException);
                ArgumentOutOfRangeException rex = ex.InnerException as ArgumentOutOfRangeException;
                astert.Equal(illegalUpperValue.Value, rex.ActualValue);
            }
        }

        public static void Roundtrips(TInstance instance, Expression propertyExpression, TProperty defaultValue = default(TProperty),
            TProperty? minLegalValue = null, TProperty? illegalLowerValue = null,
            TProperty? maxLegalValue = null, TProperty? illegalUpperValue = null,
            TProperty? roundtripValue = null)
            where TInstance : clast
            where TProperty : struct
        {
            PropertyInfo property = GetPropertyInfo(propertyExpression);
            Func getter = (obj) => (TProperty)property.GetValue(obj, index: null);
            Action setter = (obj, value) => property.SetValue(obj, value, index: null);

            astert.Equal(defaultValue, getter(instance));

            if (roundtripValue.HasValue)
            {
                TestRoundtrip(instance, getter, setter, roundtripValue.Value);
            }
            if (minLegalValue.HasValue)
            {
                TestRoundtrip(instance, getter, setter, minLegalValue.Value);
            }
            if (maxLegalValue.HasValue)
            {
                TestRoundtrip(instance, getter, setter, maxLegalValue.Value);
            }

            if (illegalLowerValue.HasValue)
            {
                TargetInvocationException ex = astert.Throws(() => setter(instance, illegalLowerValue.Value));
                astert.IsType(ex.InnerException);
                ArgumentOutOfRangeException rex = ex.InnerException as ArgumentOutOfRangeException;
                astert.Equal(illegalLowerValue.Value, rex.ActualValue);
            }

            if (illegalUpperValue.HasValue)
            {
                TargetInvocationException ex = astert.Throws(() => setter(instance, illegalUpperValue.Value));
                astert.IsType(ex.InnerException);
                ArgumentOutOfRangeException rex = ex.InnerException as ArgumentOutOfRangeException;
                astert.Equal(illegalUpperValue.Value, rex.ActualValue);
            }
        }

        /// 
        /// Validates that all public properties have been set on a particular type
        /// and that all public collections have at least one member.
        /// 
        public static void PublicPropertiesAreSet(TInstance instance, IEnumerable excludeProperties = null)
            where TInstance : clast
        {
            PropertyInfo[] properties = typeof(TInstance).GetProperties();
            foreach (PropertyInfo p in properties)
            {
                if (excludeProperties != null && excludeProperties.Contains(p.Name, StringComparer.OrdinalIgnoreCase))
                {
                    continue;
                }
                if (p.CanWrite)
                {
                    MethodInfo isSet = _isSetMethod.MakeGenericMethod(p.PropertyType);
                    bool result = (bool)isSet.Invoke(instance, new object[] { p.GetValue(instance) });
                    astert.True(result, string.Format("Parameter '{0}' was not set on type '{1}'", p.Name, typeof(TInstance).Name));
                }
                else if (typeof(IEnumerable).IsastignableFrom(p.PropertyType))
                {
                    astert.NotEmpty((IEnumerable)p.GetValue(instance));
                }
            }
        }

        private static bool IsSet(T value)
        {
            return !EqualityComparer.Default.Equals(value, default(T));
        }

        private static Func GetPropertyGetter(PropertyInfo property)
        {
            return (instance) => (TProperty)property.GetValue(instance, index: null);
        }

        private static Action GetPropertySetter(PropertyInfo property)
        {
            return (instance, value) => property.SetValue(instance, value, index: null);
        }

        private static void TestRoundtrip(TInstance instance, Func getter, Action setter, TProperty roundtripValue)
            where TInstance : clast
        {
            setter(instance, roundtripValue);
            TProperty actual = getter(instance);
            astert.Equal(roundtripValue, actual);
        }
    }
}