A Debian image (python:3.13-slim-bookworm with Xvfb and GNOME's window manager) runs the linters, the whole test suite, the screenshots and the smoke test of a PyInstaller Linux binary and of the source program, so no window opens on the development machine. The screenshot tool works on Linux, a smoke driver presses the known button with the virtual mouse, and Windows Sandbox scripts are written (not run, they open a window on the host). README and the wiki Install pages describe it. Review records RC-077 (Python) and RC-078 (shell). Task: MIL-013#2 Task: MIL-013#3 Task: MIL-013#4 Task: MIL-013#5 Task: MIL-013#6 Task: MIL-013#7 Task: MIL-013#9 Task: MIL-013#10
133 lines
5.7 KiB
PowerShell
133 lines
5.7 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Smoke test of flash-card.exe, run INSIDE Windows Sandbox (ADR-0005).
|
|
|
|
.DESCRIPTION
|
|
Starts the executable on the sandbox's own desktop, presses the known button
|
|
with the sandbox's own mouse, reads the progress file, takes screenshots and
|
|
writes result.json to C:\fc\out. Then it shuts the sandbox down.
|
|
It never touches the host: the host sees only the mapped folders.
|
|
#>
|
|
param(
|
|
[int]$Runs = 5,
|
|
[string]$Exe = 'C:\fc\dist\flash-card.exe',
|
|
[string]$Out = 'C:\fc\out'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Add-Type -AssemblyName System.Drawing
|
|
Add-Type @'
|
|
using System;
|
|
using System.Text;
|
|
using System.Runtime.InteropServices;
|
|
public static class Win {
|
|
public delegate bool EnumProc(IntPtr h, IntPtr l);
|
|
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumProc p, IntPtr l);
|
|
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetWindowText(IntPtr h, StringBuilder s, int n);
|
|
[DllImport("user32.dll")] public static extern bool GetClientRect(IntPtr h, out RECT r);
|
|
[DllImport("user32.dll")] public static extern bool ClientToScreen(IntPtr h, ref POINT p);
|
|
[DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y);
|
|
[DllImport("user32.dll")] public static extern void mouse_event(uint f, uint x, uint y, uint d, UIntPtr e);
|
|
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
|
|
[DllImport("shcore.dll")] public static extern int SetProcessDpiAwareness(int v);
|
|
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
|
|
[StructLayout(LayoutKind.Sequential)] public struct POINT { public int X, Y; }
|
|
public static IntPtr Find(string title) {
|
|
IntPtr found = IntPtr.Zero;
|
|
EnumWindows(delegate (IntPtr h, IntPtr l) {
|
|
if (IsWindowVisible(h)) {
|
|
var sb = new StringBuilder(256);
|
|
GetWindowText(h, sb, 256);
|
|
if (sb.ToString() == title) { found = h; return false; }
|
|
}
|
|
return true;
|
|
}, IntPtr.Zero);
|
|
return found;
|
|
}
|
|
}
|
|
'@
|
|
[void][Win]::SetProcessDpiAwareness(2)
|
|
|
|
function Wait-Window([int]$Seconds) {
|
|
$end = (Get-Date).AddSeconds($Seconds)
|
|
while ((Get-Date) -lt $end) {
|
|
$h = [Win]::Find('Flashy')
|
|
if ($h -ne [IntPtr]::Zero) { return $h }
|
|
Start-Sleep -Milliseconds 250
|
|
}
|
|
return [IntPtr]::Zero
|
|
}
|
|
|
|
function Save-Screen([string]$Path) {
|
|
$b = [System.Windows.Forms.SystemInformation]::VirtualScreen
|
|
$bmp = New-Object System.Drawing.Bitmap $b.Width, $b.Height
|
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
$g.CopyFromScreen($b.Left, $b.Top, 0, 0, $bmp.Size)
|
|
$bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
$g.Dispose(); $bmp.Dispose()
|
|
}
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
|
|
$work = Join-Path $env:TEMP 'fc-smoke'
|
|
$results = @()
|
|
for ($n = 1; $n -le $Runs; $n++) {
|
|
$run = [ordered]@{ run = $n; window = $false; clicked = $false; progress_rows = $null; ok = $false; seconds = 0 }
|
|
$t0 = Get-Date
|
|
$dir = Join-Path $work "run$n"
|
|
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
|
|
New-Item -ItemType Directory -Path (Join-Path $dir 'data') -Force | Out-Null
|
|
Copy-Item $Exe (Join-Path $dir 'flash-card.exe')
|
|
# three words: after one known word the progress file has a header and two cards
|
|
[IO.File]::WriteAllText((Join-Path $dir 'data\ms-da.csv'), "ms;da`naaa;bbb`nccc;ddd`neee;fff`n", (New-Object Text.UTF8Encoding $false))
|
|
$p = Start-Process -FilePath (Join-Path $dir 'flash-card.exe') -ArgumentList '--level', 'all', '--no-speech' -WorkingDirectory $dir -PassThru
|
|
try {
|
|
$h = Wait-Window 60
|
|
if ($h -ne [IntPtr]::Zero) {
|
|
$run.window = $true
|
|
Start-Sleep -Milliseconds 1500
|
|
if ($n -eq 1) { Save-Screen (Join-Path $Out 'sandbox-card.png') }
|
|
$r = New-Object Win+RECT
|
|
[void][Win]::GetClientRect($h, [ref]$r)
|
|
$pt = New-Object Win+POINT
|
|
[void][Win]::ClientToScreen($h, [ref]$pt)
|
|
$x = $pt.X + 650
|
|
$y = $pt.Y + $r.Bottom - 101
|
|
[void][Win]::SetForegroundWindow($h)
|
|
Start-Sleep -Milliseconds 400
|
|
[void][Win]::SetCursorPos($x - 12, $y - 12)
|
|
Start-Sleep -Milliseconds 300
|
|
[void][Win]::SetCursorPos($x, $y)
|
|
Start-Sleep -Milliseconds 400
|
|
[Win]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero)
|
|
Start-Sleep -Milliseconds 120
|
|
[Win]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero)
|
|
$run.clicked = $true
|
|
$prog = Join-Path $dir 'data\words_to_learn.csv'
|
|
$end = (Get-Date).AddSeconds(6)
|
|
while ((Get-Date) -lt $end -and -not (Test-Path $prog)) { Start-Sleep -Milliseconds 250 }
|
|
if (Test-Path $prog) {
|
|
$rows = ([IO.File]::ReadAllLines($prog) | Where-Object { $_.Trim() -ne '' }).Count
|
|
$run.progress_rows = $rows
|
|
$run.ok = ($rows -eq 3)
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
if (-not $p.HasExited) { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue }
|
|
Get-Process -Name 'flash-card' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
}
|
|
$run.seconds = [math]::Round(((Get-Date) - $t0).TotalSeconds, 1)
|
|
$results += [pscustomobject]$run
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
|
|
$summary = [ordered]@{
|
|
runs = $Runs
|
|
passed = ($results | Where-Object { $_.ok }).Count
|
|
os = (Get-CimInstance Win32_OperatingSystem).Caption
|
|
results = $results
|
|
}
|
|
$summary | ConvertTo-Json -Depth 5 | Set-Content -Path (Join-Path $Out 'result.json') -Encoding UTF8
|
|
shutdown.exe /s /t 5
|