CompositorTestServices.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Threading.Tasks;
  6. using Avalonia.Controls;
  7. using Avalonia.Controls.Embedding;
  8. using Avalonia.Controls.Platform.Surfaces;
  9. using Avalonia.Controls.Presenters;
  10. using Avalonia.Controls.Templates;
  11. using Avalonia.Data;
  12. using Avalonia.Input;
  13. using Avalonia.Input.Raw;
  14. using Avalonia.Platform;
  15. using Avalonia.Rendering;
  16. using Avalonia.Rendering.Composition;
  17. using Avalonia.Threading;
  18. using Xunit;
  19. namespace Avalonia.UnitTests;
  20. public class CompositorTestServices : IDisposable
  21. {
  22. private readonly IDisposable _app;
  23. public Compositor Compositor { get; }
  24. internal CompositingRenderer Renderer { get; }
  25. public ManualRenderTimer Timer { get; } = new();
  26. public EmbeddableControlRoot TopLevel { get; }
  27. public DebugEvents Events { get; } = new();
  28. public void Dispose()
  29. {
  30. TopLevel.StopRendering();
  31. TopLevel.Dispose();
  32. _app.Dispose();
  33. }
  34. public CompositorTestServices(Size? size = null, IPlatformRenderInterface renderInterface = null)
  35. {
  36. var services = TestServices.MockPlatformRenderInterface;
  37. if (renderInterface != null)
  38. services = services.With(renderInterface: renderInterface);
  39. _app = UnitTestApplication.Start(services);
  40. try
  41. {
  42. AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(Timer);
  43. Compositor = new Compositor(new RenderLoop(Timer), null);
  44. var impl = new TopLevelImpl(Compositor, size ?? new Size(1000, 1000));
  45. TopLevel = new EmbeddableControlRoot(impl)
  46. {
  47. Template = new FuncControlTemplate((parent, scope) =>
  48. {
  49. var presenter = new ContentPresenter
  50. {
  51. [~ContentPresenter.ContentProperty] = new TemplateBinding(ContentControl.ContentProperty)
  52. };
  53. scope.Register("PART_ContentPresenter", presenter);
  54. return presenter;
  55. })
  56. };
  57. TopLevel.Prepare();
  58. TopLevel.StartRendering();
  59. RunJobs();
  60. Renderer = ((CompositingRenderer)TopLevel.Renderer);
  61. Renderer.CompositionTarget.Server.DebugEvents = Events;
  62. }
  63. catch
  64. {
  65. _app.Dispose();
  66. throw;
  67. }
  68. }
  69. public void RunJobs()
  70. {
  71. Dispatcher.UIThread.RunJobs();
  72. Timer.TriggerTick();
  73. Dispatcher.UIThread.RunJobs();
  74. }
  75. public void AssertRects(params Rect[] rects)
  76. {
  77. RunJobs();
  78. var toAssert = rects.Select(x => x.ToString()).Distinct().OrderBy(x => x);
  79. var invalidated = Events.Rects.Select(x => x.ToString()).Distinct().OrderBy(x => x);
  80. Assert.Equal(toAssert, invalidated);
  81. Events.Rects.Clear();
  82. }
  83. public void AssertHitTest(double x, double y, Func<Visual, bool> filter, params object[] expected)
  84. => AssertHitTest(new Point(x, y), filter, expected);
  85. public void AssertHitTest(Point pt, Func<Visual, bool> filter, params object[] expected)
  86. {
  87. RunJobs();
  88. var tested = Renderer.HitTest(pt, TopLevel, filter);
  89. Assert.Equal(expected, tested);
  90. }
  91. public void AssertHitTestFirst(Point pt, Func<Visual, bool> filter, object expected)
  92. {
  93. RunJobs();
  94. var tested = Renderer.HitTest(pt, TopLevel, filter).First();
  95. Assert.Equal(expected, tested);
  96. }
  97. public class DebugEvents : ICompositionTargetDebugEvents
  98. {
  99. public List<Rect> Rects = new();
  100. public void RectInvalidated(Rect rc)
  101. {
  102. Rects.Add(rc);
  103. }
  104. public void Reset()
  105. {
  106. Rects.Clear();
  107. }
  108. }
  109. public class ManualRenderTimer : IRenderTimer
  110. {
  111. public event Action<TimeSpan> Tick;
  112. public bool RunsInBackground => false;
  113. public void TriggerTick() => Tick?.Invoke(TimeSpan.Zero);
  114. }
  115. class TopLevelImpl : ITopLevelImpl
  116. {
  117. private readonly Compositor _compositor;
  118. public TopLevelImpl(Compositor compositor, Size clientSize)
  119. {
  120. ClientSize = clientSize;
  121. _compositor = compositor;
  122. }
  123. public void Dispose()
  124. {
  125. }
  126. public Size ClientSize { get; }
  127. public Size? FrameSize { get; }
  128. public double RenderScaling => 1;
  129. public IEnumerable<object> Surfaces { get; } = new[] { new DummyFramebufferSurface() };
  130. public Action<RawInputEventArgs> Input { get; set; }
  131. public Action<Rect> Paint { get; set; }
  132. public Action<Size, WindowResizeReason> Resized { get; set; }
  133. public Action<double> ScalingChanged { get; set; }
  134. public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
  135. class DummyFramebufferSurface : IFramebufferPlatformSurface
  136. {
  137. public ILockedFramebuffer Lock()
  138. {
  139. var ptr = Marshal.AllocHGlobal(128);
  140. return new LockedFramebuffer(ptr, new PixelSize(1, 1), 4, new Vector(96, 96),
  141. PixelFormat.Rgba8888, () => Marshal.FreeHGlobal(ptr));
  142. }
  143. }
  144. public Compositor Compositor => _compositor;
  145. public void Invalidate(Rect rect)
  146. {
  147. }
  148. public void SetInputRoot(IInputRoot inputRoot)
  149. {
  150. }
  151. public Point PointToClient(PixelPoint point) => default;
  152. public PixelPoint PointToScreen(Point point) => new();
  153. public void SetCursor(ICursorImpl cursor)
  154. {
  155. }
  156. public Action Closed { get; set; }
  157. public Action LostFocus { get; set; }
  158. public IMouseDevice MouseDevice { get; } = new MouseDevice();
  159. public IPopupImpl CreatePopup() => throw new NotImplementedException();
  160. public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
  161. {
  162. }
  163. public WindowTransparencyLevel TransparencyLevel { get; }
  164. public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
  165. {
  166. }
  167. public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
  168. public object TryGetFeature(Type featureType) => null;
  169. }
  170. }
  171. public class NullCompositorScheduler : ICompositorScheduler
  172. {
  173. public void CommitRequested(Compositor compositor)
  174. {
  175. }
  176. }