// ----------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ----------------------------------------------------------------------- using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Runtime.Serialization; namespace System.UnitTesting { public static class ExceptionAssert { // NOTE: To catch state corrupting exceptions, it by design that // the ThrowsXXX methods retry by default. To prevent this in a // test, simply use one of the overloads that takes a RetryMode. /// /// Verifies that the exception has the default message generated by the base Exception class. /// public static void HasDefaultMessage(Exception exception) { Assert.IsNotNull(exception); // Exception of type '[typename]' was thrown StringAssert.Contains(exception.Message, exception.GetType().FullName); } /// /// Verifies that the specified action throws a SerializationException. /// public static SerializationException ThrowsSerialization(string memberName, Action action) { var exception = Throws(RetryMode.Retry, action, (actual, retryCount) => { AssertSerializationMemberName(memberName, actual, retryCount); }); return exception; } /// /// Verifies that the specified action throws an ObjectDisposedException. /// public static ObjectDisposedException ThrowsDisposed(object instance, Action action) { var exception = Throws(RetryMode.Retry, action, (actual, retryCount) => { AssertObjectDisposed(instance, actual, retryCount); }); return exception; } /// /// Verifies that the specified action throws an ArgumentNullException. /// public static ArgumentNullException ThrowsArgumentNull(string parameterName, Action action) { return ThrowsArgument(parameterName, action); } /// /// Verifies that the specified action throws an ArgumentException. /// public static ArgumentException ThrowsArgument(string parameterName, Action action) { return ThrowsArgument(parameterName, action); } /// /// Verifies that the specified action throws an ArgumentException of type . /// public static T ThrowsArgument(string parameterName, Action action) where T : ArgumentException { var exception = Throws(RetryMode.Retry, action, (actual, retryCount) => { AssertSameParameterName(parameterName, actual, retryCount); }); return exception; } /// /// Verifies that the specified action throws an exception of type , /// with the specified inner exception of type . /// public static T Throws(Action action) where T : Exception where TInner : Exception { return Throws(RetryMode.Retry, action); } /// /// Verifies that the specified action throws an exception of type , /// with the specified inner exception of type , and indicating /// whether to retry. /// public static T Throws(RetryMode retry, Action action) where T : Exception where TInner : Exception { return Throws(retry, action, (Action)null); } /// /// Verifies that the specified action throws an exception of type , /// with the specified inner exception of type , indicating /// whether to retry and running the specified validator. /// public static T Throws(RetryMode retry, Action action, Action validator) where T : Exception where TInner : Exception { var exception = Throws(retry, action, (actual, retryCount) => { AssertIsExactInstanceOfInner(typeof(TInner), actual, retryCount); if (validator != null) { validator(actual, retryCount); } }); return exception; } /// /// Verifies that the specified action throws an exception of type , /// with the specified inner exception. /// public static T Throws(Exception innerException, Action action) where T : Exception { return Throws(innerException, RetryMode.Retry, action, (Action)null); } /// /// Verifies that the specified action throws an exception of type , /// with the specified inner exception, and indicating whether to retry. /// public static T Throws(Exception innerException, RetryMode retry, Action action) where T : Exception { return Throws(innerException, RetryMode.Retry, action, (Action)null); } /// /// Verifies that the specified action throws an exception of type , /// with the specified inner exception, indicating whether to retry and running the /// specified validator. /// public static T Throws(Exception innerException, RetryMode retry, Action action, Action validator) where T : Exception { T exception = Throws(retry, action, (actual, retryCount) => { AssertSameInner(innerException, actual, retryCount); if (validator != null) { validator(actual, retryCount); } }); return exception; } /// /// Verifies that the specified action throws an exception of type . /// public static T Throws(Action action) where T : Exception { return Throws(RetryMode.Retry, action, (Action)null); } /// /// Verifies that the specified action throws an exception of type , /// indicating whether to retry. /// public static T Throws(RetryMode retry, Action action) where T : Exception { return Throws(retry, action, (Action)null); } /// /// Verifies that the specified action throws an exception of type , /// indicating whether to retry and running the specified validator. /// public static T Throws(RetryMode retry, Action action, Action validator) where T : Exception { var exception = (T)Run(retry, action, (actual, retryCount) => { AssertIsExactInstanceOf(typeof(T), actual, retryCount); if (validator != null) { validator((T)actual, retryCount); } }); return exception; } /// /// Verifies that the specified action throws the specified exception. /// public static void Throws(Exception expected, Action action) { Throws(expected, RetryMode.Retry, action); } /// /// Verifies that the specified action throws the specified exception, /// indicating whether to retry. /// public static void Throws(Exception expected, RetryMode retry, Action action) { Throws(expected, retry, action, (Action)null); } /// /// Verifies that the specified action throws the specified exception, /// indicating whether to retry and running the specified validator. /// public static void Throws(Exception expected, RetryMode retry, Action action, Action validator) { Run(retry, action, (actual, retryCount) => { AssertSame(expected, actual, retryCount); if (validator != null) { validator(actual, retryCount); } }); } private static Exception Run(RetryMode retry, Action action, Action validator) { Exception exception = null; for (int i = -1; i < (int)retry; i++) { exception = Run(action); validator(exception, i + 2); } return exception; } private static Exception Run(Action action) { try { action(); return null; } catch (Exception ex) { return ex; } } private static void AssertSerializationMemberName(string memberName, SerializationException actual, int retryCount) { // Unfortunately, SerializationException does not provide a way to get our hands on the // the actual member that was missing from the SerializationInfo, so we need to grok the // message string. // Member '[memberName]' was not found. StringAssert.Contains(actual.Message, "'" + memberName + "'", "Retry Count {0}: Expected SerializationException MemberName to be '{1}'", retryCount, memberName); } private static void AssertObjectDisposed(object instance, ObjectDisposedException actual, int retryCount) { string objectName = instance.GetType().FullName; Assert.AreEqual(objectName, actual.ObjectName, "Retry Count {0}: Expected {1}.ObjectName to be '{2}', however, '{3}' is.", retryCount, actual.GetType().Name, objectName, actual.ObjectName); } private static void AssertSameParameterName(string parameterName, ArgumentException actual, int retryCount) { #if !SILVERLIGHT Assert.AreEqual(parameterName, actual.ParamName, "Retry Count {0}: Expected {1}.ParamName to be '{2}', however, '{3}' is.", retryCount, actual.GetType().Name, parameterName, actual.ParamName); #else // Silverlight doesn't have ArgumentException.ParamName StringAssert.Contains(actual.Message, parameterName, "Retry Count {0}: Expected {1}.ParamName to be '{2}'", retryCount, actual.GetType().Name, parameterName); #endif } private static void AssertSame(Exception expected, Exception actual, int retryCount) { Assert.AreSame(expected, actual, "Retry Count {0}: Expected '{1}' to be thrown, however, '{2}' was thrown.", retryCount, expected, actual); } private static void AssertSameInner(Exception innerException, Exception actual, int retryCount) { Assert.AreSame(innerException, actual.InnerException, "Retry Count {0}: Expected '{1}' to be the inner exception, however, '{2}' is.", retryCount, innerException, actual.InnerException); } private static void AssertIsExactInstanceOf(Type expectedType, Exception actual, int retryCount) { if (actual == null) Assert.Fail("Retry Count {0}: Expected '{1}' to be thrown", retryCount, expectedType); Type actualType = actual.GetType(); Assert.AreSame(expectedType, actualType, "Retry Count {0}: Expected '{1}' to be thrown, however, '{2}' was thrown.", retryCount, expectedType, actualType); } private static void AssertIsExactInstanceOfInner(Type expectedType, Exception actual, int retryCount) { if (actual.InnerException == null) Assert.Fail("Retry Count {0}: Expected '{1}' be the inner exception, however, it is null.", retryCount, expectedType); Type actualType = actual.InnerException.GetType(); Assert.AreSame(expectedType, actualType, "Retry Count {0}: Expected '{1}' to be the inner exception, however, '{2}' is.", retryCount, expectedType, actualType); } } }