Fix smoke 2차 gaps: hotkey tests, focus filter, viewport picking (#13)
Gap E — Hotkey named key - UiaPlayerHost: extract ParsedHotkey record + ParseHotkey static - HotkeyParseTests: 8 tests (enter/tab/a/ctrl+c/ctrl+shift+s/f5/alt+f4/empty) Gap F — recorder focus_change SUT filter - FocusEventFilter.ShouldAccept static rule (same/zero/unknown/unknown-sut) - Program.cs wires it inside RegisterFocusChangedEvent callback - FocusEventFilterTests: 4 tests Gap G — viewport picking foreign-process fallback - IWindowPointSource + WindowPointResolver pure resolver - FlaUiPointSource wired in Program.cs (best-effort hit test, honest partial for live SUT) - WindowPointResolverTests: 5 tests Tests: 77 → 94, build 0/0 (TreatWarningsAsErrors preserved). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -72,10 +72,12 @@ public static class Program
|
||||
Application? app = null;
|
||||
UIA3Automation? automation = null;
|
||||
AutomationElement? mainWindow = null;
|
||||
int sutPid = 0;
|
||||
|
||||
try
|
||||
{
|
||||
(app, automation, mainWindow) = TryAttach(args.Attach);
|
||||
if (app is not null) sutPid = app.ProcessId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -86,7 +88,6 @@ public static class Program
|
||||
// any window not owned by the SUT process.
|
||||
if (app is not null)
|
||||
{
|
||||
int sutPid = app.ProcessId;
|
||||
hook.Filter = new SutProcessWindowFilter(
|
||||
sutPid,
|
||||
processFromPoint: (x, y) =>
|
||||
@@ -131,6 +132,11 @@ public static class Program
|
||||
try
|
||||
{
|
||||
if (el is null) return;
|
||||
// Issue #13 Gap F — drop focus events from non-SUT processes.
|
||||
int elPid = 0;
|
||||
try { elPid = el.Properties.ProcessId.ValueOrDefault; }
|
||||
catch { elPid = 0; }
|
||||
if (!FocusEventFilter.ShouldAccept(elPid, sutPid)) return;
|
||||
var snap = new FlaUiSnapshot(el);
|
||||
var path = ElementPathBuilder.Build(snap);
|
||||
channel.Writer.TryWrite(new RawEvent(
|
||||
@@ -186,7 +192,8 @@ public static class Program
|
||||
}
|
||||
try
|
||||
{
|
||||
var snap = ResolveAt(automation, ev.X, ev.Y);
|
||||
var source = new FlaUiPointSource(automation, mainWindow);
|
||||
var snap = WindowPointResolver.Resolve(source, ev.X, ev.Y, sutPid);
|
||||
if (snap is null)
|
||||
{
|
||||
unresolvedPaths++;
|
||||
@@ -274,11 +281,59 @@ public static class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static IElementSnapshot? ResolveAt(UIA3Automation automation, int x, int y)
|
||||
/// <summary>
|
||||
/// FlaUI/Win32-backed <see cref="IWindowPointSource"/>. The SUT-scope
|
||||
/// fallback is a best-effort stub (returns null) pending live verification
|
||||
/// in smoke 3 — the load-bearing piece of Gap G is the pure
|
||||
/// <see cref="WindowPointResolver"/> rule which falls back to the primary
|
||||
/// result when the fallback returns null.
|
||||
/// </summary>
|
||||
private sealed class FlaUiPointSource : IWindowPointSource
|
||||
{
|
||||
var raw = automation.FromPoint(new System.Drawing.Point(x, y));
|
||||
if (raw is null) return null;
|
||||
return new FlaUiSnapshot(raw);
|
||||
private readonly UIA3Automation _automation;
|
||||
private readonly AutomationElement? _mainWindow;
|
||||
|
||||
public FlaUiPointSource(UIA3Automation automation, AutomationElement? mainWindow)
|
||||
{
|
||||
_automation = automation;
|
||||
_mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
public int? GetProcessIdAt(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
var hwnd = NativeMethods.WindowFromPoint(new NativeMethods.POINT { x = x, y = y });
|
||||
if (hwnd == IntPtr.Zero) return null;
|
||||
NativeMethods.GetWindowThreadProcessId(hwnd, out var pid);
|
||||
return (int)pid;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IElementSnapshot? GetElementAt(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
var raw = _automation.FromPoint(new System.Drawing.Point(x, y));
|
||||
return raw is null ? null : new FlaUiSnapshot(raw);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IElementSnapshot? GetElementFromSutScope(int x, int y)
|
||||
{
|
||||
// Partial Gap G: honest stub. Returning null lets WindowPointResolver
|
||||
// fall back to the primary element as a last resort. Full hit-test
|
||||
// walker to be implemented once smoke 3 validates the surface.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user