Created
November 4, 2014 19:13
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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