using System; using System.IO; using System.Linq.Expressions; using System.Reflection; using NUnit.Framework; namespace Invariant { [TestFixture] public class Tests { [Test] [ExpectedException(typeof(ArgumentNullException), "Value cannot be null.\r\nParameter name: arg")] public void ArgumentNotNull_Literal_IsNull() { ArgumentNotNull(null); } [Test] public void ArgumentNotNull_Literal_IsNotNull() { ArgumentNotNull("Foo"); } [Test] [ExpectedException(typeof(ArgumentNullException), "Value cannot be null.\r\nParameter name: arg")] public void ArgumentNotNull_Variable_IsNull() { const string s = null; ArgumentNotNull(s); } [Test] public void ArgumentNotNull_Variable_IsNotNull() { var s = "Foo"; ArgumentNotNull(s); } [Test] [ExpectedException(typeof(ArgumentOutOfRangeException), "Specified argument was out of the range of valid values.\r\nParameter name: arg")] public void ArgumentNotEmpty_Literal_IsNull() { ArgumentNotEmpty(null); } [Test] [ExpectedException(typeof(ArgumentOutOfRangeException), "Specified argument was out of the range of valid values.\r\nParameter name: arg")] public void ArgumentNotEmpty_Literal_IsEmpty() { ArgumentNotEmpty(string.Empty); } [Test] public void ArgumentNotEmpty_Literal_IsNotEmpty() { ArgumentNotEmpty("Foo"); } [Test] [ExpectedException(typeof(ArgumentException), "Specified argument was invalid.\r\nParameter name: arg")] public void ArgumentNull_Literal_IsNotNull() { ArgumentNull(string.Empty); } [Test] public void ArgumentNull_Literal_IsNull() { ArgumentNull(null); } [Test] [ExpectedException(typeof(ArgumentException), "Specified argument was invalid.\r\nParameter name: arg")] public void ArgumentAdHoc_Literal_Fails() { ArgumentAdHoc(0); } [Test] public void ArgumentAdHoc_Literal_Passes() { ArgumentAdHoc(1); } private static void ArgumentNotNull(object arg) { Check.Argument(() => arg.IsNotNull); } private static void ArgumentNull(object arg) { Check.Argument(() => arg.IsNull); } private static void ArgumentNotEmpty(string arg) { Check.Argument(() => arg.IsNotEmpty); } private static void ArgumentAdHoc(int arg) { Check.Argument(() => arg > 0); } [Test] [ExpectedException(typeof(NullReferenceException), "arg cannot be null")] public void NotNull_Literal_IsNull() { NotNull(null); } [Test] public void NotNull_Literal_IsNotNull() { NotNull("Foo"); } [Test] [ExpectedException(typeof(NullReferenceException), "arg cannot be null")] public void NotNull_Variable_IsNull() { const string s = null; NotNull(s); } [Test] public void NotNull_Variable_IsNotNull() { var s = "Foo"; NotNull(s); } [Test] [ExpectedException(typeof(InvalidOperationException), "Program invariant failure")] public void NotEmpty_Literal_IsEmpty() { NotEmpty(null); } [Test] [ExpectedException(typeof(InvalidOperationException), "Program invariant failure")] public void AdHoc_Literal_Fails() { AdHoc(0); } private static void NotNull(object arg) { Check.Invariant(() => arg.IsNotNull); } private static void NotEmpty(string arg) { Check.Invariant(() => arg.IsNotEmpty); } private static void AdHoc(int arg) { Check.Invariant(() => arg > 0); } } internal static class Check { public static bool IsNotNull(this object o) { return o != null; } public static bool IsNull(this object o) { return o == null; } public static bool IsNotEmpty(this string s) { return !string.IsNullOrEmpty(s); } public static bool FileExists(this string path) { return File.Exists(path); } private class ExceptionBuilder { protected const string IsNotNull = "IsNotNull"; protected const string IsNotEmpty = "IsNotEmpty"; protected const string FileExists = "FileExists"; public virtual void Build(string methodName, string argumentName, object value) { if (Equals(methodName, IsNotNull)) { if (!string.IsNullOrEmpty(argumentName)) { throw new NullReferenceException(string.Format("{0} cannot be null", argumentName)); } throw new NullReferenceException(); } if (Equals(methodName, FileExists)) { if (value != null) { throw new FileNotFoundException(value.ToString()); } throw new FileNotFoundException(); } throw new InvalidOperationException("Program invariant failure"); } } private sealed class ArgumentExceptionBuilder : ExceptionBuilder { public override void Build(string methodName, string argumentName, object value) { switch (methodName) { case IsNotNull: throw new ArgumentNullException(argumentName); case IsNotEmpty: throw new ArgumentOutOfRangeException(argumentName); } throw new ArgumentException("Specified argument was invalid.", argumentName); } } private static readonly ExceptionBuilder _exceptionBuilder = new ExceptionBuilder(); private static readonly ExceptionBuilder _argumentExceptionBuilder = new ArgumentExceptionBuilder(); public static void Argument(Expression>> expression) { Verify(expression, _argumentExceptionBuilder); } public static void Argument(Expression> expression) { Verify(expression, _argumentExceptionBuilder); } public static void Invariant(Expression>> expression) { Verify(expression, _exceptionBuilder); } public static void Invariant(Expression> expression) { Verify(expression, _exceptionBuilder); } private static void Verify(Expression> expression, ExceptionBuilder exceptionBuilder) { if (expression == null) { return; } var binaryExpression = expression.Body as BinaryExpression; if (binaryExpression == null) { return; } var memberExpression = binaryExpression.Left as MemberExpression; if (memberExpression == null) { return; } var argumentName = memberExpression.Member.Name; var result = expression.Compile()(); if (!result) { exceptionBuilder.Build(null, argumentName, null); } } private static void Verify(Expression>> expression, ExceptionBuilder exceptionBuilder) { if (expression == null) { return; } var unaryExpression = expression.Body as UnaryExpression; if (unaryExpression == null) { return; } var methodCallExpression = unaryExpression.Operand as MethodCallExpression; if ((methodCallExpression == null) || (methodCallExpression.Arguments.Count != 3)) { return; } var memberExpression = methodCallExpression.Arguments[1] as MemberExpression; var constantExpression = methodCallExpression.Arguments[2] as ConstantExpression; if ((memberExpression == null) || (constantExpression == null) || (constantExpression.Value == null)) { return; } var methodInfo = constantExpression.Value as MethodInfo; if (methodInfo == null) { return; } constantExpression = memberExpression.Expression as ConstantExpression; if ((constantExpression == null) || (constantExpression.Value == null)) { return; } var argumentName = memberExpression.Member.Name; var fieldInfo = constantExpression.Value.GetType().GetField(argumentName); if (fieldInfo == null) { return; } var value = fieldInfo.GetValue(constantExpression.Value); var result = (bool)methodInfo.Invoke(null, new[] {value}); if (!result) { exceptionBuilder.Build(methodInfo.Name, argumentName, value); } } } }