Skip to content

Instantly share code, notes, and snippets.

@zapthedingbat
Created November 4, 2014 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zapthedingbat/16c9e91928bfecdecb0d to your computer and use it in GitHub Desktop.
Save zapthedingbat/16c9e91928bfecdecb0d to your computer and use it in GitHub Desktop.
NUnit Has.Attribute<T> Constraint asserts an attribute is applied to a memberInfo with a given set of construction arguments.
public static class Has
{
public static IResolveConstraint Attribute<T>() where T : class
{
return new Constraints.AttributeConstraint<T>(new Dictionary<string, object>(), new object[0]);
}
public static IResolveConstraint Attribute<T>(IDictionary<string, object> namedConstructorArguments, params object[] constructorArguments) where T : class
{
return new Constraints.AttributeConstraint<T>(namedConstructorArguments, constructorArguments);
}
}
public static class Constraints
{
public class AttributeConstraint<T> : Constraint where T : class
{
private readonly object[] _expectedConstructorArguments;
private readonly IDictionary<string, object> _expectedNamedConstructorArguments;
private Type _expectedAttributeType;
private IList<CustomAttributeData> _actualAttributes;
public AttributeConstraint(IDictionary<string, object> namedConstructorArguments, object[] constructorArguments)
{
_expectedNamedConstructorArguments = namedConstructorArguments ?? new Dictionary<string, object>();
_expectedConstructorArguments = constructorArguments;
}
public override bool Matches(object actual)
{
_expectedAttributeType = typeof(T);
var member = actual as MemberInfo;
if (member == null)
{
return false;
}
var isMatched = false;
_actualAttributes = CustomAttributeData.GetCustomAttributes(member);
foreach (var data in _actualAttributes)
{
var attributeType = data.Constructor.DeclaringType;
if (attributeType == _expectedAttributeType)
{
var actualConstructorArguments = data.ConstructorArguments.ToArray();
var actualNamedConstructorArguments = data.NamedArguments.ToDictionary(x => x.MemberInfo.Name, x => x.TypedValue);
if (MatchesConstructorArguments(actualConstructorArguments) && MatchesNamedConstructorArguments(actualNamedConstructorArguments))
{
isMatched = true;
break;
}
}
}
return isMatched;
}
// Check the actual Constructor Arguments arguments match the expected ones
private bool MatchesConstructorArguments(CustomAttributeTypedArgument[] actualConstructorArguments)
{
if (_expectedConstructorArguments.Length != actualConstructorArguments.Length)
{
return false;
}
for (var i = 0; i < _expectedConstructorArguments.Length; i++)
{
var actualArgument = actualConstructorArguments[i];
var expectedArgumentValue = _expectedConstructorArguments[i];
if (!MatchArgument(actualArgument, expectedArgumentValue))
{
return false;
}
}
return true;
}
// Check the actual named arguments match the expected ones
private bool MatchesNamedConstructorArguments(Dictionary<string, CustomAttributeTypedArgument> actualNamedConstructorArguments)
{
if(_expectedNamedConstructorArguments.Count != actualNamedConstructorArguments.Count)
{
return false;
}
foreach (var namedConstructorArgumentKey in _expectedNamedConstructorArguments.Keys)
{
if (!actualNamedConstructorArguments.ContainsKey(namedConstructorArgumentKey))
{
return false;
}
var actualArgument = actualNamedConstructorArguments[namedConstructorArgumentKey];
var expectedArgumentValue = _expectedNamedConstructorArguments[namedConstructorArgumentKey];
if (!MatchArgument(actualArgument, expectedArgumentValue))
{
return false;
}
}
return true;
}
// Check the specified arguments match
private static bool MatchArgument(CustomAttributeTypedArgument actualArgument, object expectedArgumentValue)
{
if (actualArgument.ArgumentType != expectedArgumentValue.GetType())
{
return false;
}
object actualValue;
if (actualArgument.ArgumentType.IsEnum)
{
actualValue = Enum.ToObject(actualArgument.ArgumentType, actualArgument.Value);
}
else
{
actualValue = Convert.ChangeType(actualArgument.Value, actualArgument.ArgumentType);
}
if (!actualValue.Equals(expectedArgumentValue))
{
return false;
}
return true;
}
public override void WriteActualValueTo(MessageWriter writer)
{
var stringBuilder = new StringBuilder();
for (var i = 0; i < _actualAttributes.Count; i++)
{
var data = _actualAttributes[i];
if (i > 0)
{
stringBuilder.AppendLine();
}
stringBuilder.Append(data.Constructor.DeclaringType.Name);
var constructorArguments = data.ConstructorArguments.ToArray();
stringBuilder.Append("(");
for (var j = 0; j < constructorArguments.Length; j++)
{
if (j > 0)
{
stringBuilder.Append(", ");
}
var arg = constructorArguments[j];
stringBuilder.Append(arg.ArgumentType.Name);
stringBuilder.Append(" ");
stringBuilder.Append(arg.Value);
}
stringBuilder.Append(")");
var namedConstructorArguments = data.NamedArguments.ToDictionary(x => x.MemberInfo.Name, x => x.TypedValue.Value);
if (namedConstructorArguments.Count > 0)
{
stringBuilder.Append("{");
var keys = namedConstructorArguments.Keys.ToArray();
for (var j = 0; j < keys.Length; j++)
{
if (j > 0)
{
stringBuilder.Append(", ");
}
var key = keys[j];
var arg = namedConstructorArguments[key];
stringBuilder.Append(key);
stringBuilder.Append(" = ");
stringBuilder.Append(arg);
}
stringBuilder.Append("}");
}
}
writer.WriteActualValue(stringBuilder.ToString());
}
public override void WriteDescriptionTo(MessageWriter writer)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append(_expectedAttributeType.Name);
stringBuilder.Append("(");
for (var i = 0; i < _expectedConstructorArguments.Length; i++)
{
if (i > 0)
{
stringBuilder.Append(", ");
}
var arg = _expectedConstructorArguments[i];
stringBuilder.Append(arg.GetType().Name);
stringBuilder.Append(" ");
stringBuilder.Append(arg);
}
stringBuilder.Append(")");
if (_expectedNamedConstructorArguments != null)
{
var keys = _expectedNamedConstructorArguments.Keys.ToArray();
if (keys.Length > 0)
{
stringBuilder.Append("{");
for (var i = 0; i < keys.Length; i++)
{
if (i > 0)
{
stringBuilder.Append(", ");
}
var key = keys[i];
var arg = _expectedNamedConstructorArguments[key];
stringBuilder.Append(key);
stringBuilder.Append(" = ");
stringBuilder.Append(arg);
}
stringBuilder.Append("}");
}
}
writer.WriteExpectedValue(stringBuilder.ToString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment