1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 
3 using System;
4 using System.ComponentModel;
5 using System.Globalization;
6 using System.Reflection;
7 using System.Web;
8 
9 namespace Microsoft.TestCommon
10 {
11     public partial class AssertEx
12     {
13         /// <summary>
14         /// Determines if your thread's current culture and current UI culture is English.
15         /// </summary>
16         public static bool CurrentCultureIsEnglish
17         {
18             get
19             {
20                 return String.Equals(CultureInfo.CurrentCulture.TwoLetterISOLanguageName, "en", StringComparison.OrdinalIgnoreCase)
21                     && String.Equals(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, "en", StringComparison.OrdinalIgnoreCase);
22             }
23         }
24 
25         /// <summary>
26         /// Determines whether the specified exception is of the given type (or optionally of a derived type).
27         /// The exception is not allowed to be null;
28         /// </summary>
29         /// <param name="exceptionType">The type of the exception to test for.</param>
30         /// <param name="exception">The exception to be tested.</param>
31         /// <param name="expectedMessage">The expected exception message (only verified on US English OSes).</param>
32         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
IsException(Type exceptionType, Exception exception, string expectedMessage = null, bool allowDerivedExceptions = false)33         public static void IsException(Type exceptionType, Exception exception, string expectedMessage = null, bool allowDerivedExceptions = false)
34         {
35             exception = UnwrapException(exception);
36             NotNull(exception);
37 
38             if (allowDerivedExceptions)
39                 IsAssignableFrom(exceptionType, exception);
40             else
41                 IsType(exceptionType, exception);
42 
43             VerifyExceptionMessage(exception, expectedMessage, partialMatch: false);
44         }
45 
46         /// <summary>
47         /// Determines whether the specified exception is of the given type (or optionally of a derived type).
48         /// The exception is not allowed to be null;
49         /// </summary>
50         /// <typeparam name="TException">The type of the exception to test for.</typeparam>
51         /// <param name="exception">The exception to be tested.</param>
52         /// <param name="expectedMessage">The expected exception message (only verified on US English OSes).</param>
53         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
54         /// <returns>The exception cast to TException.</returns>
55         public static TException IsException<TException>(Exception exception, string expectedMessage = null, bool allowDerivedExceptions = false)
56             where TException : Exception
57         {
58             TException result;
59 
60             exception = UnwrapException(exception);
61             NotNull(exception);
62 
63             if (allowDerivedExceptions)
64                 result = IsAssignableFrom<TException>(exception);
65             else
66                 result = IsType<TException>(exception);
67 
68             VerifyExceptionMessage(exception, expectedMessage, partialMatch: false);
69             return result;
70         }
71 
72         // We've re-implemented all the xUnit.net Throws code so that we can get this
73         // updated implementation of RecordException which silently unwraps any instances
74         // of AggregateException. This lets our tests better simulate what "await" would do
75         // and thus makes them easier to port to .NET 4.5.
RecordException(Action testCode)76         private static Exception RecordException(Action testCode)
77         {
78             try
79             {
80                 testCode();
81                 return null;
82             }
83             catch (Exception exception)
84             {
85                 return UnwrapException(exception);
86             }
87         }
88 
89         /// <summary>
90         /// Verifies that the exact exception is thrown (and not a derived exception type).
91         /// </summary>
92         /// <typeparam name="T">The type of the exception expected to be thrown</typeparam>
93         /// <param name="testCode">A delegate to the code to be tested</param>
94         /// <returns>The exception that was thrown, when successful</returns>
95         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
96         public static T Throws<T>(Action testCode)
97             where T : Exception
98         {
99             return (T)Throws(typeof(T), testCode);
100         }
101 
102         /// <summary>
103         /// Verifies that the exact exception is thrown (and not a derived exception type).
104         /// Generally used to test property accessors.
105         /// </summary>
106         /// <typeparam name="T">The type of the exception expected to be thrown</typeparam>
107         /// <param name="testCode">A delegate to the code to be tested</param>
108         /// <returns>The exception that was thrown, when successful</returns>
109         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
110         public static T Throws<T>(Func<object> testCode)
111             where T : Exception
112         {
113             return (T)Throws(typeof(T), testCode);
114         }
115 
116         /// <summary>
117         /// Verifies that the exact exception is thrown (and not a derived exception type).
118         /// </summary>
119         /// <param name="exceptionType">The type of the exception expected to be thrown</param>
120         /// <param name="testCode">A delegate to the code to be tested</param>
121         /// <returns>The exception that was thrown, when successful</returns>
122         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
Throws(Type exceptionType, Action testCode)123         public static Exception Throws(Type exceptionType, Action testCode)
124         {
125             Exception exception = RecordException(testCode);
126 
127             if (exception == null)
128                 throw new ThrowsException(exceptionType);
129 
130             if (!exceptionType.Equals(exception.GetType()))
131                 throw new ThrowsException(exceptionType, exception);
132 
133             return exception;
134         }
135 
136         /// <summary>
137         /// Verifies that the exact exception is thrown (and not a derived exception type).
138         /// Generally used to test property accessors.
139         /// </summary>
140         /// <param name="exceptionType">The type of the exception expected to be thrown</param>
141         /// <param name="testCode">A delegate to the code to be tested</param>
142         /// <returns>The exception that was thrown, when successful</returns>
143         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
Throws(Type exceptionType, Func<object> testCode)144         public static Exception Throws(Type exceptionType, Func<object> testCode)
145         {
146             return Throws(exceptionType, () => { object unused = testCode(); });
147         }
148 
149         /// <summary>
150         /// Verifies that an exception of the given type (or optionally a derived type) is thrown.
151         /// </summary>
152         /// <typeparam name="TException">The type of the exception expected to be thrown</typeparam>
153         /// <param name="testCode">A delegate to the code to be tested</param>
154         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
155         /// <returns>The exception that was thrown, when successful</returns>
156         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
157         public static TException Throws<TException>(Action testCode, bool allowDerivedExceptions)
158             where TException : Exception
159         {
160             Type exceptionType = typeof(TException);
161             Exception exception = RecordException(testCode);
162 
163             TargetInvocationException tie = exception as TargetInvocationException;
164             if (tie != null)
165             {
166                 exception = tie.InnerException;
167             }
168 
169             if (exception == null)
170             {
171                 throw new ThrowsException(exceptionType);
172             }
173 
174             var typedException = exception as TException;
175             if (typedException == null || (!allowDerivedExceptions && typedException.GetType() != typeof(TException)))
176             {
177                 throw new ThrowsException(exceptionType, exception);
178             }
179 
180             return typedException;
181         }
182 
183         /// <summary>
184         /// Verifies that an exception of the given type (or optionally a derived type) is thrown.
185         /// Generally used to test property accessors.
186         /// </summary>
187         /// <typeparam name="TException">The type of the exception expected to be thrown</typeparam>
188         /// <param name="testCode">A delegate to the code to be tested</param>
189         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
190         /// <returns>The exception that was thrown, when successful</returns>
191         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
192         public static TException Throws<TException>(Func<object> testCode, bool allowDerivedExceptions)
193             where TException : Exception
194         {
195             return Throws<TException>(() => { testCode(); }, allowDerivedExceptions);
196         }
197 
198         /// <summary>
199         /// Verifies that an exception of the given type (or optionally a derived type) is thrown.
200         /// Also verified that the exception message matches if the current thread locale is English.
201         /// </summary>
202         /// <typeparam name="TException">The type of the exception expected to be thrown</typeparam>
203         /// <param name="testCode">A delegate to the code to be tested</param>
204         /// <param name="exceptionMessage">The exception message to verify</param>
205         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
206         /// <returns>The exception that was thrown, when successful</returns>
207         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
208         public static TException Throws<TException>(Action testCode, string exceptionMessage, bool allowDerivedExceptions = false)
209             where TException : Exception
210         {
211             var ex = Throws<TException>(testCode, allowDerivedExceptions);
212             VerifyExceptionMessage(ex, exceptionMessage);
213             return ex;
214         }
215 
216         /// <summary>
217         /// Verifies that an exception of the given type (or optionally a derived type) is thrown.
218         /// Also verified that the exception message matches if the current thread locale is English.
219         /// </summary>
220         /// <typeparam name="TException">The type of the exception expected to be thrown</typeparam>
221         /// <param name="testCode">A delegate to the code to be tested</param>
222         /// <param name="exceptionMessage">The exception message to verify</param>
223         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
224         /// <returns>The exception that was thrown, when successful</returns>
225         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
226         public static TException Throws<TException>(Func<object> testCode, string exceptionMessage, bool allowDerivedExceptions = false)
227             where TException : Exception
228         {
229             return Throws<TException>(() => { testCode(); }, exceptionMessage, allowDerivedExceptions);
230         }
231 
232         /// <summary>
233         /// Verifies that the code throws an <see cref="ArgumentException"/> (or optionally any exception which derives from it).
234         /// </summary>
235         /// <param name="testCode">A delegate to the code to be tested</param>
236         /// <param name="paramName">The name of the parameter that should throw the exception</param>
237         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
238         /// <returns>The exception that was thrown, when successful</returns>
239         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgument(Action testCode, string paramName, bool allowDerivedExceptions = false)240         public static ArgumentException ThrowsArgument(Action testCode, string paramName, bool allowDerivedExceptions = false)
241         {
242             var ex = Throws<ArgumentException>(testCode, allowDerivedExceptions);
243 
244             if (paramName != null)
245             {
246                 Equal(paramName, ex.ParamName);
247             }
248 
249             return ex;
250         }
251 
252         /// <summary>
253         /// Verifies that the code throws an <see cref="ArgumentException"/> (or optionally any exception which derives from it).
254         /// </summary>
255         /// <param name="testCode">A delegate to the code to be tested</param>
256         /// <param name="paramName">The name of the parameter that should throw the exception</param>
257         /// <param name="exceptionMessage">The exception message to verify</param>
258         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
259         /// <returns>The exception that was thrown, when successful</returns>
260         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgument(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false)261         public static ArgumentException ThrowsArgument(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false)
262         {
263             var ex = Throws<ArgumentException>(testCode, allowDerivedExceptions);
264 
265             if (paramName != null)
266             {
267                 Equal(paramName, ex.ParamName);
268             }
269 
270             VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true);
271 
272             return ex;
273         }
274 
275         /// <summary>
276         /// Verifies that the code throws an ArgumentException (or optionally any exception which derives from it).
277         /// </summary>
278         /// <param name="testCode">A delegate to the code to be tested</param>
279         /// <param name="paramName">The name of the parameter that should throw the exception</param>
280         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
281         /// <returns>The exception that was thrown, when successful</returns>
282         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgument(Func<object> testCode, string paramName, bool allowDerivedExceptions = false)283         public static ArgumentException ThrowsArgument(Func<object> testCode, string paramName, bool allowDerivedExceptions = false)
284         {
285             var ex = Throws<ArgumentException>(testCode, allowDerivedExceptions);
286 
287             if (paramName != null)
288             {
289                 Equal(paramName, ex.ParamName);
290             }
291 
292             return ex;
293         }
294 
295         /// <summary>
296         /// Verifies that the code throws an ArgumentNullException (or optionally any exception which derives from it).
297         /// </summary>
298         /// <param name="testCode">A delegate to the code to be tested</param>
299         /// <param name="paramName">The name of the parameter that should throw the exception</param>
300         /// <returns>The exception that was thrown, when successful</returns>
301         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentNull(Action testCode, string paramName)302         public static ArgumentNullException ThrowsArgumentNull(Action testCode, string paramName)
303         {
304             var ex = Throws<ArgumentNullException>(testCode, allowDerivedExceptions: false);
305 
306             if (paramName != null)
307             {
308                 Equal(paramName, ex.ParamName);
309             }
310 
311             return ex;
312         }
313 
314         /// <summary>
315         /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot
316         /// be null or empty.
317         /// </summary>
318         /// <param name="testCode">A delegate to the code to be tested</param>
319         /// <param name="paramName">The name of the parameter that should throw the exception</param>
320         /// <returns>The exception that was thrown, when successful</returns>
321         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentNullOrEmpty(Action testCode, string paramName)322         public static ArgumentException ThrowsArgumentNullOrEmpty(Action testCode, string paramName)
323         {
324             return Throws<ArgumentException>(testCode, "Value cannot be null or empty.\r\nParameter name: " + paramName, allowDerivedExceptions: false);
325         }
326 
327         /// <summary>
328         /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot
329         /// be null or empty string.
330         /// </summary>
331         /// <param name="testCode">A delegate to the code to be tested</param>
332         /// <param name="paramName">The name of the parameter that should throw the exception</param>
333         /// <returns>The exception that was thrown, when successful</returns>
334         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentNullOrEmptyString(Action testCode, string paramName)335         public static ArgumentException ThrowsArgumentNullOrEmptyString(Action testCode, string paramName)
336         {
337             return ThrowsArgument(testCode, paramName, "Value cannot be null or an empty string.", allowDerivedExceptions: true);
338         }
339 
340         /// <summary>
341         /// Verifies that the code throws an ArgumentOutOfRangeException (or optionally any exception which derives from it).
342         /// </summary>
343         /// <param name="testCode">A delegate to the code to be tested</param>
344         /// <param name="paramName">The name of the parameter that should throw the exception</param>
345         /// <param name="exceptionMessage">The exception message to verify</param>
346         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
347         /// <param name="actualValue">The actual value provided</param>
348         /// <returns>The exception that was thrown, when successful</returns>
349         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentOutOfRange(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false, object actualValue = null)350         public static ArgumentOutOfRangeException ThrowsArgumentOutOfRange(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false, object actualValue = null)
351         {
352             exceptionMessage = exceptionMessage != null
353                                    ? exceptionMessage + "\r\nParameter name: " + paramName +
354                                        (actualValue != null ? "\r\nActual value was " + actualValue.ToString() + "." : "")
355                                    : exceptionMessage;
356             var ex = Throws<ArgumentOutOfRangeException>(testCode, exceptionMessage, allowDerivedExceptions);
357 
358             if (paramName != null)
359             {
360                 Equal(paramName, ex.ParamName);
361             }
362 
363             return ex;
364         }
365 
366         /// <summary>
367         /// Verifies that the code throws an <see cref="ArgumentOutOfRangeException"/> with the expected message that indicates that
368         /// the value must be greater than the given <paramref name="value"/>.
369         /// </summary>
370         /// <param name="testCode">A delegate to the code to be tested</param>
371         /// <param name="paramName">The name of the parameter that should throw the exception</param>
372         /// <param name="actualValue">The actual value provided.</param>
373         /// <param name="value">The expected limit value.</param>
374         /// <returns>The exception that was thrown, when successful</returns>
375         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentGreaterThan(Action testCode, string paramName, string value, object actualValue = null)376         public static ArgumentOutOfRangeException ThrowsArgumentGreaterThan(Action testCode, string paramName, string value, object actualValue = null)
377         {
378             return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be greater than {0}.", value), false, actualValue);
379         }
380 
381         /// <summary>
382         /// Verifies that the code throws an <see cref="ArgumentOutOfRangeException"/> with the expected message that indicates that
383         /// the value must be greater than or equal to the given value.
384         /// </summary>
385         /// <param name="testCode">A delegate to the code to be tested</param>
386         /// <param name="paramName">The name of the parameter that should throw the exception</param>
387         /// <param name="value">The expected limit value.</param>
388         /// <returns>The exception that was thrown, when successful</returns>
389         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentGreaterThanOrEqualTo(Action testCode, string paramName, string value, object actualValue = null)390         public static ArgumentOutOfRangeException ThrowsArgumentGreaterThanOrEqualTo(Action testCode, string paramName, string value, object actualValue = null)
391         {
392             return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be greater than or equal to {0}.", value), false, actualValue);
393         }
394 
395         /// <summary>
396         /// Verifies that the code throws an <see cref="ArgumentOutOfRangeException"/> with the expected message that indicates that
397         /// the value must be less than the given <paramref name="maxValue"/>.
398         /// </summary>
399         /// <param name="testCode">A delegate to the code to be tested</param>
400         /// <param name="paramName">The name of the parameter that should throw the exception</param>
401         /// <param name="actualValue">The actual value provided.</param>
402         /// <param name="maxValue">The expected limit value.</param>
403         /// <returns>The exception that was thrown, when successful</returns>
404         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentLessThan(Action testCode, string paramName, string maxValue, object actualValue = null)405         public static ArgumentOutOfRangeException ThrowsArgumentLessThan(Action testCode, string paramName, string maxValue, object actualValue = null)
406         {
407             return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be less than {0}.", maxValue), false, actualValue);
408         }
409 
410         /// <summary>
411         /// Verifies that the code throws an <see cref="ArgumentOutOfRangeException"/> with the expected message that indicates that
412         /// the value must be less than or equal to the given <paramref name="maxValue"/>.
413         /// </summary>
414         /// <param name="testCode">A delegate to the code to be tested</param>
415         /// <param name="paramName">The name of the parameter that should throw the exception</param>
416         /// <param name="actualValue">The actual value provided.</param>
417         /// <param name="maxValue">The expected limit value.</param>
418         /// <returns>The exception that was thrown, when successful</returns>
419         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsArgumentLessThanOrEqualTo(Action testCode, string paramName, string maxValue, object actualValue = null)420         public static ArgumentOutOfRangeException ThrowsArgumentLessThanOrEqualTo(Action testCode, string paramName, string maxValue, object actualValue = null)
421         {
422             return ThrowsArgumentOutOfRange(testCode, paramName, String.Format("Value must be less than or equal to {0}.", maxValue), false, actualValue);
423         }
424 
425         /// <summary>
426         /// Verifies that the code throws an HttpException (or optionally any exception which derives from it).
427         /// </summary>
428         /// <param name="testCode">A delegate to the code to be tested</param>
429         /// <param name="exceptionMessage">The exception message to verify</param>
430         /// <param name="httpCode">The expected HTTP status code of the exception</param>
431         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
432         /// <returns>The exception that was thrown, when successful</returns>
433         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsHttpException(Action testCode, string exceptionMessage, int httpCode, bool allowDerivedExceptions = false)434         public static HttpException ThrowsHttpException(Action testCode, string exceptionMessage, int httpCode, bool allowDerivedExceptions = false)
435         {
436             var ex = Throws<HttpException>(testCode, exceptionMessage, allowDerivedExceptions);
437             Equal(httpCode, ex.GetHttpCode());
438             return ex;
439         }
440 
441         /// <summary>
442         /// Verifies that the code throws an InvalidEnumArgumentException (or optionally any exception which derives from it).
443         /// </summary>
444         /// <param name="testCode">A delegate to the code to be tested</param>
445         /// <param name="paramName">The name of the parameter that should throw the exception</param>
446         /// <param name="invalidValue">The expected invalid value that should appear in the message</param>
447         /// <param name="enumType">The type of the enumeration</param>
448         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
449         /// <returns>The exception that was thrown, when successful</returns>
450         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false)451         public static InvalidEnumArgumentException ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false)
452         {
453             return Throws<InvalidEnumArgumentException>(
454                 testCode,
455                 String.Format("The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.{3}Parameter name: {0}", paramName, invalidValue, enumType.Name, Environment.NewLine),
456                 allowDerivedExceptions
457             );
458         }
459 
460         /// <summary>
461         /// Verifies that the code throws an HttpException (or optionally any exception which derives from it).
462         /// </summary>
463         /// <param name="testCode">A delegate to the code to be tested</param>
464         /// <param name="objectName">The name of the object that was dispose</param>
465         /// <param name="allowDerivedExceptions">Pass true to allow exceptions which derive from TException; pass false, otherwise</param>
466         /// <returns>The exception that was thrown, when successful</returns>
467         /// <exception cref="ThrowsException">Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown</exception>
ThrowsObjectDisposed(Action testCode, string objectName, bool allowDerivedExceptions = false)468         public static ObjectDisposedException ThrowsObjectDisposed(Action testCode, string objectName, bool allowDerivedExceptions = false)
469         {
470             var ex = Throws<ObjectDisposedException>(testCode, allowDerivedExceptions);
471 
472             if (objectName != null)
473             {
474                 Equal(objectName, ex.ObjectName);
475             }
476 
477             return ex;
478         }
479 
UnwrapException(Exception exception)480         private static Exception UnwrapException(Exception exception)
481         {
482             AggregateException aggEx;
483             while ((aggEx = exception as AggregateException) != null)
484                 exception = aggEx.GetBaseException();
485 
486             return exception;
487         }
488 
VerifyExceptionMessage(Exception exception, string expectedMessage, bool partialMatch = false)489         private static void VerifyExceptionMessage(Exception exception, string expectedMessage, bool partialMatch = false)
490         {
491             if (expectedMessage != null && CurrentCultureIsEnglish)
492             {
493                 if (!partialMatch)
494                 {
495                     Equal(expectedMessage, exception.Message);
496                 }
497                 else
498                 {
499                     Contains(expectedMessage, exception.Message);
500                 }
501             }
502         }
503 
504         // Custom ThrowsException so we can filter the stack trace.
505         private class ThrowsException : Xunit.Sdk.ThrowsException
506         {
ThrowsException(Type type)507             public ThrowsException(Type type) : base(type) { }
508 
ThrowsException(Type type, Exception ex)509             public ThrowsException(Type type, Exception ex) : base(type, ex) { }
510 
ExcludeStackFrame(string stackFrame)511             protected override bool ExcludeStackFrame(string stackFrame)
512             {
513                 if (stackFrame.StartsWith("at Microsoft.TestCommon.AssertEx.", StringComparison.OrdinalIgnoreCase))
514                 {
515                     return true;
516                 }
517 
518                 return base.ExcludeStackFrame(stackFrame);
519             }
520         }
521     }
522 }
523