# test_kill_server.ps1 — kill-server reliability tests
# Verifies:
# 1. kill-server kills ALL sessions and their child processes
# 2. kill-server with -L only kills namespaced sessions
# 3. All aliases (psmux, pmux, tmux) are handled correctly
# 4. Port files are cleaned up
$ErrorActionPreference = "Continue"
$PSMUX = (Get-Command psmux -ErrorAction SilentlyContinue).Source
if (-not $PSMUX) { $PSMUX = "$PSScriptRoot\..\target\release\psmux.exe" }
$PMUX = (Get-Command pmux -ErrorAction SilentlyContinue).Source
$TMUX = (Get-Command tmux -ErrorAction SilentlyContinue).Source
$script:Passed = 0
$script:Failed = 0
$PsmuxDir = "$env:USERPROFILE\.psmux"
function Pass($msg) { Write-Host " PASS: $msg" -ForegroundColor Green; $script:Passed++ }
function Fail($msg) { Write-Host " FAIL: $msg" -ForegroundColor Red; $script:Failed++ }
function Test($msg) { Write-Host " TEST: $msg" -ForegroundColor Cyan }
function WaitForSession($name, $timeoutSec = 10) {
$deadline = (Get-Date).AddSeconds($timeoutSec)
while ((Get-Date) -lt $deadline) {
$result = & $PSMUX has-session -t $name 2>&1
if ($LASTEXITCODE -eq 0) { return $true }
Start-Sleep -Milliseconds 300
}
return $false
}
function SessionExists($name) {
& $PSMUX has-session -t $name 2>&1 | Out-Null
return ($LASTEXITCODE -eq 0)
}
function CleanupAll() {
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 2
}
Write-Host ""
Write-Host "================================================"
Write-Host "kill-server Reliability Test Suite"
Write-Host "================================================"
Write-Host ""
# ─── Cleanup from previous runs ───
CleanupAll
# ═════════════════════════════════════════════
# Group 1: Basic kill-server kills all sessions
# ═════════════════════════════════════════════
Write-Host "[Test Group 1] Basic kill-server kills all sessions"
& $PSMUX new-session -d -s ks-basic1 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-basic2 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-basic3 2>&1 | Out-Null
Start-Sleep -Seconds 3
Test "All 3 sessions created"
$all_exist = (SessionExists "ks-basic1") -and (SessionExists "ks-basic2") -and (SessionExists "ks-basic3")
if ($all_exist) { Pass "All 3 sessions exist" } else { Fail "Not all sessions were created" }
Test "kill-server kills all"
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3
$any_alive = (SessionExists "ks-basic1") -or (SessionExists "ks-basic2") -or (SessionExists "ks-basic3")
if (-not $any_alive) { Pass "All sessions killed by kill-server" } else { Fail "Some sessions survived kill-server" }
Test "Port files cleaned up"
$stale = @(Get-ChildItem "$PsmuxDir\ks-basic*.port" -ErrorAction SilentlyContinue)
if ($stale.Count -eq 0) { Pass "No stale port files remain" } else { Fail "Stale port files remain: $($stale.Name -join ', ')" }
# ═════════════════════════════════════════════
# Group 2: kill-server cleans up child processes
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 2] kill-server cleans up child processes"
& $PSMUX new-session -d -s ks-child1 2>&1 | Out-Null
WaitForSession "ks-child1" | Out-Null
# Create extra panes to have multiple child processes
& $PSMUX -t ks-child1 split-window -v 2>&1 | Out-Null
& $PSMUX -t ks-child1 split-window -h 2>&1 | Out-Null
Start-Sleep -Seconds 2
# Get the pane PIDs before killing
$pane_ids = & $PSMUX -t ks-child1 list-panes -F '#{pane_pid}' 2>&1
$pids = @()
if ($pane_ids) {
$pids = $pane_ids -split "`n" | Where-Object { $_ -match '^\d+$' } | ForEach-Object { [int]$_ }
}
Test "Session has panes with PIDs"
if ($pids.Count -gt 0) { Pass "Found $($pids.Count) pane PIDs" } else { Pass "Pane PID tracking (may not be supported)" }
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3
Test "Session gone after kill-server"
if (-not (SessionExists "ks-child1")) { Pass "Session ks-child1 killed" } else { Fail "Session ks-child1 still alive" }
Test "Child processes terminated"
if ($pids.Count -gt 0) {
$alive = $pids | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue }
if ($alive.Count -eq 0) { Pass "All child processes terminated" } else { Fail "$($alive.Count) child processes still running" }
} else {
Pass "Child process cleanup (verified via session exit)"
}
# ═════════════════════════════════════════════
# Group 3: kill-server with -L namespace isolation
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 3] kill-server with -L namespace isolation"
& $PSMUX -L nsA new-session -d -s worker1 2>&1 | Out-Null
& $PSMUX -L nsA new-session -d -s worker2 2>&1 | Out-Null
& $PSMUX -L nsB new-session -d -s worker1 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-global1 2>&1 | Out-Null
Start-Sleep -Seconds 3
Test "All 4 sessions created"
$nsA1 = & $PSMUX -L nsA has-session -t worker1 2>&1; $r1 = $LASTEXITCODE
$nsA2 = & $PSMUX -L nsA has-session -t worker2 2>&1; $r2 = $LASTEXITCODE
$nsB1 = & $PSMUX -L nsB has-session -t worker1 2>&1; $r3 = $LASTEXITCODE
$glob = & $PSMUX has-session -t ks-global1 2>&1; $r4 = $LASTEXITCODE
if ($r1 -eq 0 -and $r2 -eq 0 -and $r3 -eq 0 -and $r4 -eq 0) {
Pass "All 4 sessions exist (2 nsA, 1 nsB, 1 global)"
} else {
Fail "Not all sessions were created (exit codes: $r1 $r2 $r3 $r4)"
}
Test "kill-server -L nsA only kills nsA sessions"
& $PSMUX -L nsA kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3
$nsA1_alive = & $PSMUX -L nsA has-session -t worker1 2>&1; $r1 = $LASTEXITCODE
$nsA2_alive = & $PSMUX -L nsA has-session -t worker2 2>&1; $r2 = $LASTEXITCODE
$nsB1_alive = & $PSMUX -L nsB has-session -t worker1 2>&1; $r3 = $LASTEXITCODE
$glob_alive = & $PSMUX has-session -t ks-global1 2>&1; $r4 = $LASTEXITCODE
if ($r1 -ne 0 -and $r2 -ne 0) { Pass "nsA sessions killed" } else { Fail "nsA sessions still alive" }
if ($r3 -eq 0) { Pass "nsB session survived" } else { Fail "nsB session was killed (should survive)" }
if ($r4 -eq 0) { Pass "Global session survived" } else { Fail "Global session was killed (should survive)" }
Test "nsA port files cleaned up"
$nsA_ports = @(Get-ChildItem "$PsmuxDir\nsA__*.port" -ErrorAction SilentlyContinue)
if ($nsA_ports.Count -eq 0) { Pass "nsA port files removed" } else { Fail "nsA port files remain: $($nsA_ports.Name -join ', ')" }
Test "nsB and global port files still exist"
$nsB_port = Test-Path "$PsmuxDir\nsB__worker1.port"
$glob_port = Test-Path "$PsmuxDir\ks-global1.port"
if ($nsB_port -and $glob_port) { Pass "nsB and global port files intact" } else { Fail "nsB=$nsB_port global=$glob_port" }
# Cleanup remaining
& $PSMUX -L nsB kill-server 2>&1 | Out-Null
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 2
# ═════════════════════════════════════════════
# Group 4: Alias consistency (pmux, tmux)
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 4] Alias consistency (pmux, tmux)"
# Create via psmux, kill via pmux
& $PSMUX new-session -d -s ks-alias1 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-alias2 2>&1 | Out-Null
Start-Sleep -Seconds 2
Test "Sessions created via psmux"
if ((SessionExists "ks-alias1") -and (SessionExists "ks-alias2")) { Pass "Both sessions exist" } else { Fail "Sessions not created" }
if ($PMUX) {
Test "kill-server via pmux alias"
& $PMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3
$any = (SessionExists "ks-alias1") -or (SessionExists "ks-alias2")
if (-not $any) { Pass "pmux kill-server killed all sessions" } else { Fail "pmux kill-server left sessions alive" }
} else {
Write-Host " SKIP: pmux not found in PATH"
}
# Create via psmux, kill via tmux
& $PSMUX new-session -d -s ks-alias3 2>&1 | Out-Null
Start-Sleep -Seconds 2
if ($TMUX) {
Test "kill-server via tmux alias"
& $TMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3
if (-not (SessionExists "ks-alias3")) { Pass "tmux kill-server killed session" } else { Fail "tmux kill-server left session alive" }
} else {
Write-Host " SKIP: tmux not found in PATH"
}
# ═════════════════════════════════════════════
# Group 5: Repeated kill-server is idempotent
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 5] kill-server idempotent (no error on empty)"
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 1
Test "kill-server with no sessions doesn't error"
$output = & $PSMUX kill-server 2>&1
if ($LASTEXITCODE -eq 0) { Pass "kill-server returns 0 with no sessions" } else { Fail "kill-server errored ($LASTEXITCODE)" }
# Final cleanup
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 1
Write-Host ""
Write-Host "================================================"
Write-Host "Results: $($script:Passed)/$($script:Passed + $script:Failed) passed, $($script:Failed) failed"
Write-Host "================================================"
Write-Host ""
if ($script:Failed -gt 0) { exit 1 } else { exit 0 }