1 // ***********************************************************************
2 // Copyright (c) 2008 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using NUnit.Framework.Internal;
32 namespace NUnit.Framework.Constraints
35 /// ThrowsConstraint is used to test the exception thrown by
36 /// a delegate by applying a constraint to it.
38 public class ThrowsConstraint : PrefixConstraint
40 private Exception caughtException;
43 /// Initializes a new instance of the <see cref="ThrowsConstraint"/> class,
44 /// using a constraint to be applied to the exception.
46 /// <param name="baseConstraint">A constraint to apply to the caught exception.</param>
47 public ThrowsConstraint(IConstraint baseConstraint)
48 : base(baseConstraint) { }
51 /// Get the actual exception thrown - used by Assert.Throws.
53 public Exception ActualException
55 get { return caughtException; }
58 #region Constraint Overrides
61 /// Gets text describing a constraint
63 public override string Description
65 get { return BaseConstraint.Description; }
69 /// Executes the code of the delegate and captures any exception.
70 /// If a non-null base constraint was provided, it applies that
71 /// constraint to the exception.
73 /// <param name="actual">A delegate representing the code to be tested</param>
74 /// <returns>True if an exception is thrown and the constraint succeeds, otherwise false</returns>
75 public override ConstraintResult ApplyTo<TActual>(TActual actual)
77 //TestDelegate code = actual as TestDelegate;
79 // throw new ArgumentException(
80 // string.Format("The actual value must be a TestDelegate but was {0}", actual.GetType().Name), "actual");
82 //caughtException = null;
88 //catch (Exception ex)
90 // caughtException = ex;
93 caughtException = ExceptionInterceptor.Intercept(actual);
95 return new ThrowsConstraintResult(
98 caughtException != null
99 ? BaseConstraint.ApplyTo(caughtException)
104 /// Converts an ActualValueDelegate to a TestDelegate
105 /// before calling the primary overload.
107 /// <param name="del"></param>
108 /// <returns></returns>
109 public override ConstraintResult ApplyTo<TActual>(ActualValueDelegate<TActual> del)
111 //TestDelegate testDelegate = new TestDelegate(delegate { del(); });
112 //return ApplyTo((object)testDelegate);
113 return ApplyTo(new GenericInvocationDescriptor<TActual>(del));
118 #region Nested Result Class
120 private class ThrowsConstraintResult : ConstraintResult
122 private readonly ConstraintResult baseResult;
124 public ThrowsConstraintResult(ThrowsConstraint constraint,
125 Exception caughtException,
126 ConstraintResult baseResult)
127 : base(constraint, caughtException)
129 if (caughtException != null && baseResult.IsSuccess)
130 Status = ConstraintStatus.Success;
132 Status = ConstraintStatus.Failure;
134 this.baseResult = baseResult;
138 /// Write the actual value for a failing constraint test to a
139 /// MessageWriter. This override only handles the special message
140 /// used when an exception is expected but none is thrown.
142 /// <param name="writer">The writer on which the actual value is displayed</param>
143 public override void WriteActualValueTo(MessageWriter writer)
145 if (ActualValue == null)
146 writer.Write("no exception thrown");
148 baseResult.WriteActualValueTo(writer);
154 #region ExceptionInterceptor
156 internal class ExceptionInterceptor
158 private ExceptionInterceptor() { }
160 internal static Exception Intercept(object invocation)
162 var invocationDescriptor = GetInvocationDescriptor(invocation);
164 #if NET_4_0 || NET_4_5 || PORTABLE
165 if (AsyncInvocationRegion.IsAsyncOperation(invocationDescriptor.Delegate))
167 using (var region = AsyncInvocationRegion.Create(invocationDescriptor.Delegate))
171 object result = invocationDescriptor.Invoke();
172 region.WaitForPendingOperationsToComplete(result);
186 invocationDescriptor.Invoke();
196 private static IInvocationDescriptor GetInvocationDescriptor(object actual)
198 var invocationDescriptor = actual as IInvocationDescriptor;
200 if (invocationDescriptor == null)
202 var testDelegate = actual as TestDelegate;
204 if (testDelegate != null)
206 invocationDescriptor = new VoidInvocationDescriptor(testDelegate);
209 #if NET_4_0 || NET_4_5 || PORTABLE
212 var asyncTestDelegate = actual as AsyncTestDelegate;
213 if (asyncTestDelegate != null)
215 invocationDescriptor = new GenericInvocationDescriptor<System.Threading.Tasks.Task>(() => asyncTestDelegate());
220 if (invocationDescriptor == null)
221 throw new ArgumentException(
223 "The actual value must be a TestDelegate or AsyncTestDelegate but was {0}",
224 actual.GetType().Name),
227 return invocationDescriptor;
233 #region InvocationDescriptor
235 internal class GenericInvocationDescriptor<T> : IInvocationDescriptor
237 private readonly ActualValueDelegate<T> _del;
239 public GenericInvocationDescriptor(ActualValueDelegate<T> del)
244 public object Invoke()
249 public Delegate Delegate
255 private interface IInvocationDescriptor
257 Delegate Delegate { get; }
261 private class VoidInvocationDescriptor : IInvocationDescriptor
263 private readonly TestDelegate _del;
265 public VoidInvocationDescriptor(TestDelegate del)
270 public object Invoke()
276 public Delegate Delegate