diff --git a/scripts/test_windows_input.ps1 b/scripts/test_windows_input.ps1 index 3c3a6e54..6ee042aa 100644 --- a/scripts/test_windows_input.ps1 +++ b/scripts/test_windows_input.ps1 @@ -333,6 +333,15 @@ try { if ($case.kind -eq 'mode-transitions') { $null = Observer-Request $plan 'set-mode' $mode } $row.capture_id = $end.id $row.hex = $end.hex; $row.records = $end.records; $row.error = $end.error; $row.complete = $end.quiet_reached + if ($case.kind -eq 'clipboard-image' -and $path -eq 'herdr-remote') { + $capture = [Convert]::FromHexString([string]$end.hex) + if ($capture.Length -ge 12) { + $stagedPath = [Text.Encoding]::UTF8.GetString($capture, 6, $capture.Length - 12) + if (Test-Path -LiteralPath $stagedPath -PathType Leaf) { + $row.staged_image_sha256 = (Get-FileHash -LiteralPath $stagedPath -Algorithm SHA256).Hash + } + } + } $row.final_outer_geometry = (Outer-State $plan).geometry $row.status = 'observed'; $row.final_pane_geometry = $end.geometry if ($path -in @('herdr', 'herdr-remote') -and (Test-Path -LiteralPath $plan.input_trace)) { diff --git a/scripts/test_windows_input.py b/scripts/test_windows_input.py index 4e111cbb..38868843 100644 --- a/scripts/test_windows_input.py +++ b/scripts/test_windows_input.py @@ -94,7 +94,10 @@ class WindowsInputGauntletTests(unittest.TestCase): empty_paste = b"\x1b[200~\x1b[201~" staged = b"\x1b[200~C:\\Temp\\herdr-clipboard-images-user\\image.png\x1b[201~" self.assertEqual(verdict(case, "legacy", {**self.evidence, "path": "direct", "hex": empty_paste.hex()})[0], "pass") - self.assertEqual(verdict(case, "legacy", {**self.evidence, "path": "herdr-remote", "hex": staged.hex()})[0], "pass") + remote = {**self.evidence, "path": "herdr-remote", "hex": staged.hex(), + "staged_image_sha256": case["expected"]["legacy"]["sha256"]} + self.assertEqual(verdict(case, "legacy", remote)[0], "pass") + self.assertEqual(verdict(case, "legacy", {**remote, "staged_image_sha256": "0" * 64})[0], "fail") self.assertEqual(verdict(case, "legacy", {**self.evidence, "path": "herdr-remote", "hex": empty_paste.hex()})[0], "fail") self.assertEqual(verdict(case, "legacy", {**self.evidence, "path": "herdr", "hex": empty_paste.hex()})[0], "not_run") diff --git a/scripts/windows_input/Native.cs b/scripts/windows_input/Native.cs index 735d41a4..decbc58b 100644 --- a/scripts/windows_input/Native.cs +++ b/scripts/windows_input/Native.cs @@ -165,6 +165,7 @@ namespace HerdrInputGauntlet { } finally { CloseClipboard(); } } public static uint SetEmptyClipboardImage(IntPtr owner,string text) { + if(owner==IntPtr.Zero) throw new Exception("Clipboard owner is required"); if(!OpenClipboard(owner)) throw new Exception("Clipboard busy; refusing replacement"); IntPtr pngMemory=IntPtr.Zero,textMemory=IntPtr.Zero; bool complete=false; diff --git a/scripts/windows_input/report.py b/scripts/windows_input/report.py index 8f077cc7..d6c20803 100644 --- a/scripts/windows_input/report.py +++ b/scripts/windows_input/report.py @@ -79,7 +79,9 @@ def catalogue(): ]: cases.append(dict(id=name, kind="paste", text=text, expected={mode: {"paste": text} for mode in MODES[1:]})) cases.append(dict(id="clipboard-image", kind="clipboard-image", - expected={mode: {"clipboard_image": True} for mode in MODES[1:]})) + expected={mode: {"clipboard_image": True, + "sha256": "4BA8D4FD5AB42544FEEFF22D50E84412502433CC95952FD1DF9A5293588DDBEA"} + for mode in MODES[1:]})) cases.append(dict(id="clipboard-mixed", kind="clipboard-mixed", text="clipboard text wins", expected={mode: {"paste": "clipboard text wins"} for mode in MODES[1:]})) cases.append(dict(id="mouse-interleave", kind="mouse-interleave", text="mouse\npaste", @@ -223,8 +225,11 @@ def verdict(case, mode, evidence): except UnicodeDecodeError: return "fail", "Staged clipboard image path is not UTF-8" valid_path = re.fullmatch(r"[A-Za-z]:\\.*\\herdr-clipboard-images-[^\\]+\\[^\\]+\.png", path) - return (("pass", "Image-only clipboard was staged and its path reached the pane") if valid_path else - ("fail", "Pane did not receive a staged clipboard PNG path")) + if not valid_path: + return "fail", "Pane did not receive a staged clipboard PNG path" + return (("pass", "Exact clipboard PNG was staged and its path reached the pane") + if evidence.get("staged_image_sha256") == expected["sha256"] else + ("fail", "Staged clipboard image contents differ from the fixture")) if expected.get("mouse_interleave"): motion = rb"(?:\x1b\[<35;\d+;\d+M)+" newline = rb"(?:\r\n|\r|\n)"