ExceptionAssert.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // -----------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // -----------------------------------------------------------------------
  4. using System;
  5. using Microsoft.VisualStudio.TestTools.UnitTesting;
  6. using System.Runtime.Serialization;
  7. namespace System.UnitTesting
  8. {
  9. public static class ExceptionAssert
  10. {
  11. // NOTE: To catch state corrupting exceptions, it by design that
  12. // the ThrowsXXX methods retry by default. To prevent this in a
  13. // test, simply use one of the overloads that takes a RetryMode.
  14. /// <summary>
  15. /// Verifies that the exception has the default message generated by the base Exception class.
  16. /// </summary>
  17. public static void HasDefaultMessage(Exception exception)
  18. {
  19. Assert.IsNotNull(exception);
  20. // Exception of type '[typename]' was thrown
  21. StringAssert.Contains(exception.Message, exception.GetType().FullName);
  22. }
  23. /// <summary>
  24. /// Verifies that the specified action throws a SerializationException.
  25. /// </summary>
  26. public static SerializationException ThrowsSerialization(string memberName, Action action)
  27. {
  28. var exception = Throws<SerializationException>(RetryMode.Retry, action, (actual, retryCount) =>
  29. {
  30. AssertSerializationMemberName(memberName, actual, retryCount);
  31. });
  32. return exception;
  33. }
  34. /// <summary>
  35. /// Verifies that the specified action throws an ObjectDisposedException.
  36. /// </summary>
  37. public static ObjectDisposedException ThrowsDisposed(object instance, Action action)
  38. {
  39. var exception = Throws<ObjectDisposedException>(RetryMode.Retry, action, (actual, retryCount) =>
  40. {
  41. AssertObjectDisposed(instance, actual, retryCount);
  42. });
  43. return exception;
  44. }
  45. /// <summary>
  46. /// Verifies that the specified action throws an ArgumentNullException.
  47. /// </summary>
  48. public static ArgumentNullException ThrowsArgumentNull(string parameterName, Action action)
  49. {
  50. return ThrowsArgument<ArgumentNullException>(parameterName, action);
  51. }
  52. /// <summary>
  53. /// Verifies that the specified action throws an ArgumentException.
  54. /// </summary>
  55. public static ArgumentException ThrowsArgument(string parameterName, Action action)
  56. {
  57. return ThrowsArgument<ArgumentException>(parameterName, action);
  58. }
  59. /// <summary>
  60. /// Verifies that the specified action throws an ArgumentException of type <typeparam name="T"/>.
  61. /// </summary>
  62. public static T ThrowsArgument<T>(string parameterName, Action action)
  63. where T : ArgumentException
  64. {
  65. var exception = Throws<T>(RetryMode.Retry, action, (actual, retryCount) =>
  66. {
  67. AssertSameParameterName(parameterName, actual, retryCount);
  68. });
  69. return exception;
  70. }
  71. /// <summary>
  72. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  73. /// with the specified inner exception of type <typeparam name="TInner"/>.
  74. /// </summary>
  75. public static T Throws<T, TInner>(Action action)
  76. where T : Exception
  77. where TInner : Exception
  78. {
  79. return Throws<T, TInner>(RetryMode.Retry, action);
  80. }
  81. /// <summary>
  82. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  83. /// with the specified inner exception of type <typeparam name="TInner"/>, and indicating
  84. /// whether to retry.
  85. /// </summary>
  86. public static T Throws<T, TInner>(RetryMode retry, Action action)
  87. where T : Exception
  88. where TInner : Exception
  89. {
  90. return Throws<T, TInner>(retry, action, (Action<T, int>)null);
  91. }
  92. /// <summary>
  93. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  94. /// with the specified inner exception of type <typeparam name="TInner"/>, indicating
  95. /// whether to retry and running the specified validator.
  96. /// </summary>
  97. public static T Throws<T, TInner>(RetryMode retry, Action action, Action<T, int> validator)
  98. where T : Exception
  99. where TInner : Exception
  100. {
  101. var exception = Throws<T>(retry, action, (actual, retryCount) =>
  102. {
  103. AssertIsExactInstanceOfInner(typeof(TInner), actual, retryCount);
  104. if (validator != null)
  105. {
  106. validator(actual, retryCount);
  107. }
  108. });
  109. return exception;
  110. }
  111. /// <summary>
  112. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  113. /// with the specified inner exception.
  114. /// </summary>
  115. public static T Throws<T>(Exception innerException, Action action)
  116. where T : Exception
  117. {
  118. return Throws<T>(innerException, RetryMode.Retry, action, (Action<T, int>)null);
  119. }
  120. /// <summary>
  121. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  122. /// with the specified inner exception, and indicating whether to retry.
  123. /// </summary>
  124. public static T Throws<T>(Exception innerException, RetryMode retry, Action action)
  125. where T : Exception
  126. {
  127. return Throws<T>(innerException, RetryMode.Retry, action, (Action<T, int>)null);
  128. }
  129. /// <summary>
  130. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  131. /// with the specified inner exception, indicating whether to retry and running the
  132. /// specified validator.
  133. /// </summary>
  134. public static T Throws<T>(Exception innerException, RetryMode retry, Action action, Action<T, int> validator)
  135. where T : Exception
  136. {
  137. T exception = Throws<T>(retry, action, (actual, retryCount) =>
  138. {
  139. AssertSameInner(innerException, actual, retryCount);
  140. if (validator != null)
  141. {
  142. validator(actual, retryCount);
  143. }
  144. });
  145. return exception;
  146. }
  147. /// <summary>
  148. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>.
  149. /// </summary>
  150. public static T Throws<T>(Action action)
  151. where T : Exception
  152. {
  153. return Throws<T>(RetryMode.Retry, action, (Action<T, int>)null);
  154. }
  155. /// <summary>
  156. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  157. /// indicating whether to retry.
  158. /// </summary>
  159. public static T Throws<T>(RetryMode retry, Action action)
  160. where T : Exception
  161. {
  162. return Throws<T>(retry, action, (Action<T, int>)null);
  163. }
  164. /// <summary>
  165. /// Verifies that the specified action throws an exception of type <typeparam name="T"/>,
  166. /// indicating whether to retry and running the specified validator.
  167. /// </summary>
  168. public static T Throws<T>(RetryMode retry, Action action, Action<T, int> validator)
  169. where T : Exception
  170. {
  171. var exception = (T)Run(retry, action, (actual, retryCount) =>
  172. {
  173. AssertIsExactInstanceOf(typeof(T), actual, retryCount);
  174. if (validator != null)
  175. {
  176. validator((T)actual, retryCount);
  177. }
  178. });
  179. return exception;
  180. }
  181. /// <summary>
  182. /// Verifies that the specified action throws the specified exception.
  183. /// </summary>
  184. public static void Throws(Exception expected, Action action)
  185. {
  186. Throws(expected, RetryMode.Retry, action);
  187. }
  188. /// <summary>
  189. /// Verifies that the specified action throws the specified exception,
  190. /// indicating whether to retry.
  191. /// </summary>
  192. public static void Throws(Exception expected, RetryMode retry, Action action)
  193. {
  194. Throws(expected, retry, action, (Action<Exception, int>)null);
  195. }
  196. /// <summary>
  197. /// Verifies that the specified action throws the specified exception,
  198. /// indicating whether to retry and running the specified validator.
  199. /// </summary>
  200. public static void Throws(Exception expected, RetryMode retry, Action action, Action<Exception, int> validator)
  201. {
  202. Run(retry, action, (actual, retryCount) =>
  203. {
  204. AssertSame(expected, actual, retryCount);
  205. if (validator != null)
  206. {
  207. validator(actual, retryCount);
  208. }
  209. });
  210. }
  211. private static Exception Run(RetryMode retry, Action action, Action<Exception, int> validator)
  212. {
  213. Exception exception = null;
  214. for (int i = -1; i < (int)retry; i++)
  215. {
  216. exception = Run(action);
  217. validator(exception, i + 2);
  218. }
  219. return exception;
  220. }
  221. private static Exception Run(Action action)
  222. {
  223. try
  224. {
  225. action();
  226. return null;
  227. }
  228. catch (Exception ex)
  229. {
  230. return ex;
  231. }
  232. }
  233. private static void AssertSerializationMemberName(string memberName, SerializationException actual, int retryCount)
  234. {
  235. // Unfortunately, SerializationException does not provide a way to get our hands on the
  236. // the actual member that was missing from the SerializationInfo, so we need to grok the
  237. // message string.
  238. // Member '[memberName]' was not found.
  239. StringAssert.Contains(actual.Message, "'" + memberName + "'", "Retry Count {0}: Expected SerializationException MemberName to be '{1}'", retryCount, memberName);
  240. }
  241. private static void AssertObjectDisposed(object instance, ObjectDisposedException actual, int retryCount)
  242. {
  243. string objectName = instance.GetType().FullName;
  244. Assert.AreEqual(objectName, actual.ObjectName, "Retry Count {0}: Expected {1}.ObjectName to be '{2}', however, '{3}' is.", retryCount, actual.GetType().Name, objectName, actual.ObjectName);
  245. }
  246. private static void AssertSameParameterName(string parameterName, ArgumentException actual, int retryCount)
  247. {
  248. #if !SILVERLIGHT
  249. Assert.AreEqual(parameterName, actual.ParamName, "Retry Count {0}: Expected {1}.ParamName to be '{2}', however, '{3}' is.", retryCount, actual.GetType().Name, parameterName, actual.ParamName);
  250. #else
  251. // Silverlight doesn't have ArgumentException.ParamName
  252. StringAssert.Contains(actual.Message, parameterName, "Retry Count {0}: Expected {1}.ParamName to be '{2}'", retryCount, actual.GetType().Name, parameterName);
  253. #endif
  254. }
  255. private static void AssertSame(Exception expected, Exception actual, int retryCount)
  256. {
  257. Assert.AreSame(expected, actual, "Retry Count {0}: Expected '{1}' to be thrown, however, '{2}' was thrown.", retryCount, expected, actual);
  258. }
  259. private static void AssertSameInner(Exception innerException, Exception actual, int retryCount)
  260. {
  261. Assert.AreSame(innerException, actual.InnerException, "Retry Count {0}: Expected '{1}' to be the inner exception, however, '{2}' is.", retryCount, innerException, actual.InnerException);
  262. }
  263. private static void AssertIsExactInstanceOf(Type expectedType, Exception actual, int retryCount)
  264. {
  265. if (actual == null)
  266. Assert.Fail("Retry Count {0}: Expected '{1}' to be thrown", retryCount, expectedType);
  267. Type actualType = actual.GetType();
  268. Assert.AreSame(expectedType, actualType, "Retry Count {0}: Expected '{1}' to be thrown, however, '{2}' was thrown.", retryCount, expectedType, actualType);
  269. }
  270. private static void AssertIsExactInstanceOfInner(Type expectedType, Exception actual, int retryCount)
  271. {
  272. if (actual.InnerException == null)
  273. Assert.Fail("Retry Count {0}: Expected '{1}' be the inner exception, however, it is null.", retryCount, expectedType);
  274. Type actualType = actual.InnerException.GetType();
  275. Assert.AreSame(expectedType, actualType, "Retry Count {0}: Expected '{1}' to be the inner exception, however, '{2}' is.", retryCount, expectedType, actualType);
  276. }
  277. }
  278. }