repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
pi/agent/skillz/win32-drive/scripts/win32-drive.test.ps1
Raw# -IncludeTheme also exercises theme writes. Those broadcast WM_SETTINGCHANGE to
# every window on the desktop and have been observed to wedge apps that react to
# it, so they stay opt-in.
param([switch] $IncludeTheme)
$ErrorActionPreference = 'Stop'
$script = Join-Path $PSScriptRoot 'win32-drive.ps1'
if (-not (Test-Path $script)) { throw "Missing script: $script" }
Add-Type -AssemblyName System.Drawing
function Invoke-Raw($arguments, [switch] $AllowFail) {
$raw = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $script @arguments
$code = $LASTEXITCODE
if (-not $AllowFail -and $code -ne 0) { throw "Command failed: $($arguments -join ' ')`n$raw" }
[pscustomobject]@{ Code = $code; Json = ($raw | ConvertFrom-Json); Raw = $raw }
}
function Invoke-Json($arguments) { (Invoke-Raw $arguments).Json }
function Assert($condition, $message) { if (-not $condition) { throw $message } }
function Assert-File($path) { Assert (Test-Path $path) "missing file: $path" }
function Assert-Err($arguments, $pattern) {
$r = Invoke-Raw $arguments -AllowFail
Assert ($r.Code -ne 0) "expected failure: $($arguments -join ' ')"
Assert ($r.Json.ok -eq $false) 'failure JSON must have ok=false'
Assert ($r.Json.error -match $pattern) "error '$($r.Json.error)' does not match '$pattern'"
}
function Wait-Title($hwnd, [string] $Expected, [int] $TimeoutMs = 4000) {
$deadline = (Get-Date).AddMilliseconds($TimeoutMs)
do {
$win = @(Invoke-Json @('list')).windows | Where-Object hwnd -eq $hwnd | Select-Object -First 1
if ($win -and $win.title -eq $Expected) { return $win.title }
Start-Sleep -Milliseconds 120
} while ((Get-Date) -lt $deadline)
throw "window title never became '$Expected' (last: '$($win.title)')"
}
function Start-TestWindow($title) {
$app = Join-Path $env:TEMP ("win32-drive-app-" + [guid]::NewGuid().ToString('N') + '.ps1')
@"
`$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
[Windows.Forms.Application]::EnableVisualStyles()
`$form = New-Object Windows.Forms.Form
`$form.Text = '$title'
`$form.StartPosition = 'Manual'
`$form.SetBounds(80,80,420,260)
`$form.BackColor = [Drawing.Color]::FromArgb(0,120,215)
`$box = New-Object Windows.Forms.TextBox
`$box.Multiline = `$true
`$box.Name = 'InputBox'
`$box.SetBounds(20,20,360,140)
`$button = New-Object Windows.Forms.Button
`$button.Name = 'ClickMe'
`$button.Text = 'ClickMe'
`$button.SetBounds(20,175,100,32)
`$button.Add_Click({ `$form.Text = '$title clicked' })
`$box.Add_TextChanged({ `$form.Text = '$title text:' + `$box.Text })
`$form.Controls.Add(`$box)
`$form.Controls.Add(`$button)
`$form.Add_Shown({ `$box.Focus() })
[Windows.Forms.Application]::Run(`$form)
"@ | Set-Content -Encoding UTF8 $app
$p = Start-Process powershell.exe -ArgumentList @('-STA','-NoProfile','-ExecutionPolicy','Bypass','-File',$app) -PassThru
$deadline = (Get-Date).AddSeconds(8)
do {
Start-Sleep -Milliseconds 150
$wins = @(Invoke-Json @('list')).windows | Where-Object { $_.pid -eq $p.Id -and $_.title -like "$title*" }
} while ($wins.Count -eq 0 -and (Get-Date) -lt $deadline)
if ($wins.Count -eq 0) { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue; throw 'test window did not appear' }
Start-Sleep -Milliseconds 500
[pscustomobject]@{ Process = $p; Script = $app; Window = $wins[0]; Title = $title }
}
$created = @()
$originalClipboard = $null
try {
try { $originalClipboard = (Invoke-Json @('clipboard')).text } catch { $originalClipboard = $null }
$help = Invoke-Json @('help')
foreach ($cmd in 'help','list','shot','pixel','uia','uia-invoke','uia-set','cursor','focus','idle','clipboard','overlay','click','type','key','window','theme') {
Assert ($help.commands.PSObject.Properties.Name -contains $cmd) "help missing $cmd"
}
foreach ($cmd in 'click','type','key','window','theme','uia-invoke','uia-set') { Assert ($help.commands.$cmd.racing -eq $true) "$cmd must be racing" }
foreach ($cmd in 'list','shot','pixel','uia','cursor','focus','idle','overlay') { Assert ($help.commands.$cmd.racing -eq $false) "$cmd must be nonracing" }
Assert ($help.dpiAwareness -in @('per-monitor-v2','per-monitor','system','unaware')) 'help must report dpi awareness'
Assert ($help.dpiAwareness -ne 'unaware') 'process must be dpi aware'
Assert ($help.nativeLoad -in @('cache','compiled','loaded')) 'help must report how the native assembly loaded'
Assert ($help.cmd -eq 'help' -and $help.elapsedMs -ge 0) 'every result needs cmd and elapsedMs'
# batch: one process start, ordered steps, per-step envelopes.
# Steps go through a file because shells mangle embedded quotes.
function Invoke-Batch([string] $StepsJson, [switch] $AllowFail, [string[]] $Extra = @()) {
$path = Join-Path $env:TEMP ("win32-drive-test-batch-" + [guid]::NewGuid().ToString('N') + '.json')
Set-Content -LiteralPath $path -Value $StepsJson -Encoding UTF8
try { (Invoke-Raw (@('batch','-File',$path) + $Extra) -AllowFail:$AllowFail) } finally { Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue }
}
$batch = (Invoke-Batch '[{"cmd":"focus"},{"cmd":"wait","ms":50},{"cmd":"idle"}]').Json
Assert ($batch.ok -and $batch.steps -eq 3 -and $batch.ran -eq 3) 'batch must run every step'
Assert ($batch.results[0].cmd -eq 'focus' -and $batch.results[0].step -eq 0) 'batch results need cmd and step'
Assert ($batch.results[1].waitedMs -eq 50) 'batch wait step failed'
Assert ($null -eq $batch.failedAt) 'successful batch must not report failedAt'
$batchFail = Invoke-Batch '[{"cmd":"idle"},{"cmd":"nonsense"},{"cmd":"idle"}]' -AllowFail
Assert ($batchFail.Json.ok -eq $false -and $batchFail.Json.failedAt -eq 1 -and $batchFail.Json.ran -eq 2) 'batch must stop at the failing step'
Assert ($batchFail.Json.results[1].code -eq 'unknown-command') 'batch failures must carry the error code'
$batchContinue = Invoke-Batch '[{"cmd":"nonsense"},{"cmd":"idle"}]' -AllowFail -Extra @('-ContinueOnError')
Assert ($batchContinue.Json.ran -eq 2) 'batch -ContinueOnError must keep going'
$batchBogus = Invoke-Batch '[{"cmd":"idle","Bogus":1}]' -AllowFail
Assert ($batchBogus.Json.code -eq 'unknown-option' -and $batchBogus.Json.error -match 'step 0') 'batch must reject unknown step options'
# structured errors carry codes, hints, and context
$noWindow = (Invoke-Raw @('list','-Hwnd','1') -AllowFail).Json
Assert ($noWindow.count -eq 0) 'list with an unmatched selector returns no windows'
$stale = (Invoke-Raw @('window','-Hwnd','999999999','-Action','min','-IdleMs','0') -AllowFail).Json
Assert ($stale.code -eq 'stale-window' -and $stale.hint) 'stale handles need code stale-window plus a hint'
$missing = (Invoke-Raw @('shot','-TitleContains',('no-window-' + [guid]::NewGuid().ToString('N'))) -AllowFail).Json
Assert ($missing.code -eq 'no-window' -and $missing.candidates.Count -gt 0) 'no-window must list candidates'
$consent = (Invoke-Raw @('click','-X','10','-Y','10','-IdleMs','0') -AllowFail).Json
Assert ($consent.code -eq 'pointer-consent') 'click without -Pointer must fail with pointer-consent'
$list = Invoke-Json @('list')
Assert ($list.windows.Count -gt 0) 'list should return visible windows'
Assert ($list.count -eq $list.windows.Count -and $null -ne $list.foreground -and $null -ne $list.idleMs) 'list must report count, foreground, and idle time'
Assert (($list.windows | Where-Object { $_.focused }).Count -le 1) 'at most one window can be focused'
Assert ($list.windows[0].z -eq 0) 'list must report z order'
$idle = Invoke-Json @('idle')
Assert ($idle.idleMs -is [int] -or $idle.idleMs -is [long]) 'idleMs must be numeric'
$cursor = Invoke-Json @('cursor')
Assert ($cursor.x -is [int] -and $cursor.y -is [int]) 'cursor must have x/y ints'
Assert ((Invoke-Json @('overlay','-DurationMs','1')).ok) 'overlay failed'
Assert ((Invoke-Json @('overlay','-X','10','-Y','10','-DurationMs','1')).ok) 'overlay target failed'
$px = Invoke-Json @('pixel','-X','0','-Y','0')
Assert ($px.hex -match '^#[0-9A-F]{6}$' -and $px.r -ge 0 -and $px.r -le 255 -and $px.g -ge 0 -and $px.g -le 255 -and $px.b -ge 0 -and $px.b -le 255) 'pixel failed'
$shotPath = Join-Path $env:TEMP ("win32-drive-test-region-" + [guid]::NewGuid().ToString('N') + '.png')
$created += $shotPath
$shot = Invoke-Json @('shot','-X','0','-Y','0','-Width','8','-Height','8','-Out',$shotPath)
Assert-File $shot.path
Assert ($shot.width -eq 8 -and $shot.height -eq 8 -and $shot.mode -eq 'region') 'region shot metadata wrong'
$app = Start-TestWindow ("Win32DriveTest-" + [guid]::NewGuid().ToString('N'))
$created += $app.Script
$hwnd = [string]$app.Window.hwnd
$winShotPath = Join-Path $env:TEMP ("win32-drive-test-window-" + [guid]::NewGuid().ToString('N') + '.png')
$created += $winShotPath
$winShot = Invoke-Json @('shot','-Hwnd',$hwnd,'-Out',$winShotPath)
Assert-File $winShot.path
Assert ($winShot.hwnd -eq $app.Window.hwnd -and $winShot.width -gt 0 -and $winShot.height -gt 0) 'window shot metadata wrong'
Assert ($winShot.width -eq $app.Window.width -and $winShot.height -eq $app.Window.height) 'window shot must match the frame bounds reported by list'
Assert ((Invoke-Json @('uia','-Depth','0')).ok) 'focused uia failed'
$uia = Invoke-Json @('uia','-Hwnd',$hwnd,'-Depth','1')
Assert ($uia.tree.name -like 'Win32DriveTest-*') 'uia root title wrong'
Assert ($uia.tree.children.Count -gt 0) 'uia children missing'
Assert ($null -eq $uia.tree.error) 'uia root must not carry an error'
Assert ((Invoke-Json @('uia','-Hwnd',$hwnd,'-Depth','0')).tree.truncated -eq $true) 'uia must flag truncated subtrees'
# Capture origin must be the frame origin reported by list/shot: a control's
# screen rect minus that origin must hit the same control inside the image.
# This catches the PrintWindow shadow offset without depending on z-order.
$edge = $uia.tree.children | Where-Object { $_.name -ne 'ClickMe' -and $_.width -gt 200 } | Select-Object -First 1
Assert ($null -ne $edge) 'uia did not expose the text box'
$geometryShotPath = Join-Path $env:TEMP ("win32-drive-test-geometry-" + [guid]::NewGuid().ToString('N') + '.png')
$created += $geometryShotPath
$geometryShot = Invoke-Json @('shot','-Hwnd',$hwnd,'-Out',$geometryShotPath)
$bitmap = [Drawing.Bitmap]::FromFile($geometryShot.path)
try {
$inside = $bitmap.GetPixel([int]($edge.x - $geometryShot.x + 8), [int]($edge.y - $geometryShot.y + 8))
$outside = $bitmap.GetPixel([int]($edge.x - $geometryShot.x - 4), [int]($edge.y - $geometryShot.y - 4))
} finally { $bitmap.Dispose() }
Assert ($inside.R -gt 200 -and $inside.G -gt 200 -and $inside.B -gt 200) "capture offset: inside text box is $($inside.R),$($inside.G),$($inside.B), expected white (mode=$($geometryShot.mode) shot=$($geometryShot.x),$($geometryShot.y) $($geometryShot.width)x$($geometryShot.height) edge=$($edge.x),$($edge.y) $($edge.width)x$($edge.height) file=$($geometryShot.path))"
Assert ($outside.B -gt $outside.R) "capture offset: outside text box is $($outside.R),$($outside.G),$($outside.B), expected the blue form background"
# Accessibility actions must change the app without touching focus or pointer.
$foregroundBefore = Invoke-Json @('focus')
$invoked = Invoke-Json @('uia-invoke','-Hwnd',$hwnd,'-Name','ClickMe','-IdleMs','0','-TimeoutMs','1000')
Assert ($invoked.pattern -in @('Invoke','MsaaDoDefaultAction','BM_CLICK')) "unexpected invoke pattern: $($invoked.pattern)"
Wait-Title $app.Window.hwnd "$($app.Title) clicked" | Out-Null
$uiaText = 'uia-set-' + [guid]::NewGuid().ToString('N')
$valued = Invoke-Json @('uia-set','-Hwnd',$hwnd,'-ClassName','*EDIT*','-Value',$uiaText,'-IdleMs','0','-TimeoutMs','1000')
Assert ($valued.pattern -in @('Value','WM_CHAR')) "unexpected value pattern: $($valued.pattern)"
Wait-Title $app.Window.hwnd "$($app.Title) text:$uiaText" | Out-Null
Assert ((Invoke-Json @('focus')).hwnd -eq $foregroundBefore.hwnd) 'accessibility actions must not change the foreground window'
Assert-Err @('uia-invoke','-Hwnd',$hwnd,'-IdleMs','0','-TimeoutMs','1000') 'need -Name, -AutomationId, -ClassName, or -ControlType'
Assert-Err @('uia-invoke','-Name','ClickMe','-IdleMs','0','-TimeoutMs','1000') 'need a window selector'
Assert-Err @('uia-set','-Hwnd',$hwnd,'-ClassName','*EDIT*','-IdleMs','0','-TimeoutMs','1000') 'uia-set needs -Value'
Assert-Err @('uia-invoke','-Hwnd',$hwnd,'-Name','definitely-no-such-control','-IdleMs','0','-TimeoutMs','1000') 'No matching element'
# pointer input needs explicit consent
Assert ((Invoke-Raw @('click','-Hwnd',$hwnd,'-IdleMs','0','-TimeoutMs','1000') -AllowFail).Json.code -eq 'pointer-consent') 'click without -Pointer must fail with pointer-consent'
Assert ((Invoke-Raw @('type','-Hwnd',$hwnd,'-Text','x','-Focus','auto','-IdleMs','0','-TimeoutMs','1000') -AllowFail).Json.code -eq 'pointer-consent') '-Focus auto without -Pointer must fail with pointer-consent'
$theme = Invoke-Json @('theme')
Assert ($theme.apps -in @('dark','light') -and $theme.system -in @('dark','light')) 'theme read failed'
Assert-Err @('theme','-Mode','blue','-IdleMs','0','-TimeoutMs','1000') 'Mode must be dark or light'
Assert-Err @('theme','-Mode',$theme.apps,'-Scope','wat','-IdleMs','0','-TimeoutMs','1000') 'Scope must be apps, system, or both'
Assert-Err @('theme','-Mode',$theme.apps,'-Refresh','wat','-IdleMs','0','-TimeoutMs','1000') 'Refresh must be broadcast or full'
if ($IncludeTheme) {
$themeSet = Invoke-Json @('theme','-Mode',$theme.apps,'-Scope','apps','-IdleMs','0','-TimeoutMs','1000')
Assert ($themeSet.apps -eq $theme.apps) 'theme apps set failed'
Assert ($themeSet.refresh -eq 'broadcast') 'theme must default to the broadcast refresh'
Assert ($themeSet.previous.apps -in @('dark','light')) 'theme set must report previous values'
Assert ((Invoke-Json @('theme','-Mode',$theme.system,'-Scope','system','-IdleMs','0','-TimeoutMs','1000')).system -eq $theme.system) 'theme system set failed'
} else {
Write-Host 'skipping theme writes (pass -IncludeTheme to exercise them)'
}
$clipValue = 'win32-drive-clip-' + [guid]::NewGuid().ToString('N')
Assert ((Invoke-Json @('clipboard','-Set',$clipValue,'-IdleMs','0','-TimeoutMs','1000')).ok) 'clipboard set failed'
Assert ((Invoke-Json @('clipboard')).text -eq $clipValue) 'clipboard read after set failed'
# click must land on the control, focus the target, and leave the cursor untouched
$button = $uia.tree.children | Where-Object { $_.name -eq 'ClickMe' } | Select-Object -First 1
Assert ($null -ne $button) 'uia did not expose the ClickMe button'
$buttonX = [int]($button.x + $button.width / 2 - $winShot.x)
$buttonY = [int]($button.y + $button.height / 2 - $winShot.y)
$before = Invoke-Json @('cursor')
$click = Invoke-Json @('click','-Hwnd',$hwnd,'-X',[string]$buttonX,'-Y',[string]$buttonY,'-Pointer','-Focus','auto','-Return','keep','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')
Assert ($click.foreground.hwnd -eq $app.Window.hwnd) 'click must report the target window as foreground'
Assert ($click.target.hwnd -eq $app.Window.hwnd -and $click.target.title -like 'Win32DriveTest-*') 'click must echo the resolved target window'
Assert ($click.return -eq 'keep' -and $null -eq $click.returnedTo) '-Return keep must leave focus on the target'
Assert ($click.focus -eq 'auto' -and $click.cursor -eq 'restore') 'click focus/cursor reporting wrong'
Wait-Title $app.Window.hwnd "$($app.Title) clicked" | Out-Null
$after = Invoke-Json @('cursor')
# Tolerates the human moving the mouse mid-run: the regression is a cursor
# parked on the click target, not a cursor that simply moved.
Assert (($after.x -eq $before.x -and $after.y -eq $before.y) -or $after.x -ne $click.x -or $after.y -ne $click.y) 'click must restore the cursor position'
Assert ((Invoke-Json @('click','-Hwnd',$hwnd,'-X',[string]$buttonX,'-Y',[string]$buttonY,'-Pointer','-Cursor','keep','-Focus','skip','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')).cursor -eq 'keep') 'click -Cursor keep failed'
Assert-Err @('click','-Hwnd',$hwnd,'-Pointer','-Focus','wat','-IdleMs','0','-TimeoutMs','1000') 'Focus must be auto, require, or skip'
Assert-Err @('click','-Hwnd',$hwnd,'-Pointer','-Cursor','wat','-IdleMs','0','-TimeoutMs','1000') 'Cursor must be restore or keep'
# type must reach the focused control verbatim, including non-ASCII
# WinForms controls surface through the MSAA bridge, so match the text box by
# elimination instead of by AutomationId (which is just the raw HWND here).
$textbox = $uia.tree.children | Where-Object { $_.name -ne 'ClickMe' -and $_.width -gt 200 } | Select-Object -First 1
Assert ($null -ne $textbox) 'uia did not expose the text box'
Assert ((Invoke-Json @('click','-Hwnd',$hwnd,'-X',[string]([int]($textbox.x + $textbox.width / 2 - $winShot.x)),'-Y',[string]([int]($textbox.y + $textbox.height / 2 - $winShot.y)),'-Pointer','-Focus','auto','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')).ok) 'textbox click failed'
$unicode = 'Grusse-ae-' + [char] 0x00E4 + [char] 0x20AC
Assert ((Invoke-Json @('key','-Hwnd',$hwnd,'-Keys','^a','-Pointer','-Focus','auto','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')).ok) 'select all before typing failed'
$typed = Invoke-Json @('type','-Hwnd',$hwnd,'-Text',$unicode,'-Pointer','-Focus','auto','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')
Assert ($typed.chars -eq $unicode.Length) 'type failed'
Assert ($typed.method -eq 'unicode') 'type must default to the unicode method on 64-bit'
Assert ($typed.foreground.hwnd -eq $app.Window.hwnd) 'type must report the target window as foreground'
Wait-Title $app.Window.hwnd "$($app.Title) text:$unicode" | Out-Null
Assert ((Invoke-Json @('key','-Hwnd',$hwnd,'-Keys','^a','-Pointer','-Focus','auto','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')).foreground.hwnd -eq $app.Window.hwnd) 'key failed'
$sendkeys = 'sk+^%~(){}[]'
Assert ((Invoke-Json @('type','-Hwnd',$hwnd,'-Text',$sendkeys,'-Method','sendkeys','-Pointer','-Focus','auto','-NoCountdown','-IdleMs','0','-TimeoutMs','1000')).method -eq 'sendkeys') 'type -Method sendkeys failed'
Wait-Title $app.Window.hwnd "$($app.Title) text:$sendkeys" | Out-Null
Assert-Err @('type','-Hwnd',$hwnd,'-Text','x','-Method','wat','-Pointer','-IdleMs','0','-TimeoutMs','1000') 'Method must be unicode or sendkeys'
# focusing must fail loudly instead of driving whatever happens to be in front
Assert ((Invoke-Json @('window','-Hwnd',$hwnd,'-Action','min','-IdleMs','0','-TimeoutMs','1000')).ok) 'window min failed'
Start-Sleep -Milliseconds 250
Assert-Err @('type','-Hwnd',$hwnd,'-Text','must-not-arrive','-Pointer','-Focus','auto','-NoCountdown','-IdleMs','0','-TimeoutMs','1000') 'minimized'
Assert ((Invoke-Json @('window','-Hwnd',$hwnd,'-Action','restore','-IdleMs','0','-TimeoutMs','1000')).ok) 'window restore failed'
Start-Sleep -Milliseconds 250
$movedResult = Invoke-Json @('window','-Hwnd',$hwnd,'-Action','move','-X','100','-Y','100','-Width','430','-Height','270','-IdleMs','0','-TimeoutMs','1000')
Assert ($movedResult.ok) 'window move failed'
Assert ($movedResult.window.width -gt 350 -and $movedResult.window.height -gt 200) 'window must return the updated rect without a second query'
Start-Sleep -Milliseconds 150
$moved = @(Invoke-Json @('list')).windows | Where-Object hwnd -eq $app.Window.hwnd | Select-Object -First 1
Assert ($moved.width -gt 350 -and $moved.height -gt 200) 'window move/list update failed'
foreach ($action in 'min','restore','max','restore') {
Assert ((Invoke-Json @('window','-Hwnd',$hwnd,'-Action',$action,'-IdleMs','0','-TimeoutMs','1000')).action -eq $action) "window $action failed"
Start-Sleep -Milliseconds 150
}
Assert ((Invoke-Json @('window','-Hwnd',$hwnd,'-Action','close','-IdleMs','0','-TimeoutMs','1000')).ok) 'window close failed'
$app.Process.WaitForExit(5000) | Out-Null
Assert ($app.Process.HasExited) 'test window did not close'
# the takeover countdown must be vetoable by real user input
$abortJob = Start-Job -ScriptBlock {
param($scriptPath)
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $scriptPath overlay -Text 'abort probe' -DurationMs 3000
} -ArgumentList $script
Start-Sleep -Milliseconds 1500
Invoke-Json @('key','-Keys','{F13}','-Focus','skip','-NoCountdown','-IdleMs','0','-TimeoutMs','1000') | Out-Null
$abort = (Receive-Job -Job $abortJob -Wait -AutoRemoveJob) | ConvertFrom-Json
Assert ($abort.completed -eq $false) 'user input during the countdown must cancel the overlay'
Assert-Err @('wat') 'Unknown command'
Assert-Err @('shot','-Bogus','x') 'Unknown option'
Assert-Err @('shot','-X') 'Missing value'
Assert-Err @('shot','-TitleContains',('definitely-no-window-' + [guid]::NewGuid().ToString('N'))) 'No matching window'
# an option value equal to the script name must not swallow the command line
Assert-Err @('shot','-TitleContains','win32-drive.ps1') 'No matching window'
Assert-Err @('window','-Hwnd','999999999','-Action','move','-IdleMs','0','-TimeoutMs','1000') 'Invalid window|empty bounds|failed'
$bad = Start-TestWindow ("Win32DriveBadAction-" + [guid]::NewGuid().ToString('N'))
$created += $bad.Script
Assert-Err @('window','-Hwnd',([string]$bad.Window.hwnd),'-Action','wat','-IdleMs','0','-TimeoutMs','1000') 'Unknown window action'
Stop-Process -Id $bad.Process.Id -Force -ErrorAction SilentlyContinue
'OK'
} finally {
if ($originalClipboard -ne $null) {
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $script clipboard -Set $originalClipboard -IdleMs 0 -TimeoutMs 1000 | Out-Null
}
if ($app -and -not $app.Process.HasExited) { Stop-Process -Id $app.Process.Id -Force -ErrorAction SilentlyContinue }
foreach ($path in $created) { if ($path -and (Test-Path $path)) { Remove-Item $path -Force -ErrorAction SilentlyContinue } }
}