Browse Source
* test(headless): reproduce cleanup exception dispatch hang A dispatcher job that throws during work-item cleanup leaves the dispatch task incomplete and prevents later work from running. The session contract requires every queued dispatch to reach a terminal state. Add behavioral coverage that posts a throwing cleanup job, expects its exception from Dispatch, and verifies the assembly session can process the next dispatch. The shared NUnit test runs under both PerTest and PerAssembly projects. * fix(headless): report dispatch cleanup exceptions An exception during application cleanup escapes the work item before its completion source is settled, faults the private consumer task, and leaves current and future Dispatch calls waiting forever. Capture cleanup failures as the work item's exception so the consumer can continue. Restore synchronization context, locator scope, and dispatcher state in finally blocks before processing later work. --------- Co-authored-by: Julien Lebosquain <julien@lebosquain.net>pull/21812/head
committed by
GitHub
2 changed files with 69 additions and 9 deletions
@ -0,0 +1,32 @@ |
|||||
|
#if NUNIT
|
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Threading; |
||||
|
|
||||
|
namespace Avalonia.Headless.UnitTests; |
||||
|
|
||||
|
public class HeadlessUnitTestSessionTests |
||||
|
{ |
||||
|
[Test] |
||||
|
public async Task Dispatch_Should_Report_Cleanup_Exceptions_And_Continue() |
||||
|
{ |
||||
|
var session = HeadlessUnitTestSession.GetOrStartForAssembly(GetType().Assembly); |
||||
|
|
||||
|
const string message = "Thrown by a dispatcher job during cleanup."; |
||||
|
var poisonedDispatch = session.Dispatch( |
||||
|
() => Dispatcher.UIThread.Post(() => throw new InvalidOperationException(message)), |
||||
|
CancellationToken.None); |
||||
|
|
||||
|
var exception = Assert.ThrowsAsync<InvalidOperationException>(async () => |
||||
|
await poisonedDispatch.WaitAsync(TimeSpan.FromSeconds(10))); |
||||
|
|
||||
|
Assert.That(exception!.Message, Is.EqualTo(message)); |
||||
|
|
||||
|
var result = await session.Dispatch(() => 42, CancellationToken.None) |
||||
|
.WaitAsync(TimeSpan.FromSeconds(10)); |
||||
|
|
||||
|
Assert.That(result, Is.EqualTo(42)); |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
Loading…
Reference in new issue