param( [ValidateSet('Debug', 'Release')] [string]$Configuration = 'Debug', [ValidateSet('x64', 'Win32')] [string]$Platform = 'x64', [ValidateSet('All', 'wide-text', 'dpi-coordinate', 'ime-position')] [string]$Mode = 'All', [ValidateSet('inherit', 'unaware', 'system', 'per-monitor', 'per-monitor-v2')] [string]$DpiAwareness = 'inherit', [ValidateSet('unspecified', 'MicrosoftPinyin', 'Sogou')] [string]$ImeName = 'unspecified' ) $ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $msbuild = if ($env:STELLARX_MSBUILD) { $env:STELLARX_MSBUILD } else { 'D:\vs2022\MSBuild\Current\Bin\MSBuild.exe' } $project = Join-Path $root 'tests\EasyX.VisualProbe.vcxproj' $probe = Join-Path $root "build\$Platform\$Configuration\EasyX.VisualProbe.exe" if (-not (Test-Path -LiteralPath $msbuild)) { throw "MSBuild not found: $msbuild" } $pathKeyCount = @( [Environment]::GetEnvironmentVariables([EnvironmentVariableTarget]::Process).Keys | Where-Object { [string]$_ -ieq 'Path' } ).Count if ($pathKeyCount -gt 1) { $buildCommand = 'set "PATH=" & "' + $msbuild + '" "' + $project + '" /m /t:Build /p:Configuration=' + $Configuration + ' /p:Platform=' + $Platform + ' /v:minimal' & $env:ComSpec /d /c $buildCommand } else { & $msbuild $project /m /t:Build /p:Configuration=$Configuration /p:Platform=$Platform /v:minimal } if ($LASTEXITCODE -ne 0) { throw 'Visual probe build failed' } if (-not (Test-Path -LiteralPath $probe)) { throw "Visual probe executable not found: $probe" } $sessionId = [guid]::NewGuid().ToString() $stamp = Get-Date -Format 'yyyyMMdd-HHmmss-fff' $sessionRoot = Join-Path $root "TestResults\VisualProbe\$stamp-$sessionId" New-Item -ItemType Directory -Force -Path $sessionRoot | Out-Null $modes = if ($Mode -eq 'All') { @('wide-text', 'dpi-coordinate') } else { @($Mode) } $summaries = New-Object System.Collections.Generic.List[object] $probeFailed = $false foreach ($probeMode in $modes) { $startedAtUtc = [DateTime]::UtcNow.ToString('o') $runDirectory = Join-Path $sessionRoot $probeMode New-Item -ItemType Directory -Force -Path $runDirectory | Out-Null if ($probeMode -eq 'ime-position') { $manualEvidence = Join-Path $runDirectory 'manual-evidence' New-Item -ItemType Directory -Force -Path $manualEvidence | Out-Null Write-Host "Manual IME position probe: $ImeName" Write-Host ' 1. Select the requested IME and click the EasyX window.' Write-Host ' 2. Press F1, type ceshi, inspect the candidate position, then press Escape.' Write-Host ' 3. Repeat with F2 and F3. Use only the fixed non-sensitive input ceshi.' Write-Host ' 4. At F3, commit once with Space; switch back to F1, type ceshi, and verify the old F3 position is not reused.' Write-Host ' 5. To close 0C-3, save tightly cropped screenshots with anchor-id prefixes under:' Write-Host " $manualEvidence" Write-Host ' 6. Close the EasyX window when all three anchors and the post-commit F1 regression have been exercised.' } Write-Host "Running visual probe mode: $probeMode" & $probe --mode $probeMode --output $runDirectory --dpi-awareness $DpiAwareness --ime-name $ImeName $exitCode = $LASTEXITCODE $rawPath = Join-Path $runDirectory 'raw-result.json' if (-not (Test-Path -LiteralPath $rawPath)) { throw "Visual probe did not produce raw-result.json (exit $exitCode): $runDirectory" } $summary = & (Join-Path $PSScriptRoot 'analyze-visual-probe.ps1') ` -Path $runDirectory -SessionId $sessionId -StartedAtUtc $startedAtUtc -ProbeExitCode $exitCode $summaries.Add($summary) if ($exitCode -ne 0 -or $summary.automatedStatus -ne 'pass') { $probeFailed = $true } } if ($summaries.Count -eq 2) { $drawing = $summaries | Where-Object mode -eq 'wide-text' | Select-Object -First 1 $coordinate = $summaries | Where-Object mode -eq 'dpi-coordinate' | Select-Object -First 1 $combinedInput = "$($drawing.manifestSha256):$($coordinate.manifestSha256)" $sha = [Security.Cryptography.SHA256]::Create() try { $combinedBytes = [Text.Encoding]::UTF8.GetBytes($combinedInput) $combinedHash = ([BitConverter]::ToString($sha.ComputeHash($combinedBytes))).Replace('-', '') } finally { $sha.Dispose() } $baseline = [pscustomobject][ordered]@{ visualProbeBaselineSchemaVersion = 1 sessionId = $sessionId createdAtUtc = [DateTime]::UtcNow.ToString('o') drawingBaselineManifest = 'wide-text/manifest-v1.json' coordinateBaselineManifest = 'dpi-coordinate/manifest-v1.json' drawingBaselineSha256 = $drawing.manifestSha256 coordinateBaselineSha256 = $coordinate.manifestSha256 combinedBaselineId = "sha256:$combinedHash" dpiAwareness = $coordinate.windowAwareness dpiForWindow = $coordinate.dpiForWindow imeIdentity = $null hkl = $null automatedStatus = if ($probeFailed) { 'fail' } else { 'pass' } manualStatus = 'not_reviewed' } $baselinePath = Join-Path $sessionRoot 'baseline-set-v1.json' $utf8Bom = New-Object System.Text.UTF8Encoding($true) [IO.File]::WriteAllText($baselinePath, ($baseline | ConvertTo-Json -Depth 20) + [Environment]::NewLine, $utf8Bom) $baselineHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $baselinePath).Hash $utf8NoBom = New-Object System.Text.UTF8Encoding($false) [IO.File]::WriteAllText($baselinePath + '.sha256', "$baselineHash *baseline-set-v1.json`r`n", $utf8NoBom) Write-Host "Combined baseline: $baselinePath" Write-Host "Combined baseline ID: $($baseline.combinedBaselineId)" } $summaries | Format-Table mode, automatedStatus, manualStatus, testCaseCount, evidenceCount, dpiForWindow Write-Host "Result directory: $sessionRoot" if ($probeFailed) { throw "One or more visual probe modes did not pass; evidence was preserved in $sessionRoot" }