Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image assert #336

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
21 changes: 20 additions & 1 deletion src/FlaUI.Core/Capturing/Capture.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Exceptions;
using FlaUI.Core.WindowsAPI;
using FlaUI.Core.Definitions;
using System;
using System.Collections.Generic;
using System.Drawing;
Expand Down Expand Up @@ -80,7 +81,25 @@ public static CaptureImage ScreensWithElement(AutomationElement element, Capture
/// </summary>
public static CaptureImage Element(AutomationElement element, CaptureSettings settings = null)
{
return Rectangle(element.BoundingRectangle, settings);
Rectangle rectangle = element.BoundingRectangle;
bool isWindows10OrLater = (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2) ||
Environment.OSVersion.Version.Major >= 7;

if (isWindows10OrLater && element.ControlType == ControlType.Window &&
element.Properties.NativeWindowHandle.IsSupported &&
Win32Fallback.IsTopLevelWindow(element.Properties.NativeWindowHandle.ValueOrDefault))
{
// For top-level windows in Windows 10 there is an extra border around any window.
// The border is transparent and has 8 pixels width to left, 8 pixels to right, 8 pixels height on bottom and 1 pixel on top.
// The UIA bounding rectangle is larger then the window itself and capturing using the bounding rectangle as it is will also get a part of what is under our window.
// To capture only the window we need to shrink the rectangle a little bit.
rectangle.X += 8;
rectangle.Y += 1;
rectangle.Width -= 16;
rectangle.Height -= 9;
}

return Rectangle(rectangle, settings);
}

/// <summary>
Expand Down
84 changes: 84 additions & 0 deletions src/FlaUI.Core/Capturing/ImageAssert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Drawing;
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Definitions;

namespace FlaUI.Core.Capturing
{
/// <summary>
/// Provides methods to compare element screenshot with an image saved in a file.
/// </summary>
public static class ImageAssert
{
/// <summary>
/// Compares the automation element with the image from specified file path.
/// If the snapshot of the automation element differs from the image in the file then an exception is thrown.
/// When you create the image file specified as the parameter (using FlaUInspect tool), make sure the element you are taking a snapshot of is entirely visible on the screen and it is not (partially or completely) overlapped by another window.
/// Also, when calling this method make sure the element is entirely visible on screen.
/// </summary>
/// <param name="element">The automation element being compared.</param>
/// <param name="filePath">The path of file containing the image.</param>
public static void AreEqual(AutomationElement element, string filePath)
{
var progManager = element.Automation.GetDesktop().FindFirstChild(cf => cf.ByControlType(ControlType.Pane).And(cf.ByName("Program Manager")));
if (progManager != null)
{
var desktopList = progManager.FindFirstChild(cf => cf.ByControlType(ControlType.List).And(cf.ByName("Desktop")));
if (desktopList != null)
{
// Set focus on desktop so the tested application will lose the focus.
// We want to capture the element without focus.
// Controls and windows look different when they have the focus (on edit control a caret may appear, text may be selected and so on).
desktopList.FrameworkAutomationElement.SetFocus();
System.Threading.Thread.Sleep(300);
}
}

Bitmap crtBitmap = element.Capture();

// restore focus on the automation element
element.FrameworkAutomationElement.SetFocus();

Bitmap bitmapFromFile = null;
try
{
bitmapFromFile = new Bitmap(filePath);
}
catch (Exception ex)
{
crtBitmap.Dispose();
throw ex;
}

//compare...
if (crtBitmap.Height != bitmapFromFile.Height || crtBitmap.Width != bitmapFromFile.Width)
{
throw new Exception($"Images have different sizes. FilePath: \"{filePath}\"");
}

bool identic = true;
for (int i = 0; i < crtBitmap.Width; i++)
{
for (int j = 0; j < crtBitmap.Height; j++)
{
Color pixel1 = crtBitmap.GetPixel(i, j);
Color pixel2 = bitmapFromFile.GetPixel(i, j);

if (pixel1.ToArgb() != pixel2.ToArgb())
{
identic = false;
break;
}
}
}

crtBitmap.Dispose();
bitmapFromFile.Dispose();

if (identic == false)
{
throw new Exception($"Images differ. FilePath: \"{filePath}\"");
}
}
}
}
10 changes: 10 additions & 0 deletions src/FlaUI.Core/WindowsAPI/Win32Fallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -622,5 +622,15 @@ internal static void SetDTPSelectedDate(IntPtr handle, DateTime? date)
User32.VirtualFreeEx(hProcess, hMem, Marshal.SizeOf(systemtime), AllocationType.Decommit | AllocationType.Release);
User32.CloseHandle(hProcess);
}

internal static bool IsTopLevelWindow(IntPtr hWnd)
{
uint style = User32.GetWindowLong(hWnd, WindowLongParam.GWL_STYLE);
if (style == 0)
{
return false;
}
return ((style & WindowStyles.WS_CHILD) == 0);
}
}
}