ThrowsConstraint
ThrowsConstraint is used to test the exception thrown by
a delegate by applying a constraint to it.
using NUnit.Framework.Internal;
using System;
using System.Threading.Tasks;
namespace NUnit.Framework.Constraints
{
public class ThrowsConstraint : PrefixConstraint
{
private class ThrowsConstraintResult : ConstraintResult
{
private readonly ConstraintResult baseResult;
public ThrowsConstraintResult(ThrowsConstraint constraint, Exception caughtException, ConstraintResult baseResult)
: base(constraint, caughtException)
{
if (caughtException != null && baseResult.IsSuccess)
base.Status = ConstraintStatus.Success;
else
base.Status = ConstraintStatus.Failure;
this.baseResult = baseResult;
}
public override void WriteActualValueTo(MessageWriter writer)
{
if (base.ActualValue == null)
writer.Write("no exception thrown");
else
baseResult.WriteActualValueTo(writer);
}
}
internal class ExceptionInterceptor
{
private ExceptionInterceptor()
{
}
internal static Exception Intercept(object invocation)
{
IInvocationDescriptor invocationDescriptor = GetInvocationDescriptor(invocation);
if (AsyncInvocationRegion.IsAsyncOperation(invocationDescriptor.Delegate)) {
using (AsyncInvocationRegion asyncInvocationRegion = AsyncInvocationRegion.Create(invocationDescriptor.Delegate)) {
try {
object invocationResult = invocationDescriptor.Invoke();
asyncInvocationRegion.WaitForPendingOperationsToComplete(invocationResult);
return null;
} catch (Exception result) {
return result;
}
}
}
using (new TestExecutionContext.IsolatedContext())
try {
invocationDescriptor.Invoke();
return null;
} catch (Exception result2) {
return result2;
}
}
private static IInvocationDescriptor GetInvocationDescriptor(object actual)
{
IInvocationDescriptor invocationDescriptor = actual as IInvocationDescriptor;
if (invocationDescriptor == null) {
TestDelegate testDelegate = actual as TestDelegate;
if (testDelegate != null)
invocationDescriptor = new VoidInvocationDescriptor(testDelegate);
else {
AsyncTestDelegate asyncTestDelegate = actual as AsyncTestDelegate;
if (asyncTestDelegate != null)
invocationDescriptor = new GenericInvocationDescriptor<Task>(() => asyncTestDelegate());
}
}
if (invocationDescriptor == null)
throw new ArgumentException($"""{actual.GetType().Name}", "actual");
return invocationDescriptor;
}
}
internal class GenericInvocationDescriptor<T> : IInvocationDescriptor
{
private readonly ActualValueDelegate<T> _del;
public Delegate Delegate => _del;
public GenericInvocationDescriptor(ActualValueDelegate<T> del)
{
_del = del;
}
public object Invoke()
{
return _del();
}
}
private interface IInvocationDescriptor
{
Delegate Delegate { get; }
object Invoke();
}
private class VoidInvocationDescriptor : IInvocationDescriptor
{
private readonly TestDelegate _del;
public Delegate Delegate => _del;
public VoidInvocationDescriptor(TestDelegate del)
{
_del = del;
}
public object Invoke()
{
_del();
return null;
}
}
private Exception caughtException;
public Exception ActualException => caughtException;
public override string Description => base.BaseConstraint.Description;
public ThrowsConstraint(IConstraint baseConstraint)
: base(baseConstraint)
{
}
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
caughtException = ExceptionInterceptor.Intercept(actual);
return new ThrowsConstraintResult(this, caughtException, (caughtException != null) ? base.BaseConstraint.ApplyTo(caughtException) : null);
}
public override ConstraintResult ApplyTo<TActual>(ActualValueDelegate<TActual> del)
{
return ApplyTo(new GenericInvocationDescriptor<TActual>(del));
}
}
}