public void ValidateNoDanglingAttributes()
{
// This list represents attributes that have been cleared to ship without deriving
// from ArgMetadata. This test is here to make sure that, by default, the attributes
// in this project behave like most of the others. That is, most attributes should
// derive from ArgMetadata.
var whitelist = new Type[]
{
typeof(ArgReviverAttribute),
typeof(DynamicExpressionProviderAttribute),
typeof(ArgActions),
typeof(FilterableAttribute),
typeof(KeyAttribute),
typeof(MarkupPropertyAttribute),
typeof(MarkupExtensionAttribute),
typeof(MarkupIgnoreAttribute),
};
var iArgMetadataSubInterfaces = typeof(Args).Assembly.GetTypes().Where(t =>
t.IsInterface &&
t.GetInterfaces().Contains(typeof(IArgMetadata))).ToList();
// In general, attributes in this project should derive from ArgMetadata
var danglingAttrs = typeof(Args).Assembly.GetTypes().Where(t =>
t.IsSubclassOf(typeof(Attribute)) == true &&
t.GetInterfaces().Contains(typeof(IArgMetadata)) == false &&
whitelist.Contains(t) == false
).ToList();
// In general, attibutes should use more specific IArgMetadata interfaces
var danglingAttrs2 = typeof(Args).Assembly.GetTypes().Where(t =>
t.GetInterfaces().Contains(typeof(Attribute)) == true &&
(from i in t.GetInterfaces() where iArgMetadataSubInterfaces.Contains(i) select i).Count() == 0 &&
whitelist.Contains(t) == false
).ToList();
Assert.AreEqual(0, danglingAttrs.Count, string.Join(", ", danglingAttrs.Select(a => a.FullName)));
Assert.AreEqual(0, danglingAttrs2.Count, string.Join(", ", danglingAttrs2.Select(a => a.FullName)));
}