DispatcherTests.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Threading;
  5. using Xunit;
  6. namespace Avalonia.Base.UnitTests;
  7. public class DispatcherTests
  8. {
  9. class SimpleDispatcherImpl : IDispatcherImpl, IDispatcherImplWithPendingInput
  10. {
  11. public bool CurrentThreadIsLoopThread => true;
  12. public void Signal() => AskedForSignal = true;
  13. public event Action Signaled;
  14. public event Action Timer;
  15. public long? NextTimer { get; private set; }
  16. public bool AskedForSignal { get; private set; }
  17. public void UpdateTimer(long? dueTimeInTicks)
  18. {
  19. NextTimer = dueTimeInTicks;
  20. }
  21. public long Now { get; set; }
  22. public void ExecuteSignal()
  23. {
  24. if (!AskedForSignal)
  25. return;
  26. AskedForSignal = false;
  27. Signaled?.Invoke();
  28. }
  29. public void ExecuteTimer()
  30. {
  31. if (NextTimer == null)
  32. return;
  33. Now = NextTimer.Value;
  34. Timer?.Invoke();
  35. }
  36. public bool CanQueryPendingInput => TestInputPending != null;
  37. public bool HasPendingInput => TestInputPending == true;
  38. public bool? TestInputPending { get; set; }
  39. }
  40. [Fact]
  41. public void DispatcherExecutesJobsAccordingToPriority()
  42. {
  43. var impl = new SimpleDispatcherImpl();
  44. var disp = new Dispatcher(impl);
  45. var actions = new List<string>();
  46. disp.Post(()=>actions.Add("Background"), DispatcherPriority.Background);
  47. disp.Post(()=>actions.Add("Render"), DispatcherPriority.Render);
  48. disp.Post(()=>actions.Add("Input"), DispatcherPriority.Input);
  49. Assert.True(impl.AskedForSignal);
  50. impl.ExecuteSignal();
  51. Assert.Equal(new[] { "Render", "Input", "Background" }, actions);
  52. }
  53. [Fact]
  54. public void DispatcherPreservesOrderWhenChangingPriority()
  55. {
  56. var impl = new SimpleDispatcherImpl();
  57. var disp = new Dispatcher(impl);
  58. var actions = new List<string>();
  59. var toPromote = disp.InvokeAsync(()=>actions.Add("PromotedRender"), DispatcherPriority.Background);
  60. var toPromote2 = disp.InvokeAsync(()=>actions.Add("PromotedRender2"), DispatcherPriority.Input);
  61. disp.Post(() => actions.Add("Render"), DispatcherPriority.Render);
  62. toPromote.Priority = DispatcherPriority.Render;
  63. toPromote2.Priority = DispatcherPriority.Render;
  64. Assert.True(impl.AskedForSignal);
  65. impl.ExecuteSignal();
  66. Assert.Equal(new[] { "PromotedRender", "PromotedRender2", "Render" }, actions);
  67. }
  68. [Fact]
  69. public void DispatcherStopsItemProcessingWhenInteractivityDeadlineIsReached()
  70. {
  71. var impl = new SimpleDispatcherImpl();
  72. var disp = new Dispatcher(impl);
  73. var actions = new List<int>();
  74. for (var c = 0; c < 10; c++)
  75. {
  76. var itemId = c;
  77. disp.Post(() =>
  78. {
  79. actions.Add(itemId);
  80. impl.Now += 20;
  81. }, DispatcherPriority.Background);
  82. }
  83. Assert.False(impl.AskedForSignal);
  84. Assert.NotNull(impl.NextTimer);
  85. for (var c = 0; c < 4; c++)
  86. {
  87. Assert.NotNull(impl.NextTimer);
  88. Assert.False(impl.AskedForSignal);
  89. impl.ExecuteTimer();
  90. Assert.False(impl.AskedForSignal);
  91. impl.ExecuteSignal();
  92. var expectedCount = (c + 1) * 3;
  93. if (c == 3)
  94. expectedCount = 10;
  95. Assert.Equal(Enumerable.Range(0, expectedCount), actions);
  96. Assert.False(impl.AskedForSignal);
  97. if (c < 3)
  98. {
  99. Assert.True(impl.NextTimer > impl.Now);
  100. }
  101. else
  102. Assert.Null(impl.NextTimer);
  103. }
  104. }
  105. [Fact]
  106. public void DispatcherStopsItemProcessingWhenInputIsPending()
  107. {
  108. var impl = new SimpleDispatcherImpl();
  109. impl.TestInputPending = true;
  110. var disp = new Dispatcher(impl);
  111. var actions = new List<int>();
  112. for (var c = 0; c < 10; c++)
  113. {
  114. var itemId = c;
  115. disp.Post(() =>
  116. {
  117. actions.Add(itemId);
  118. if (itemId == 0 || itemId == 3 || itemId == 7)
  119. impl.TestInputPending = true;
  120. }, DispatcherPriority.Background);
  121. }
  122. Assert.False(impl.AskedForSignal);
  123. Assert.NotNull(impl.NextTimer);
  124. impl.TestInputPending = false;
  125. for (var c = 0; c < 4; c++)
  126. {
  127. Assert.NotNull(impl.NextTimer);
  128. impl.ExecuteTimer();
  129. Assert.False(impl.AskedForSignal);
  130. var expectedCount = c switch
  131. {
  132. 0 => 1,
  133. 1 => 4,
  134. 2 => 8,
  135. 3 => 10
  136. };
  137. Assert.Equal(Enumerable.Range(0, expectedCount), actions);
  138. Assert.False(impl.AskedForSignal);
  139. if (c < 3)
  140. {
  141. Assert.True(impl.NextTimer > impl.Now);
  142. impl.Now = impl.NextTimer.Value + 1;
  143. }
  144. else
  145. Assert.Null(impl.NextTimer);
  146. impl.TestInputPending = false;
  147. }
  148. }
  149. }