TestBase.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System.IO;
  4. using System.Runtime.CompilerServices;
  5. using ImageMagick;
  6. using Avalonia.Controls;
  7. using Avalonia.Media.Imaging;
  8. using Avalonia.Rendering;
  9. using Xunit;
  10. using Avalonia.Platform;
  11. using System.Threading.Tasks;
  12. using System;
  13. using System.Threading;
  14. using Avalonia.Threading;
  15. #if AVALONIA_SKIA
  16. using Avalonia.Skia;
  17. #else
  18. using Avalonia.Direct2D1;
  19. #endif
  20. #if AVALONIA_SKIA
  21. namespace Avalonia.Skia.RenderTests
  22. #else
  23. namespace Avalonia.Direct2D1.RenderTests
  24. #endif
  25. {
  26. public class TestBase
  27. {
  28. private static readonly TestThreadingInterface threadingInterface =
  29. new TestThreadingInterface();
  30. static TestBase()
  31. {
  32. #if AVALONIA_SKIA
  33. SkiaPlatform.Initialize();
  34. #else
  35. Direct2D1Platform.Initialize();
  36. #endif
  37. AvaloniaLocator.CurrentMutable
  38. .Bind<IPlatformThreadingInterface>()
  39. .ToConstant(threadingInterface);
  40. }
  41. public TestBase(string outputPath)
  42. {
  43. #if AVALONIA_SKIA
  44. string testFiles = Path.GetFullPath(@"..\..\..\..\..\TestFiles\Skia");
  45. #else
  46. string testFiles = Path.GetFullPath(@"..\..\tests\TestFiles\Direct2D1");
  47. #endif
  48. OutputPath = Path.Combine(testFiles, outputPath);
  49. threadingInterface.MainThread = Thread.CurrentThread;
  50. }
  51. public string OutputPath
  52. {
  53. get;
  54. }
  55. protected async Task RenderToFile(Control target, [CallerMemberName] string testName = "")
  56. {
  57. if (!Directory.Exists(OutputPath))
  58. {
  59. Directory.CreateDirectory(OutputPath);
  60. }
  61. var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
  62. var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
  63. var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
  64. using (RenderTargetBitmap bitmap = new RenderTargetBitmap(
  65. (int)target.Width,
  66. (int)target.Height))
  67. {
  68. Size size = new Size(target.Width, target.Height);
  69. target.Measure(size);
  70. target.Arrange(new Rect(size));
  71. bitmap.Render(target);
  72. bitmap.Save(immediatePath);
  73. }
  74. using (var rtb = factory.CreateRenderTargetBitmap((int)target.Width, (int)target.Height, 96, 96))
  75. using (var renderer = new DeferredRenderer(target, rtb))
  76. {
  77. Size size = new Size(target.Width, target.Height);
  78. target.Measure(size);
  79. target.Arrange(new Rect(size));
  80. renderer.UnitTestUpdateScene();
  81. // Do the deferred render on a background thread to expose any threading errors in
  82. // the deferred rendering path.
  83. await Task.Run((Action)renderer.UnitTestRender);
  84. rtb.Save(deferredPath);
  85. }
  86. }
  87. protected void CompareImages([CallerMemberName] string testName = "")
  88. {
  89. var expectedPath = Path.Combine(OutputPath, testName + ".expected.png");
  90. var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
  91. var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
  92. using (var expected = new MagickImage(expectedPath))
  93. using (var immediate = new MagickImage(immediatePath))
  94. using (var deferred = new MagickImage(deferredPath))
  95. {
  96. double immediateError = expected.Compare(immediate, ErrorMetric.RootMeanSquared);
  97. double deferredError = expected.Compare(deferred, ErrorMetric.RootMeanSquared);
  98. if (immediateError > 0.022)
  99. {
  100. Assert.True(false, immediatePath + ": Error = " + immediateError);
  101. }
  102. if (deferredError > 0.022)
  103. {
  104. Assert.True(false, deferredPath + ": Error = " + deferredError);
  105. }
  106. }
  107. }
  108. protected void CompareImagesNoRenderer([CallerMemberName] string testName = "")
  109. {
  110. var expectedPath = Path.Combine(OutputPath, testName + ".expected.png");
  111. var actualPath = Path.Combine(OutputPath, testName + ".out.png");
  112. using (var expected = new MagickImage(expectedPath))
  113. using (var actual = new MagickImage(actualPath))
  114. {
  115. double immediateError = expected.Compare(actual, ErrorMetric.RootMeanSquared);
  116. if (immediateError > 0.022)
  117. {
  118. Assert.True(false, actualPath + ": Error = " + immediateError);
  119. }
  120. }
  121. }
  122. private class TestThreadingInterface : IPlatformThreadingInterface
  123. {
  124. public bool CurrentThreadIsLoopThread => MainThread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;
  125. public Thread MainThread { get; set; }
  126. public event Action<DispatcherPriority?> Signaled;
  127. public void RunLoop(CancellationToken cancellationToken)
  128. {
  129. throw new NotImplementedException();
  130. }
  131. public void Signal(DispatcherPriority prio)
  132. {
  133. throw new NotImplementedException();
  134. }
  135. public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
  136. {
  137. throw new NotImplementedException();
  138. }
  139. }
  140. }
  141. }