DispatcherTests.Exception.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. using System;
  2. using System.Threading;
  3. using System.Threading.Tasks;
  4. using Avalonia.Controls.Platform;
  5. using Avalonia.Threading;
  6. using Xunit;
  7. namespace Avalonia.Base.UnitTests;
  8. // Some of these exceptions are based from https://github.com/dotnet/wpf-test/blob/05797008bb4975ceeb71be36c47f01688f535d53/src/Test/ElementServices/FeatureTests/Untrusted/Dispatcher/UnhandledExceptionTest.cs#L30
  9. public partial class DispatcherTests
  10. {
  11. private const string ExpectedExceptionText = "Exception thrown inside Dispatcher.Invoke / Dispatcher.BeginInvoke.";
  12. private int _numberOfHandlerOnUnhandledEventInvoked;
  13. private int _numberOfHandlerOnUnhandledEventFilterInvoked;
  14. public DispatcherTests()
  15. {
  16. _numberOfHandlerOnUnhandledEventInvoked = 0;
  17. _numberOfHandlerOnUnhandledEventFilterInvoked = 0;
  18. }
  19. [Fact]
  20. public void DispatcherHandlesExceptionWithPost()
  21. {
  22. var impl = new ManagedDispatcherImpl(null);
  23. var disp = new Dispatcher(impl);
  24. var handled = false;
  25. var executed = false;
  26. disp.UnhandledException += (sender, args) =>
  27. {
  28. handled = true;
  29. args.Handled = true;
  30. };
  31. disp.Post(() => ThrowAnException());
  32. disp.Post(() => executed = true);
  33. disp.RunJobs();
  34. Assert.True(handled);
  35. Assert.True(executed);
  36. }
  37. [Fact]
  38. public void SyncContextExceptionCanBeHandledWithPost()
  39. {
  40. var impl = new ManagedDispatcherImpl(null);
  41. var disp = new Dispatcher(impl);
  42. var syncContext = disp.GetContextWithPriority(DispatcherPriority.Background);
  43. var handled = false;
  44. var executed = false;
  45. disp.UnhandledException += (sender, args) =>
  46. {
  47. handled = true;
  48. args.Handled = true;
  49. };
  50. syncContext.Post(_ => ThrowAnException(), null);
  51. syncContext.Post(_ => executed = true, null);
  52. disp.RunJobs();
  53. Assert.True(handled);
  54. Assert.True(executed);
  55. }
  56. [Fact]
  57. public void CanRemoveDispatcherExceptionHandler()
  58. {
  59. var impl = new ManagedDispatcherImpl(null);
  60. var dispatcher = new Dispatcher(impl);
  61. var caughtCorrectException = false;
  62. dispatcher.UnhandledExceptionFilter +=
  63. HandlerOnUnhandledExceptionFilterRequestCatch;
  64. dispatcher.UnhandledException +=
  65. HandlerOnUnhandledExceptionNotHandled;
  66. dispatcher.UnhandledExceptionFilter -=
  67. HandlerOnUnhandledExceptionFilterRequestCatch;
  68. dispatcher.UnhandledException -=
  69. HandlerOnUnhandledExceptionNotHandled;
  70. try
  71. {
  72. dispatcher.Post(ThrowAnException, DispatcherPriority.Normal);
  73. dispatcher.RunJobs();
  74. }
  75. catch (Exception e)
  76. {
  77. caughtCorrectException = e.Message == ExpectedExceptionText;
  78. }
  79. finally
  80. {
  81. Verification(caughtCorrectException, 0, 0);
  82. }
  83. }
  84. [Fact]
  85. public void CanHandleExceptionWithUnhandledException()
  86. {
  87. var impl = new ManagedDispatcherImpl(null);
  88. var dispatcher = new Dispatcher(impl);
  89. dispatcher.UnhandledExceptionFilter +=
  90. HandlerOnUnhandledExceptionFilterRequestCatch;
  91. dispatcher.UnhandledException +=
  92. HandlerOnUnhandledExceptionHandled;
  93. var caughtCorrectException = true;
  94. try
  95. {
  96. dispatcher.Post(ThrowAnException, DispatcherPriority.Normal);
  97. dispatcher.RunJobs();
  98. }
  99. catch (Exception)
  100. {
  101. // should be no exception here.
  102. caughtCorrectException = false;
  103. }
  104. finally
  105. {
  106. Verification(caughtCorrectException, 1, 1);
  107. }
  108. }
  109. [Fact]
  110. public void InvokeMethodDoesntTriggerUnhandledException()
  111. {
  112. var impl = new ManagedDispatcherImpl(null);
  113. var dispatcher = new Dispatcher(impl);
  114. dispatcher.UnhandledExceptionFilter +=
  115. HandlerOnUnhandledExceptionFilterRequestCatch;
  116. dispatcher.UnhandledException +=
  117. HandlerOnUnhandledExceptionHandled;
  118. var caughtCorrectException = false;
  119. try
  120. {
  121. // Since both Invoke and InvokeAsync can throw exception, there is no need to pass them to the UnhandledException.
  122. dispatcher.Invoke(ThrowAnException, DispatcherPriority.Normal);
  123. dispatcher.RunJobs();
  124. }
  125. catch (Exception e)
  126. {
  127. // should be no exception here.
  128. caughtCorrectException = e.Message == ExpectedExceptionText;
  129. }
  130. finally
  131. {
  132. Verification(caughtCorrectException, 0, 0);
  133. }
  134. }
  135. [Fact]
  136. public void InvokeAsyncMethodDoesntTriggerUnhandledException()
  137. {
  138. var impl = new ManagedDispatcherImpl(null);
  139. var dispatcher = new Dispatcher(impl);
  140. dispatcher.UnhandledExceptionFilter +=
  141. HandlerOnUnhandledExceptionFilterRequestCatch;
  142. dispatcher.UnhandledException +=
  143. HandlerOnUnhandledExceptionHandled;
  144. var caughtCorrectException = false;
  145. try
  146. {
  147. // Since both Invoke and InvokeAsync can throw exception, there is no need to pass them to the UnhandledException.
  148. var op = dispatcher.InvokeAsync(ThrowAnException, DispatcherPriority.Normal);
  149. op.Wait();
  150. dispatcher.RunJobs();
  151. }
  152. catch (Exception e)
  153. {
  154. // should be no exception here.
  155. caughtCorrectException = e.Message == ExpectedExceptionText;
  156. }
  157. finally
  158. {
  159. Verification(caughtCorrectException, 0, 0);
  160. }
  161. }
  162. [Fact]
  163. public void CanRethrowExceptionWithUnhandledException()
  164. {
  165. var impl = new ManagedDispatcherImpl(null);
  166. var dispatcher = new Dispatcher(impl);
  167. dispatcher.UnhandledExceptionFilter +=
  168. HandlerOnUnhandledExceptionFilterRequestCatch;
  169. dispatcher.UnhandledException +=
  170. HandlerOnUnhandledExceptionNotHandled;
  171. var caughtCorrectException = false;
  172. try
  173. {
  174. dispatcher.Post(ThrowAnException, DispatcherPriority.Normal);
  175. dispatcher.RunJobs();
  176. }
  177. catch (Exception e)
  178. {
  179. caughtCorrectException = e.Message == ExpectedExceptionText;
  180. }
  181. finally
  182. {
  183. Verification(caughtCorrectException, 1, 1);
  184. }
  185. }
  186. [Fact]
  187. public void MultipleUnhandledExceptionFilterCannotResetRequestCatchFlag()
  188. {
  189. var impl = new ManagedDispatcherImpl(null);
  190. var dispatcher = new Dispatcher(impl);
  191. dispatcher.UnhandledExceptionFilter +=
  192. HandlerOnUnhandledExceptionFilterNotRequestCatch;
  193. dispatcher.UnhandledExceptionFilter +=
  194. HandlerOnUnhandledExceptionFilterRequestCatch;
  195. dispatcher.UnhandledException +=
  196. HandlerOnUnhandledExceptionNotHandled;
  197. dispatcher.UnhandledException +=
  198. HandlerOnUnhandledExceptionHandled;
  199. var caughtCorrectException = false;
  200. try
  201. {
  202. dispatcher.Post(ThrowAnException, DispatcherPriority.Normal);
  203. dispatcher.RunJobs();
  204. }
  205. catch (Exception e)
  206. {
  207. caughtCorrectException = e.Message == ExpectedExceptionText;
  208. }
  209. finally
  210. {
  211. Verification(caughtCorrectException, 0, 2);
  212. }
  213. }
  214. [Fact]
  215. public void MultipleUnhandledExceptionCannotResetHandleFlag()
  216. {
  217. var impl = new ManagedDispatcherImpl(null);
  218. var dispatcher = new Dispatcher(impl);
  219. dispatcher.UnhandledExceptionFilter +=
  220. HandlerOnUnhandledExceptionFilterRequestCatch;
  221. dispatcher.UnhandledException +=
  222. HandlerOnUnhandledExceptionHandled;
  223. dispatcher.UnhandledException +=
  224. HandlerOnUnhandledExceptionNotHandled;
  225. var caughtCorrectException = true;
  226. try
  227. {
  228. dispatcher.Post(ThrowAnException, DispatcherPriority.Normal);
  229. dispatcher.RunJobs();
  230. }
  231. catch (Exception)
  232. {
  233. // should be no exception here.
  234. caughtCorrectException = false;
  235. }
  236. finally
  237. {
  238. Verification(caughtCorrectException, 1, 1);
  239. }
  240. }
  241. [Fact]
  242. public void CanPushFrameAndShutdownDispatcherFromUnhandledException()
  243. {
  244. var impl = new ManagedDispatcherImpl(null);
  245. var dispatcher = new Dispatcher(impl);
  246. dispatcher.UnhandledExceptionFilter +=
  247. HandlerOnUnhandledExceptionFilterNotRequestCatchPushFrame;
  248. dispatcher.UnhandledException +=
  249. HandlerOnUnhandledExceptionHandledPushFrame;
  250. var caughtCorrectException = false;
  251. try
  252. {
  253. dispatcher.Post(ThrowAnException, DispatcherPriority.Normal);
  254. dispatcher.RunJobs();
  255. }
  256. catch (Exception e)
  257. {
  258. caughtCorrectException = e.Message == ExpectedExceptionText;
  259. }
  260. finally
  261. {
  262. Verification(caughtCorrectException, 0, 1);
  263. }
  264. }
  265. private void Verification(bool caughtCorrectException, int numberOfHandlerOnUnhandledEventShouldInvoke,
  266. int numberOfHandlerOnUnhandledEventFilterShouldInvoke)
  267. {
  268. Assert.True(
  269. _numberOfHandlerOnUnhandledEventInvoked >= numberOfHandlerOnUnhandledEventShouldInvoke,
  270. "Number of handler invoked on UnhandledException is invalid");
  271. Assert.True(
  272. _numberOfHandlerOnUnhandledEventFilterInvoked >= numberOfHandlerOnUnhandledEventFilterShouldInvoke,
  273. "Number of handler invoked on UnhandledExceptionFilter is invalid");
  274. Assert.True(caughtCorrectException, "Wrong exception caught.");
  275. }
  276. private void HandlerOnUnhandledExceptionFilterRequestCatch(object sender,
  277. DispatcherUnhandledExceptionFilterEventArgs args)
  278. {
  279. args.RequestCatch = true;
  280. _numberOfHandlerOnUnhandledEventFilterInvoked += 1;
  281. Assert.Equal(ExpectedExceptionText, args.Exception.Message);
  282. }
  283. private void HandlerOnUnhandledExceptionFilterNotRequestCatchPushFrame(object sender,
  284. DispatcherUnhandledExceptionFilterEventArgs args)
  285. {
  286. HandlerOnUnhandledExceptionFilterNotRequestCatch(sender, args);
  287. var frame = new DispatcherFrame();
  288. args.Dispatcher.InvokeAsync(() => frame.Continue = false, DispatcherPriority.Background);
  289. args.Dispatcher.PushFrame(frame);
  290. }
  291. private void HandlerOnUnhandledExceptionFilterNotRequestCatch(object sender,
  292. DispatcherUnhandledExceptionFilterEventArgs args)
  293. {
  294. args.RequestCatch = false;
  295. _numberOfHandlerOnUnhandledEventFilterInvoked += 1;
  296. Assert.Equal(ExpectedExceptionText, args.Exception.Message);
  297. }
  298. private void HandlerOnUnhandledExceptionHandledPushFrame(object sender, DispatcherUnhandledExceptionEventArgs args)
  299. {
  300. Assert.Equal(ExpectedExceptionText, args.Exception.Message);
  301. Assert.False(_numberOfHandlerOnUnhandledEventFilterInvoked == 0,
  302. "UnhandledExceptionFilter should be invoked before UnhandledException.");
  303. args.Handled = true;
  304. _numberOfHandlerOnUnhandledEventInvoked += 1;
  305. var dispatcher = args.Dispatcher;
  306. var frame = new DispatcherFrame();
  307. dispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
  308. dispatcher.PushFrame(frame);
  309. }
  310. private void HandlerOnUnhandledExceptionHandled(object sender, DispatcherUnhandledExceptionEventArgs args)
  311. {
  312. Assert.Equal(ExpectedExceptionText, args.Exception.Message);
  313. Assert.False(_numberOfHandlerOnUnhandledEventFilterInvoked == 0,
  314. "UnhandledExceptionFilter should be invoked before UnhandledException.");
  315. args.Handled = true;
  316. _numberOfHandlerOnUnhandledEventInvoked += 1;
  317. }
  318. private void HandlerOnUnhandledExceptionNotHandled(object sender, DispatcherUnhandledExceptionEventArgs args)
  319. {
  320. Assert.Equal(ExpectedExceptionText, args.Exception.Message);
  321. Assert.False(_numberOfHandlerOnUnhandledEventFilterInvoked == 0,
  322. "UnhandledExceptionFilter should be invoked before UnhandledException.");
  323. args.Handled = false;
  324. _numberOfHandlerOnUnhandledEventInvoked += 1;
  325. }
  326. private void ThrowAnException()
  327. {
  328. throw new Exception(ExpectedExceptionText);
  329. }
  330. }