VerifyEmptyDispatcherAfterTestAttribute.cs 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using Avalonia.Controls;
  5. using Avalonia.Threading;
  6. using Xunit;
  7. using Xunit.Sdk;
  8. namespace Avalonia.UnitTests;
  9. public sealed class VerifyEmptyDispatcherAfterTestAttribute : BeforeAfterTestAttribute
  10. {
  11. public override void After(MethodInfo methodUnderTest)
  12. {
  13. if (typeof(ScopedTestBase).IsAssignableFrom(methodUnderTest.DeclaringType))
  14. return;
  15. var dispatcher = Dispatcher.UIThread;
  16. var jobs = dispatcher.GetJobs();
  17. if (jobs.Count == 0)
  18. return;
  19. dispatcher.ClearJobs();
  20. // Ignore the Control.Loaded callback. It might happen synchronously or might be posted.
  21. if (jobs.Count == 1 && IsLoadedCallback(jobs[0]))
  22. return;
  23. Assert.Fail(
  24. $"The test left {jobs.Count} unprocessed dispatcher {(jobs.Count == 1 ? "job" : "jobs")}:\n" +
  25. $"{string.Join(Environment.NewLine, jobs.Select(job => $" - {job.DebugDisplay}"))}\n" +
  26. $"Consider using ScopedTestBase or UnitTestApplication.Start().");
  27. static bool IsLoadedCallback(DispatcherOperation job)
  28. => job.Priority == DispatcherPriority.Loaded &&
  29. (job.Callback as Delegate)?.Method.DeclaringType?.DeclaringType == typeof(Control);
  30. }
  31. }