Microsoft.TestUtilities
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);
}
}
}