// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Web;
namespace Microsoft.TestCommon
{
public partial class AssertEx
{
///
/// Determines if your thread's current culture and current UI culture is English.
///
public static bool CurrentCultureIsEnglish
{
get
{
return String.Equals(CultureInfo.CurrentCulture.TwoLetterISOLanguageName, "en", StringComparison.OrdinalIgnoreCase)
&& String.Equals(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, "en", StringComparison.OrdinalIgnoreCase);
}
}
///
/// Determines whether the specified exception is of the given type (or optionally of a derived type).
/// The exception is not allowed to be null;
///
/// The type of the exception to test for.
/// The exception to be tested.
/// The expected exception message (only verified on US English OSes).
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
public static void IsException(Type exceptionType, Exception exception, string expectedMessage = null, bool allowDerivedExceptions = false)
{
exception = UnwrapException(exception);
NotNull(exception);
if (allowDerivedExceptions)
IsAssignableFrom(exceptionType, exception);
else
IsType(exceptionType, exception);
VerifyExceptionMessage(exception, expectedMessage, partialMatch: false);
}
///
/// Determines whether the specified exception is of the given type (or optionally of a derived type).
/// The exception is not allowed to be null;
///
/// The type of the exception to test for.
/// The exception to be tested.
/// The expected exception message (only verified on US English OSes).
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception cast to TException.
public static TException IsException(Exception exception, string expectedMessage = null, bool allowDerivedExceptions = false)
where TException : Exception
{
TException result;
exception = UnwrapException(exception);
NotNull(exception);
if (allowDerivedExceptions)
result = IsAssignableFrom(exception);
else
result = IsType(exception);
VerifyExceptionMessage(exception, expectedMessage, partialMatch: false);
return result;
}
// We've re-implemented all the xUnit.net Throws code so that we can get this
// updated implementation of RecordException which silently unwraps any instances
// of AggregateException. This lets our tests better simulate what "await" would do
// and thus makes them easier to port to .NET 4.5.
private static Exception RecordException(Action testCode)
{
try
{
testCode();
return null;
}
catch (Exception exception)
{
return UnwrapException(exception);
}
}
///
/// Verifies that the exact exception is thrown (and not a derived exception type).
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static T Throws(Action testCode)
where T : Exception
{
return (T)Throws(typeof(T), testCode);
}
///
/// Verifies that the exact exception is thrown (and not a derived exception type).
/// Generally used to test property accessors.
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static T Throws(Func testCode)
where T : Exception
{
return (T)Throws(typeof(T), testCode);
}
///
/// Verifies that the exact exception is thrown (and not a derived exception type).
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static Exception Throws(Type exceptionType, Action testCode)
{
Exception exception = RecordException(testCode);
if (exception == null)
throw new ThrowsException(exceptionType);
if (!exceptionType.Equals(exception.GetType()))
throw new ThrowsException(exceptionType, exception);
return exception;
}
///
/// Verifies that the exact exception is thrown (and not a derived exception type).
/// Generally used to test property accessors.
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static Exception Throws(Type exceptionType, Func testCode)
{
return Throws(exceptionType, () => { object unused = testCode(); });
}
///
/// Verifies that an exception of the given type (or optionally a derived type) is thrown.
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static TException Throws(Action testCode, bool allowDerivedExceptions)
where TException : Exception
{
Type exceptionType = typeof(TException);
Exception exception = RecordException(testCode);
TargetInvocationException tie = exception as TargetInvocationException;
if (tie != null)
{
exception = tie.InnerException;
}
if (exception == null)
{
throw new ThrowsException(exceptionType);
}
var typedException = exception as TException;
if (typedException == null || (!allowDerivedExceptions && typedException.GetType() != typeof(TException)))
{
throw new ThrowsException(exceptionType, exception);
}
return typedException;
}
///
/// Verifies that an exception of the given type (or optionally a derived type) is thrown.
/// Generally used to test property accessors.
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static TException Throws(Func testCode, bool allowDerivedExceptions)
where TException : Exception
{
return Throws(() => { testCode(); }, allowDerivedExceptions);
}
///
/// Verifies that an exception of the given type (or optionally a derived type) is thrown.
/// Also verified that the exception message matches if the current thread locale is English.
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// The exception message to verify
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static TException Throws(Action testCode, string exceptionMessage, bool allowDerivedExceptions = false)
where TException : Exception
{
var ex = Throws(testCode, allowDerivedExceptions);
VerifyExceptionMessage(ex, exceptionMessage);
return ex;
}
///
/// Verifies that an exception of the given type (or optionally a derived type) is thrown.
/// Also verified that the exception message matches if the current thread locale is English.
///
/// The type of the exception expected to be thrown
/// A delegate to the code to be tested
/// The exception message to verify
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static TException Throws(Func testCode, string exceptionMessage, bool allowDerivedExceptions = false)
where TException : Exception
{
return Throws(() => { testCode(); }, exceptionMessage, allowDerivedExceptions);
}
///
/// Verifies that the code throws an (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentException ThrowsArgument(Action testCode, string paramName, bool allowDerivedExceptions = false)
{
var ex = Throws(testCode, allowDerivedExceptions);
if (paramName != null)
{
Equal(paramName, ex.ParamName);
}
return ex;
}
///
/// Verifies that the code throws an (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The exception message to verify
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentException ThrowsArgument(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false)
{
var ex = Throws(testCode, allowDerivedExceptions);
if (paramName != null)
{
Equal(paramName, ex.ParamName);
}
VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true);
return ex;
}
///
/// Verifies that the code throws an ArgumentException (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentException ThrowsArgument(Func testCode, string paramName, bool allowDerivedExceptions = false)
{
var ex = Throws(testCode, allowDerivedExceptions);
if (paramName != null)
{
Equal(paramName, ex.ParamName);
}
return ex;
}
///
/// Verifies that the code throws an ArgumentNullException (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentNullException ThrowsArgumentNull(Action testCode, string paramName)
{
var ex = Throws(testCode, allowDerivedExceptions: false);
if (paramName != null)
{
Equal(paramName, ex.ParamName);
}
return ex;
}
///
/// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot
/// be null or empty.
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentException ThrowsArgumentNullOrEmpty(Action testCode, string paramName)
{
return Throws(testCode, "Value cannot be null or empty.\r\nParameter name: " + paramName, allowDerivedExceptions: false);
}
///
/// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot
/// be null or empty string.
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentException ThrowsArgumentNullOrEmptyString(Action testCode, string paramName)
{
return ThrowsArgument(testCode, paramName, "Value cannot be null or an empty string.", allowDerivedExceptions: true);
}
///
/// Verifies that the code throws an ArgumentOutOfRangeException (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The exception message to verify
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The actual value provided
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentOutOfRangeException ThrowsArgumentOutOfRange(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false, object actualValue = null)
{
exceptionMessage = exceptionMessage != null
? exceptionMessage + "\r\nParameter name: " + paramName +
(actualValue != null ? "\r\nActual value was " + actualValue.ToString() + "." : "")
: exceptionMessage;
var ex = Throws(testCode, exceptionMessage, allowDerivedExceptions);
if (paramName != null)
{
Equal(paramName, ex.ParamName);
}
return ex;
}
///
/// Verifies that the code throws an with the expected message that indicates that
/// the value must be greater than the given .
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The actual value provided.
/// The expected limit value.
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentOutOfRangeException ThrowsArgumentGreaterThan(Action testCode, string paramName, string value, object actualValue = null)
{
return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be greater than {0}.", value), false, actualValue);
}
///
/// Verifies that the code throws an with the expected message that indicates that
/// the value must be greater than or equal to the given value.
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The expected limit value.
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentOutOfRangeException ThrowsArgumentGreaterThanOrEqualTo(Action testCode, string paramName, string value, object actualValue = null)
{
return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be greater than or equal to {0}.", value), false, actualValue);
}
///
/// Verifies that the code throws an with the expected message that indicates that
/// the value must be less than the given .
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The actual value provided.
/// The expected limit value.
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentOutOfRangeException ThrowsArgumentLessThan(Action testCode, string paramName, string maxValue, object actualValue = null)
{
return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be less than {0}.", maxValue), false, actualValue);
}
///
/// Verifies that the code throws an with the expected message that indicates that
/// the value must be less than or equal to the given .
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The actual value provided.
/// The expected limit value.
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ArgumentOutOfRangeException ThrowsArgumentLessThanOrEqualTo(Action testCode, string paramName, string maxValue, object actualValue = null)
{
return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be less than or equal to {0}.", maxValue), false, actualValue);
}
///
/// Verifies that the code throws an HttpException (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The exception message to verify
/// The expected HTTP status code of the exception
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static HttpException ThrowsHttpException(Action testCode, string exceptionMessage, int httpCode, bool allowDerivedExceptions = false)
{
var ex = Throws(testCode, exceptionMessage, allowDerivedExceptions);
Equal(httpCode, ex.GetHttpCode());
return ex;
}
///
/// Verifies that the code throws an InvalidEnumArgumentException (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the parameter that should throw the exception
/// The expected invalid value that should appear in the message
/// The type of the enumeration
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static InvalidEnumArgumentException ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false)
{
return Throws(
testCode,
String.Format("The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.{3}Parameter name: {0}", paramName, invalidValue, enumType.Name, Environment.NewLine),
allowDerivedExceptions
);
}
///
/// Verifies that the code throws an HttpException (or optionally any exception which derives from it).
///
/// A delegate to the code to be tested
/// The name of the object that was dispose
/// Pass true to allow exceptions which derive from TException; pass false, otherwise
/// The exception that was thrown, when successful
/// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown
public static ObjectDisposedException ThrowsObjectDisposed(Action testCode, string objectName, bool allowDerivedExceptions = false)
{
var ex = Throws(testCode, allowDerivedExceptions);
if (objectName != null)
{
Equal(objectName, ex.ObjectName);
}
return ex;
}
private static Exception UnwrapException(Exception exception)
{
AggregateException aggEx;
while ((aggEx = exception as AggregateException) != null)
exception = aggEx.GetBaseException();
return exception;
}
private static void VerifyExceptionMessage(Exception exception, string expectedMessage, bool partialMatch = false)
{
if (expectedMessage != null && CurrentCultureIsEnglish)
{
if (!partialMatch)
{
Equal(expectedMessage, exception.Message);
}
else
{
Contains(expectedMessage, exception.Message);
}
}
}
// Custom ThrowsException so we can filter the stack trace.
private class ThrowsException : Xunit.Sdk.ThrowsException
{
public ThrowsException(Type type) : base(type) { }
public ThrowsException(Type type, Exception ex) : base(type, ex) { }
protected override bool ExcludeStackFrame(string stackFrame)
{
if (stackFrame.StartsWith("at Microsoft.TestCommon.AssertEx.", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return base.ExcludeStackFrame(stackFrame);
}
}
}
}