param(
|
[switch]$CollectOnly,
|
[switch]$NoLaunch,
|
[int]$WaitSeconds = 45
|
)
|
|
$ErrorActionPreference = "Stop"
|
|
function Write-Step {
|
param([string]$Message)
|
Write-Host ""
|
Write-Host "== $Message ==" -ForegroundColor Cyan
|
}
|
|
function Add-Result {
|
param(
|
[System.Collections.Generic.List[string]]$Results,
|
[string]$Name,
|
[bool]$Passed,
|
[string]$Detail = ""
|
)
|
$state = if ($Passed) { "PASS" } else { "NEEDS_EVIDENCE" }
|
$line = if ($Detail) { "$state`t$Name`t$Detail" } else { "$state`t$Name" }
|
$Results.Add($line) | Out-Null
|
$color = if ($Passed) { "Green" } else { "Yellow" }
|
Write-Host "[$state] $Name $Detail" -ForegroundColor $color
|
}
|
|
$PackageDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
$ExePath = Join-Path $PackageDir "PrivateVoice Dictation.exe"
|
$RequiredFiles = @(
|
"PrivateVoice Dictation.exe",
|
"onnxruntime.dll",
|
"sherpa-onnx-c-api.dll",
|
"sherpa-onnx-cxx-api.dll"
|
)
|
$AppDataDir = Join-Path $env:APPDATA "PrivateVoice Input"
|
$LogPath = Join-Path $AppDataDir "app.log"
|
$Timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
$EvidenceDir = Join-Path $PackageDir "qa-evidence-$Timestamp"
|
$TargetPath = Join-Path $EvidenceDir "qa-target.txt"
|
$Results = [System.Collections.Generic.List[string]]::new()
|
|
New-Item -ItemType Directory -Path $EvidenceDir -Force | Out-Null
|
|
Write-Step "PrivateVoice Windows preview QA"
|
"GeneratedAt: $(Get-Date -Format o)" | Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "qa-summary.txt")
|
"PackageDir: $PackageDir" | Add-Content -Encoding UTF8 (Join-Path $EvidenceDir "qa-summary.txt")
|
"AppDataDir: $AppDataDir" | Add-Content -Encoding UTF8 (Join-Path $EvidenceDir "qa-summary.txt")
|
|
Write-Step "Checking package files"
|
foreach ($file in $RequiredFiles) {
|
$path = Join-Path $PackageDir $file
|
Add-Result $Results "file exists: $file" (Test-Path $path) $path
|
}
|
|
Write-Step "Collecting system information"
|
try {
|
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer, CsSystemType, CsProcessors, CsTotalPhysicalMemory |
|
Format-List | Out-String | Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "system-info.txt")
|
Add-Result $Results "system info collected" $true
|
} catch {
|
Add-Result $Results "system info collected" $false $_.Exception.Message
|
}
|
|
Write-Step "Collecting Windows runtime preflight"
|
try {
|
$webview2Entries = @()
|
$uninstallRoots = @(
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
)
|
foreach ($root in $uninstallRoots) {
|
$webview2Entries += Get-ItemProperty $root -ErrorAction SilentlyContinue |
|
Where-Object { $_.DisplayName -match "WebView2" } |
|
Select-Object DisplayName, DisplayVersion, Publisher, InstallLocation
|
}
|
if ($webview2Entries.Count -gt 0) {
|
$webview2Entries | Format-List | Out-String | Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "webview2-runtime.txt")
|
} else {
|
"No WebView2 Runtime entry found in standard uninstall registry paths. App startup logs remain authoritative." |
|
Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "webview2-runtime.txt")
|
}
|
Add-Result $Results "WebView2 registry inventory collected" $true "entries=$($webview2Entries.Count)"
|
} catch {
|
Add-Result $Results "WebView2 registry inventory collected" $false $_.Exception.Message
|
}
|
|
try {
|
if (Get-Command Get-PnpDevice -ErrorAction SilentlyContinue) {
|
Get-PnpDevice -Class AudioEndpoint -ErrorAction SilentlyContinue |
|
Select-Object Status, Class, FriendlyName, InstanceId |
|
Format-Table -AutoSize | Out-String |
|
Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "audio-endpoints.txt")
|
} else {
|
Get-CimInstance Win32_SoundDevice -ErrorAction SilentlyContinue |
|
Select-Object Status, Name, Manufacturer, DeviceID |
|
Format-Table -AutoSize | Out-String |
|
Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "audio-endpoints.txt")
|
}
|
Add-Result $Results "audio endpoint inventory collected" $true
|
} catch {
|
Add-Result $Results "audio endpoint inventory collected" $false $_.Exception.Message
|
}
|
|
if (-not $CollectOnly -and -not $NoLaunch) {
|
Write-Step "Launching app"
|
if (-not (Test-Path $ExePath)) {
|
throw "Missing executable: $ExePath"
|
}
|
Start-Process -FilePath $ExePath -WorkingDirectory $PackageDir | Out-Null
|
}
|
|
Write-Step "Waiting for app log"
|
$deadline = (Get-Date).AddSeconds($WaitSeconds)
|
while ((Get-Date) -lt $deadline -and -not (Test-Path $LogPath)) {
|
Start-Sleep -Milliseconds 500
|
}
|
Add-Result $Results "app log exists" (Test-Path $LogPath) $LogPath
|
|
Write-Step "Checking process"
|
$processes = Get-Process | Where-Object { $_.ProcessName -like "PrivateVoice*" -or $_.Path -eq $ExePath } | Select-Object Id, ProcessName, Path
|
$processes | Format-Table -AutoSize | Out-String | Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "processes.txt")
|
Add-Result $Results "PrivateVoice process visible" (($processes | Measure-Object).Count -gt 0)
|
|
if (-not $CollectOnly) {
|
Write-Step "Manual runtime check"
|
@(
|
"PrivateVoice Windows Preview QA Target",
|
"Click after the marker below, dictate one short Chinese sentence, then press Ctrl+S in Notepad.",
|
"DICTATION_RESULT:"
|
) | Set-Content -Encoding UTF8 $TargetPath
|
try {
|
Start-Process -FilePath "notepad.exe" -ArgumentList "`"$TargetPath`"" | Out-Null
|
Add-Result $Results "Notepad target launched" $true $TargetPath
|
} catch {
|
Add-Result $Results "Notepad target launched" $false $_.Exception.Message
|
}
|
Write-Host "1. Keep this PowerShell window open." -ForegroundColor White
|
Write-Host "2. In PrivateVoice, make sure the SenseVoice model is installed and the engine is ready." -ForegroundColor White
|
Write-Host "3. Use the Notepad window opened by this script as the target input field." -ForegroundColor White
|
Write-Host "4. Click after DICTATION_RESULT:, use the configured hotkey, and speak one short Chinese sentence." -ForegroundColor White
|
Write-Host "5. Confirm the recognized text appears in Notepad, press Ctrl+S in Notepad, then return here." -ForegroundColor White
|
Read-Host "Press Enter after you save the Notepad target file"
|
}
|
|
if (Test-Path $TargetPath) {
|
Copy-Item $TargetPath (Join-Path $EvidenceDir "qa-target-captured.txt") -Force
|
$targetText = Get-Content $TargetPath -Raw -ErrorAction SilentlyContinue
|
$marker = "DICTATION_RESULT:"
|
$markerIndex = $targetText.IndexOf($marker)
|
$dictationText = ""
|
if ($markerIndex -ge 0) {
|
$dictationText = $targetText.Substring($markerIndex + $marker.Length).Trim()
|
}
|
$dictationText | Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "qa-target-dictation-text.txt")
|
Add-Result $Results "target file saved with dictation text" ($dictationText.Length -gt 0) "chars=$($dictationText.Length)"
|
}
|
|
Write-Step "Collecting logs"
|
if (Test-Path $LogPath) {
|
Copy-Item $LogPath (Join-Path $EvidenceDir "app.log") -Force
|
$logText = Get-Content $LogPath -Raw -ErrorAction SilentlyContinue
|
Add-Result $Results "startup log found" ($logText -match "PrivateVoice Dictation starting")
|
Add-Result $Results "engine ready log found" ($logText -match "ASR engine ready|Engine initialized")
|
Add-Result $Results "recording log found" ($logText -match "Recording started")
|
Add-Result $Results "recognized text log found" ($logText -match "Recognized:")
|
Add-Result $Results "paste success log found" ($logText -match "PERF paste_done.*result=success|PERF fallback_type_done.*result=success")
|
}
|
|
$ConfigPath = Join-Path $AppDataDir "config.json"
|
if (Test-Path $ConfigPath) {
|
Copy-Item $ConfigPath (Join-Path $EvidenceDir "config.json") -Force
|
}
|
|
$Results | Set-Content -Encoding UTF8 (Join-Path $EvidenceDir "qa-results.tsv")
|
$Results | Add-Content -Encoding UTF8 (Join-Path $EvidenceDir "qa-summary.txt")
|
|
Write-Step "Evidence package"
|
$ZipPath = "$EvidenceDir.zip"
|
if (Test-Path $ZipPath) {
|
Remove-Item $ZipPath -Force
|
}
|
Compress-Archive -Path (Join-Path $EvidenceDir "*") -DestinationPath $ZipPath
|
Write-Host "Evidence directory: $EvidenceDir" -ForegroundColor Green
|
Write-Host "Evidence zip: $ZipPath" -ForegroundColor Green
|
|
Write-Host ""
|
Write-Host "Send back the evidence zip if any check is NEEDS_EVIDENCE or the app does not behave correctly." -ForegroundColor Yellow
|