param( [ValidateRange(1, 1000000)] [int]$Rounds = 1000 ) $ErrorActionPreference = "Stop" $utf8Encoding = New-Object System.Text.UTF8Encoding($false) [Console]::OutputEncoding = $utf8Encoding $OutputEncoding = $utf8Encoding $mutex = New-Object System.Threading.Mutex($false, "Local\AiChat-Stability-Suite") $ownsMutex = $false try { try { $ownsMutex = $mutex.WaitOne(0) } catch [System.Threading.AbandonedMutexException] { $ownsMutex = $true } if (!$ownsMutex) { Write-Host ">> Another stability run is already active; skipping this overlapping run." -ForegroundColor Yellow exit 0 } $root = Split-Path -Parent $PSScriptRoot $logRoot = Join-Path $root "logs\stability" if (!(Test-Path $logRoot)) { New-Item -ItemType Directory -Path $logRoot -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $logFile = Join-Path $logRoot "stability-$timestamp.log" $summaryFile = Join-Path $logRoot "stability-$timestamp.summary.json" function Invoke-StabilitySuite { param( [string]$SuiteName ) $testScript = Join-Path $root "backend\tests\stability_1000_round.php" Write-Host ">> Running $SuiteName suite ($Rounds rounds)" -ForegroundColor Cyan Add-Content -Path $logFile -Value "===== $SuiteName =====" -Encoding UTF8 # Windows PowerShell wraps native stderr as ErrorRecord objects. With the # script-wide Stop preference, a failing suite used to abort this runner # before the exit code, remaining suites, and JSON summary were recorded. $previousErrorActionPreference = $ErrorActionPreference try { $ErrorActionPreference = "Continue" & php $testScript $Rounds $SuiteName 2>&1 | ForEach-Object { $line = [string]$_ Write-Host $line Add-Content -Path $logFile -Value $line -Encoding UTF8 } $exitCode = $LASTEXITCODE } finally { $ErrorActionPreference = $previousErrorActionPreference } Add-Content -Path $logFile -Value "" -Encoding UTF8 return [PSCustomObject]@{ suite = $SuiteName requestedRounds = $Rounds exitCode = $exitCode completedAt = (Get-Date).ToString("s") } } $summary = @() foreach ($suite in @("agent", "image", "llm")) { $summary += Invoke-StabilitySuite -SuiteName $suite } $summary | ConvertTo-Json -Depth 4 | Set-Content -Path $summaryFile -Encoding UTF8 $failedSuites = @($summary | Where-Object { $_.exitCode -ne 0 }) if ($failedSuites.Count -gt 0) { Write-Error "Stability run failed in $($failedSuites.Count) suite(s). Log: $logFile" exit 1 } Write-Host ">> Stability suites completed. Log: $logFile" -ForegroundColor Green } finally { if ($ownsMutex) { $mutex.ReleaseMutex() } $mutex.Dispose() }