Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
namespace Devolutions.AvaloniaControls.VisualTests;

using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Headless;
using Avalonia.Headless.XUnit;
using Avalonia.Input;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
using SampleApp;
Expand All @@ -21,6 +25,8 @@ public class VisualRegressionTests
private static readonly string BaselinesDirectory = $"../../../Screenshots/Baseline/{GetCurrentOS()}";
private static readonly string TestDiffsDirectory = $"../../../Screenshots/Test-Diffs/{DateTime.Now:yyyy-MM-dd__HH-mm}";
private static readonly string[] SupportedThemes = ["MacClassic", "LiquidGlass", "Linux", "DevExpress"];
private static readonly TimeSpan CaptureStabilizationTimeout = TimeSpan.FromMilliseconds(250);
private static readonly TimeSpan CaptureStabilizationInterval = TimeSpan.FromMilliseconds(16);

private static Dictionary<string, List<string>>? _pageThemes;
private static Dictionary<string, string>? _pageViewModels;
Expand Down Expand Up @@ -304,10 +310,7 @@ private static void CaptureAndCompare(Window window, string pageName, string the
// Wait for layout and theme application
Dispatcher.UIThread.RunJobs();

// Capture
WriteableBitmap? frame = window.CaptureRenderedFrame();
if (frame == null) throw new Exception($"Failed to capture frame for {variant}");
using WriteableBitmap bitmap = frame;
using WriteableBitmap bitmap = CaptureStableFrame(window, variant);

// Save and Compare
var fileName = $"{pageName}{suffix}.png";
Expand Down Expand Up @@ -339,6 +342,53 @@ private static void CaptureAndCompare(Window window, string pageName, string the
Assert.Fail($"No baseline found for [{themeName}] {pageName} - {variant}. Saved screenshot to {testPath}");
}
}

private static WriteableBitmap CaptureStableFrame(Window window, ThemeVariant variant)
{
WriteableBitmap? previousFrame = window.CaptureRenderedFrame();
if (previousFrame == null)
{
throw new Exception($"Failed to capture frame for {variant}");
}

byte[] previousPixels = CopyFramePixels(previousFrame);
var stabilizationTimer = Stopwatch.StartNew();

while (stabilizationTimer.Elapsed < CaptureStabilizationTimeout)
{
Thread.Sleep(CaptureStabilizationInterval);
Dispatcher.UIThread.RunJobs();

WriteableBitmap? currentFrame = window.CaptureRenderedFrame();
if (currentFrame == null)
{
previousFrame.Dispose();
throw new Exception($"Failed to capture frame for {variant}");
}

byte[] currentPixels = CopyFramePixels(currentFrame);
if (previousPixels.AsSpan().SequenceEqual(currentPixels))
{
previousFrame.Dispose();
return currentFrame;
}

previousFrame.Dispose();
previousFrame = currentFrame;
previousPixels = currentPixels;
}

return previousFrame;
}

private static byte[] CopyFramePixels(WriteableBitmap frame)
{
using ILockedFramebuffer lockedFramebuffer = frame.Lock();
int byteCount = lockedFramebuffer.RowBytes * lockedFramebuffer.Size.Height;
var buffer = new byte[byteCount];
Marshal.Copy(lockedFramebuffer.Address, buffer, 0, byteCount);
return buffer;
}
}

internal static class TestInitializer
Expand Down