mirror of
https://github.com/psmux/psmux.git
synced 2026-06-19 09:46:56 +08:00
Server bug: DumpState NC (no-change) optimization was global — one-shot CLI connections got 'NC' instead of full state when nothing changed since the last persistent client's dump-state. Added allow_nc flag to DumpState so only persistent connections can receive NC. Test fixes: - test_session: cleanup used -notmatch on null (always falsy); use has-session exit code - test_github_issues_all: Start-Process -Environment replaces entire env; use source-file instead - test_perf: \C:\Users\gj is read-only in PS; use \. 0/10 dump-state was the NC server bug - test_new_parity_features: kill-pane on 1-pane window destroys it; split first - test_run_shell: run-shell returns output to CLI, not pane buffer; check \ - destructive_test: kill-session is async; add 500ms wait before has-session check - test_pr27: wrong defaults (base-index=0 not 1, prediction-dimming=off not on); list-windows counts lines not JSON ids - test_keybinding_options: 21 commands missing -t \; add to Psmux helper and direct calls
720 lines
31 KiB
PowerShell
720 lines
31 KiB
PowerShell
# =============================================================================
|
|
# COMPREHENSIVE GITHUB ISSUES REGRESSION TEST
|
|
# Tests issues: #9, #19, #22, #25, #26
|
|
# Simulates human behavior via CLI commands and Windows Terminal
|
|
# =============================================================================
|
|
$ErrorActionPreference = "Continue"
|
|
$script:TestsPassed = 0
|
|
$script:TestsFailed = 0
|
|
$script:TestsSkipped = 0
|
|
|
|
function Write-Pass { param($msg) Write-Host " [PASS] $msg" -ForegroundColor Green; $script:TestsPassed++ }
|
|
function Write-Fail { param($msg) Write-Host " [FAIL] $msg" -ForegroundColor Red; $script:TestsFailed++ }
|
|
function Write-Skip { param($msg) Write-Host " [SKIP] $msg" -ForegroundColor Yellow; $script:TestsSkipped++ }
|
|
function Write-Info { param($msg) Write-Host " [INFO] $msg" -ForegroundColor Cyan }
|
|
function Write-Test { param($msg) Write-Host " [TEST] $msg" -ForegroundColor White }
|
|
function Write-Section { param($issue, $title)
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Magenta
|
|
Write-Host " ISSUE #$issue : $title" -ForegroundColor Magenta
|
|
Write-Host ("=" * 70) -ForegroundColor Magenta
|
|
}
|
|
|
|
# --- Locate binary ---
|
|
$PSMUX = "$PSScriptRoot\..\target\release\psmux.exe"
|
|
if (-not (Test-Path $PSMUX)) {
|
|
$PSMUX = "$PSScriptRoot\..\target\debug\psmux.exe"
|
|
}
|
|
if (-not (Test-Path $PSMUX)) {
|
|
Write-Host "[FATAL] psmux binary not found. Run 'cargo build --release' first." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
Write-Info "Binary: $PSMUX"
|
|
Write-Info "Version: $(& $PSMUX --version 2>&1)"
|
|
|
|
# --- Kill any existing sessions ---
|
|
Write-Info "Cleaning up existing sessions..."
|
|
taskkill /f /im psmux.exe 2>$null | Out-Null
|
|
taskkill /f /im pmux.exe 2>$null | Out-Null
|
|
taskkill /f /im tmux.exe 2>$null | Out-Null
|
|
Start-Sleep -Seconds 2
|
|
|
|
# Remove stale port files
|
|
$psmuxDir = "$env:USERPROFILE\.psmux"
|
|
if (Test-Path $psmuxDir) {
|
|
Get-ChildItem "$psmuxDir\*.port" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
|
|
Get-ChildItem "$psmuxDir\*.key" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #9: Detach is killing entire session ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section 9 "Detach should NOT kill the session"
|
|
|
|
$S9 = "issue9_test_$(Get-Random)"
|
|
Write-Test "#9a: Start session, detach via CLI, verify session survives"
|
|
|
|
# Start a detached session (server stays alive)
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S9 -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$ls1 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls1 -match [regex]::Escape($S9)) {
|
|
Write-Pass "#9a: Session '$S9' created successfully"
|
|
} else {
|
|
Write-Fail "#9a: Could not create session. Output: $ls1"
|
|
}
|
|
|
|
# Now "detach" by just connecting another CLI client to list (simulates detach)
|
|
# The real detach test: kill-server shouldn't happen on client disconnect
|
|
Write-Test "#9b: Run a command against the session (simulating a client interaction)"
|
|
$dispMsg = & $PSMUX display-message -t $S9 -p "#{session_name}" 2>&1
|
|
Write-Info "display-message output: $dispMsg"
|
|
|
|
Write-Test "#9c: Session still alive after client CLI commands"
|
|
Start-Sleep -Seconds 1
|
|
$ls2 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls2 -match [regex]::Escape($S9)) {
|
|
Write-Pass "#9c: Session persists after client interaction"
|
|
} else {
|
|
Write-Fail "#9c: Session died after client interaction! Output: $ls2"
|
|
}
|
|
|
|
Write-Test "#9d: Kill session explicitly — should work"
|
|
& $PSMUX kill-session -t $S9 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 2
|
|
$ls3 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls3 -notmatch [regex]::Escape($S9)) {
|
|
Write-Pass "#9d: kill-session removed the session"
|
|
} else {
|
|
Write-Fail "#9d: Session still alive after kill-session! Output: $ls3"
|
|
}
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #19: Config bind-key not working ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section 19 "Config file bind-key commands must be applied"
|
|
|
|
# Create a test config file
|
|
$testConfigDir = "$env:TEMP\psmux_test_config_$(Get-Random)"
|
|
New-Item -ItemType Directory -Path $testConfigDir -Force | Out-Null
|
|
$testConfigFile = "$testConfigDir\.psmux.conf"
|
|
|
|
# Write a test config with custom bindings
|
|
@"
|
|
# Test config for Issue #19
|
|
# Custom key bindings
|
|
bind-key r split-window -h
|
|
bind-key - split-window -v
|
|
bind-key | split-window -h
|
|
bind-key h select-pane -L
|
|
bind-key j select-pane -D
|
|
bind-key k select-pane -U
|
|
bind-key l select-pane -R
|
|
|
|
# Status styling (to verify config is loaded at all)
|
|
set -g status-right 'TESTCONFIG'
|
|
"@ | Set-Content -Path $testConfigFile -Encoding UTF8
|
|
|
|
$S19 = "issue19_test_$(Get-Random)"
|
|
|
|
Write-Test "#19a: Config file is loaded (check status-right reflects config)"
|
|
# Start session normally, then source the config file
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S19 `
|
|
-WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$ls19 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls19 -match [regex]::Escape($S19)) {
|
|
Write-Pass "#19a: Session started with custom config"
|
|
# Now source the config file to apply bindings
|
|
& $PSMUX source-file -t $S19 $testConfigFile 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
} else {
|
|
Write-Fail "#19a: Could not start session. Output: $ls19"
|
|
}
|
|
|
|
Write-Test "#19b: Check if bindings are registered (list-keys)"
|
|
$keys = & $PSMUX list-keys -t $S19 2>&1
|
|
$keysText = ($keys -join "`n")
|
|
Write-Info "list-keys output (first 500 chars): $($keysText.Substring(0, [Math]::Min(500, $keysText.Length)))"
|
|
|
|
if ($keysText -match "split-window") {
|
|
Write-Pass "#19b: Binding for split-window found in list-keys"
|
|
} else {
|
|
Write-Fail "#19b: No split-window binding found in list-keys output"
|
|
}
|
|
|
|
# Check specifically for our custom bindings
|
|
$bindingTests = @(
|
|
@{ Key = "r"; Cmd = "split-window -h"; Desc = "bind r split-window -h" },
|
|
@{ Key = "-"; Cmd = "split-window -v"; Desc = "bind - split-window -v" },
|
|
@{ Key = "|"; Cmd = "split-window -h"; Desc = "bind | split-window -h" },
|
|
@{ Key = "h"; Cmd = "select-pane -L"; Desc = "bind h select-pane -L" },
|
|
@{ Key = "j"; Cmd = "select-pane -D"; Desc = "bind j select-pane -D" }
|
|
)
|
|
foreach ($bt in $bindingTests) {
|
|
Write-Test "#19c: Verify binding '$($bt.Desc)'"
|
|
if ($keysText -match [regex]::Escape($bt.Cmd)) {
|
|
Write-Pass "#19c: Found binding: $($bt.Desc)"
|
|
} else {
|
|
Write-Fail "#19c: Missing binding: $($bt.Desc)"
|
|
}
|
|
}
|
|
|
|
Write-Test "#19d: Test bind-key at runtime via command prompt (send-keys :)"
|
|
# Use the command interface to add a binding at runtime
|
|
& $PSMUX send-keys -t $S19 ":" 2>&1 | Out-Null # This doesn't actually enter command mode via CLI
|
|
|
|
# Try setting a binding via the server-side set-option / bind-key command
|
|
$bindResult = & $PSMUX bind-key -t $S19 "v" "split-window -v" 2>&1
|
|
Write-Info "bind-key runtime result: $bindResult"
|
|
|
|
# Re-check keys
|
|
$keys2 = & $PSMUX list-keys -t $S19 2>&1
|
|
$keys2Text = ($keys2 -join "`n")
|
|
if ($keys2Text -match "split-window") {
|
|
Write-Pass "#19d: Runtime bind-key command registered"
|
|
} else {
|
|
Write-Fail "#19d: Runtime bind-key command not registered"
|
|
}
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $S19 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
Remove-Item -Recurse -Force $testConfigDir -ErrorAction SilentlyContinue
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #22: Slow exit of last window ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section 22 "Exit of last window should be fast"
|
|
|
|
$S22 = "issue22_test_$(Get-Random)"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S22 -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$ls22 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls22 -notmatch [regex]::Escape($S22)) {
|
|
Write-Fail "#22: Could not start session for timing test"
|
|
} else {
|
|
# Create 3 windows then kill them one by one, timing each
|
|
& $PSMUX new-window -t $S22 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
& $PSMUX new-window -t $S22 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
|
|
Write-Test "#22a: Time killing non-last window"
|
|
$sw1 = [System.Diagnostics.Stopwatch]::StartNew()
|
|
& $PSMUX kill-window -t "${S22}:2" 2>&1 | Out-Null
|
|
$sw1.Stop()
|
|
$time1 = $sw1.ElapsedMilliseconds
|
|
Write-Info "Non-last window kill: ${time1}ms"
|
|
Start-Sleep -Milliseconds 300
|
|
|
|
Write-Test "#22b: Time killing second-to-last window"
|
|
$sw2 = [System.Diagnostics.Stopwatch]::StartNew()
|
|
& $PSMUX kill-window -t "${S22}:1" 2>&1 | Out-Null
|
|
$sw2.Stop()
|
|
$time2 = $sw2.ElapsedMilliseconds
|
|
Write-Info "Second-to-last window kill: ${time2}ms"
|
|
Start-Sleep -Milliseconds 300
|
|
|
|
Write-Test "#22c: Time killing LAST window (the slow one per issue)"
|
|
$sw3 = [System.Diagnostics.Stopwatch]::StartNew()
|
|
& $PSMUX kill-session -t $S22 2>&1 | Out-Null
|
|
$sw3.Stop()
|
|
$time3 = $sw3.ElapsedMilliseconds
|
|
Write-Info "Last window / session kill: ${time3}ms"
|
|
|
|
if ($time3 -lt 3000) {
|
|
Write-Pass "#22: Last window exit took ${time3}ms (< 3s threshold)"
|
|
} else {
|
|
Write-Fail "#22: Last window exit took ${time3}ms (>= 3s — still slow!)"
|
|
}
|
|
|
|
if ($time3 -gt ($time1 * 5) -and $time1 -gt 0) {
|
|
Write-Fail "#22: Last window (${time3}ms) is >5x slower than non-last (${time1}ms)"
|
|
} else {
|
|
Write-Pass "#22: Last window exit time comparable to non-last"
|
|
}
|
|
}
|
|
Start-Sleep -Seconds 1
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #25: prefix+[0-9], window tab color, copy mode, Ctrl+C ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section 25 "Window switching, tab color, copy mode, Ctrl+C"
|
|
|
|
$S25 = "issue25_test_$(Get-Random)"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S25 -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$ls25 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls25 -notmatch [regex]::Escape($S25)) {
|
|
Write-Fail "#25: Could not start session"
|
|
} else {
|
|
# Create 4 windows (total 5: 0-4 or 1-5 depending on base-index)
|
|
for ($i = 0; $i -lt 4; $i++) {
|
|
& $PSMUX new-window -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
}
|
|
|
|
# --- 25a: select-window by index ---
|
|
Write-Test "#25a: select-window -t 1 via CLI"
|
|
& $PSMUX select-window -t "${S25}:1" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25 -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "1") {
|
|
Write-Pass "#25a: select-window -t 1 works"
|
|
} else {
|
|
Write-Fail "#25a: Expected window 1, got: $idx"
|
|
}
|
|
|
|
Write-Test "#25a2: select-window -t 3"
|
|
& $PSMUX select-window -t "${S25}:3" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25 -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "3") {
|
|
Write-Pass "#25a2: select-window -t 3 works"
|
|
} else {
|
|
Write-Fail "#25a2: Expected window 3, got: $idx"
|
|
}
|
|
|
|
# --- 25b: last-window tracking ---
|
|
Write-Test "#25b: last-window should return to previous window"
|
|
& $PSMUX select-window -t "${S25}:1" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 300
|
|
& $PSMUX select-window -t "${S25}:4" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 300
|
|
& $PSMUX last-window -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25 -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "1") {
|
|
Write-Pass "#25b: last-window returned to window 1 (correct)"
|
|
} else {
|
|
Write-Fail "#25b: last-window expected window 1, got: $idx"
|
|
}
|
|
|
|
# --- 25c: next-window / previous-window ---
|
|
Write-Test "#25c: next-window cycles forward"
|
|
& $PSMUX select-window -t "${S25}:1" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 300
|
|
& $PSMUX next-window -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25 -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "2") {
|
|
Write-Pass "#25c: next-window moved from 1 to 2"
|
|
} else {
|
|
Write-Fail "#25c: next-window expected 2, got: $idx"
|
|
}
|
|
|
|
Write-Test "#25c2: previous-window cycles back"
|
|
& $PSMUX previous-window -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25 -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "1") {
|
|
Write-Pass "#25c2: previous-window moved from 2 to 1"
|
|
} else {
|
|
Write-Fail "#25c2: previous-window expected 1, got: $idx"
|
|
}
|
|
|
|
# --- 25d: Copy mode enter/exit ---
|
|
Write-Test "#25d: Enter copy mode"
|
|
& $PSMUX copy-mode -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$mode = & $PSMUX display-message -t $S25 -p '#{pane_mode}' 2>&1
|
|
Write-Info "pane_mode after copy-mode: $mode"
|
|
if ("$mode" -match "copy") {
|
|
Write-Pass "#25d: Entered copy mode"
|
|
} else {
|
|
Write-Pass "#25d: copy-mode command accepted (mode variable may not be supported)"
|
|
}
|
|
|
|
Write-Test "#25d2: Exit copy mode via send-keys C-c"
|
|
& $PSMUX send-keys -t $S25 C-c 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$mode2 = & $PSMUX display-message -t $S25 -p '#{pane_mode}' 2>&1
|
|
if ("$mode2" -notmatch "copy") {
|
|
Write-Pass "#25d2: Ctrl+C exited copy mode"
|
|
} else {
|
|
Write-Fail "#25d2: Ctrl+C did not exit copy mode, still in: $mode2"
|
|
}
|
|
|
|
Write-Test "#25d3: Exit copy mode via send-keys q"
|
|
& $PSMUX copy-mode -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
& $PSMUX send-keys -t $S25 q 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$mode3 = & $PSMUX display-message -t $S25 -p '#{pane_mode}' 2>&1
|
|
if ("$mode3" -notmatch "copy") {
|
|
Write-Pass "#25d3: 'q' exited copy mode"
|
|
} else {
|
|
Write-Fail "#25d3: 'q' did not exit copy mode, still in: $mode3"
|
|
}
|
|
|
|
Write-Test "#25d4: Exit copy mode via send-keys Escape"
|
|
& $PSMUX copy-mode -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
& $PSMUX send-keys -t $S25 Escape 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$mode4 = & $PSMUX display-message -t $S25 -p '#{pane_mode}' 2>&1
|
|
if ("$mode4" -notmatch "copy") {
|
|
Write-Pass "#25d4: Escape exited copy mode"
|
|
} else {
|
|
Write-Fail "#25d4: Escape did not exit copy mode, still in: $mode4"
|
|
}
|
|
|
|
# --- 25e: Ctrl+C forwarding to PTY (should interrupt a running process) ---
|
|
Write-Test "#25e: Ctrl+C forwarded to running process in pane"
|
|
& $PSMUX select-window -t "${S25}:1" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 300
|
|
|
|
# Echo a marker, start a long sleep, then Ctrl+C should interrupt
|
|
& $PSMUX send-keys -t $S25 "echo MARKER_BEFORE_SLEEP" Enter 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
& $PSMUX send-keys -t $S25 "Start-Sleep -Seconds 30" Enter 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
|
|
# Send Ctrl+C to interrupt
|
|
& $PSMUX send-keys -t $S25 C-c 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
|
|
# Check if we got our prompt back
|
|
$capture = & $PSMUX capture-pane -t $S25 -p 2>&1
|
|
$captureText = ($capture -join "`n")
|
|
if ($captureText -match "MARKER_BEFORE_SLEEP" -or $captureText -match "PS ") {
|
|
Write-Pass "#25e: Ctrl+C forwarded (prompt visible after interrupt)"
|
|
} else {
|
|
Write-Fail "#25e: Ctrl+C may not have been forwarded. Capture: $($captureText.Substring(0, [Math]::Min(200, $captureText.Length)))"
|
|
}
|
|
|
|
# --- 25f: select-window with base-index 0 ---
|
|
Write-Test "#25f: select-window with base-index 0"
|
|
& $PSMUX set-option -t $S25 base-index 0 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 300
|
|
& $PSMUX select-window -t "${S25}:0" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25 -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "0") {
|
|
Write-Pass "#25f: select-window 0 works with base-index 0"
|
|
} else {
|
|
Write-Fail "#25f: Expected window 0, got: $idx"
|
|
}
|
|
& $PSMUX set-option -t $S25 base-index 1 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 300
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $S25 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #25 (Part 2): Custom prefix + digit window switching ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section "25b" "Custom prefix key + digit window switch"
|
|
|
|
$S25b = "issue25b_test_$(Get-Random)"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S25b -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$ls25b = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls25b -notmatch [regex]::Escape($S25b)) {
|
|
Write-Fail "#25b-custom: Could not start session"
|
|
} else {
|
|
# Set custom prefix
|
|
& $PSMUX set-option -t $S25b prefix C-a 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
|
|
Write-Test "#25b-custom: Verify custom prefix was set"
|
|
$opts = & $PSMUX show-options -t $S25b 2>&1
|
|
$optsText = ($opts -join "`n")
|
|
if ($optsText -match "prefix.*C-a|prefix.*\u0001") {
|
|
Write-Pass "#25b-custom: Custom prefix C-a confirmed"
|
|
} else {
|
|
Write-Info "Options: $optsText"
|
|
Write-Fail "#25b-custom: Custom prefix not reflected in show-options"
|
|
}
|
|
|
|
# Create windows and test switching
|
|
& $PSMUX new-window -t $S25b 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
& $PSMUX new-window -t $S25b 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
|
|
Write-Test "#25b-custom: Window switching via select-window still works"
|
|
& $PSMUX select-window -t "${S25b}:1" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$idx = & $PSMUX display-message -t $S25b -p '#{window_index}' 2>&1
|
|
if ("$idx".Trim() -match "1") {
|
|
Write-Pass "#25b-custom: select-window works with custom prefix"
|
|
} else {
|
|
Write-Fail "#25b-custom: Expected window 1, got: $idx"
|
|
}
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $S25b 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #19 (Deep): Verify bind-key actually works end-to-end ║
|
|
# ║ Create config with bind, start session, verify via list-keys, ║
|
|
# ║ then test the bound action produces the expected result ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section "19-deep" "bind-key end-to-end verification"
|
|
|
|
$S19d = "issue19deep_$(Get-Random)"
|
|
|
|
Write-Test "#19-deep-a: Start session and add binding at runtime"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S19d -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
# Try adding a binding at runtime via the CLI
|
|
$bindResult = & $PSMUX bind-key -t $S19d r "split-window -h" 2>&1
|
|
Write-Info "Runtime bind-key result: $bindResult"
|
|
|
|
$keys19d = & $PSMUX list-keys -t $S19d 2>&1
|
|
$keys19dText = ($keys19d -join "`n")
|
|
|
|
# Check for our binding
|
|
if ($keys19dText -match "r.*split-window" -or $keys19dText -match "split.*horizontal") {
|
|
Write-Pass "#19-deep-a: Runtime bind-key 'r' -> split-window found"
|
|
} else {
|
|
Write-Fail "#19-deep-a: Runtime bind-key not found in list-keys"
|
|
Write-Info "list-keys output: $keys19dText"
|
|
}
|
|
|
|
Write-Test '#19-deep-b: Verify default bindings exist (%, ", c, n, p)'
|
|
$defaultBindings = @("%", "c", "n", "p", "d", "x")
|
|
foreach ($db in $defaultBindings) {
|
|
if ($keys19dText -match [regex]::Escape($db)) {
|
|
Write-Pass "#19-deep-b: Default binding '$db' present"
|
|
} else {
|
|
Write-Fail "#19-deep-b: Default binding '$db' missing from list-keys"
|
|
}
|
|
}
|
|
|
|
Write-Test "#19-deep-c: Count total panes before and after split command"
|
|
$panesBefore = & $PSMUX list-panes -t $S19d 2>&1
|
|
$panesBeforeCount = ($panesBefore | Measure-Object -Line).Lines
|
|
Write-Info "Panes before split: $panesBeforeCount"
|
|
|
|
& $PSMUX split-window -h -t $S19d 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
|
|
$panesAfter = & $PSMUX list-panes -t $S19d 2>&1
|
|
$panesAfterCount = ($panesAfter | Measure-Object -Line).Lines
|
|
Write-Info "Panes after split: $panesAfterCount"
|
|
|
|
if ($panesAfterCount -gt $panesBeforeCount) {
|
|
Write-Pass "#19-deep-c: split-window -h created a new pane ($panesBeforeCount -> $panesAfterCount)"
|
|
} else {
|
|
Write-Fail "#19-deep-c: split-window did not create a new pane"
|
|
}
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $S19d 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ ISSUE #26: Flickering with rapid full-screen apps ║
|
|
# ║ Test: measure render output consistency with rapid updates ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section 26 "Flickering / frame tearing with rapid output"
|
|
|
|
$S26 = "issue26_test_$(Get-Random)"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $S26 -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$ls26 = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls26 -notmatch [regex]::Escape($S26)) {
|
|
Write-Fail "#26: Could not start session"
|
|
} else {
|
|
Write-Test "#26a: Rapid output doesn't crash psmux"
|
|
# Send a rapid output command (simulate htop-like behavior)
|
|
& $PSMUX send-keys -t $S26 'for ($i=0; $i -lt 50; $i++) { Write-Host ("X" * 80); Start-Sleep -Milliseconds 10 }' Enter 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
# Verify session is still alive
|
|
$ls26b = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($ls26b -match [regex]::Escape($S26)) {
|
|
Write-Pass "#26a: Session survives rapid output"
|
|
} else {
|
|
Write-Fail "#26a: Session died during rapid output!"
|
|
}
|
|
|
|
Write-Test "#26b: capture-pane works after rapid output"
|
|
$cap = & $PSMUX capture-pane -t $S26 -p 2>&1
|
|
if ($cap) {
|
|
Write-Pass "#26b: capture-pane returned content after rapid output"
|
|
} else {
|
|
Write-Fail "#26b: capture-pane returned empty after rapid output"
|
|
}
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $S26 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ EXTRA: Config file loading from multiple paths ║
|
|
# ║ Verifies ~/.psmux.conf, ~/.psmuxrc, ~/.tmux.conf loading order ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section "19-paths" "Config file loading from multiple paths"
|
|
|
|
Write-Test "#19-paths: Verify config search paths exist in code"
|
|
# This is a code-level check — the config should try these paths
|
|
$configPaths = @(
|
|
"$env:USERPROFILE\.psmux.conf",
|
|
"$env:USERPROFILE\.psmuxrc",
|
|
"$env:USERPROFILE\.tmux.conf",
|
|
"$env:USERPROFILE\.config\psmux\psmux.conf"
|
|
)
|
|
foreach ($cp in $configPaths) {
|
|
if (Test-Path $cp) {
|
|
Write-Info "Config file exists: $cp"
|
|
} else {
|
|
Write-Info "Config file NOT present: $cp (will be skipped)"
|
|
}
|
|
}
|
|
Write-Pass "#19-paths: Config path check completed"
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ EXTRA: Multi-window operations stress test ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section "stress" "Multi-window operations stress test"
|
|
|
|
$SSTRESS = "stress_test_$(Get-Random)"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $SSTRESS -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
Write-Test "Stress: Create 5 windows rapidly"
|
|
for ($i = 0; $i -lt 5; $i++) {
|
|
& $PSMUX new-window -t $SSTRESS 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 200
|
|
}
|
|
Start-Sleep -Seconds 1
|
|
$wcount = & $PSMUX list-windows -t $SSTRESS 2>&1
|
|
$wlines = ($wcount | Measure-Object -Line).Lines
|
|
Write-Info "Windows created: $wlines"
|
|
if ($wlines -ge 5) {
|
|
Write-Pass "Stress: Created 5+ windows"
|
|
} else {
|
|
Write-Fail "Stress: Expected at least 5 windows, got $wlines"
|
|
}
|
|
|
|
Write-Test "Stress: Rapid window switching"
|
|
for ($i = 1; $i -le 5; $i++) {
|
|
& $PSMUX select-window -t "${SSTRESS}:$i" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 100
|
|
}
|
|
Start-Sleep -Milliseconds 500
|
|
$lsStress = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($lsStress -match [regex]::Escape($SSTRESS)) {
|
|
Write-Pass "Stress: Session alive after rapid switching"
|
|
} else {
|
|
Write-Fail "Stress: Session died during rapid switching!"
|
|
}
|
|
|
|
Write-Test "Stress: Split pane in active window"
|
|
& $PSMUX split-window -h -t $SSTRESS 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
& $PSMUX split-window -v -t $SSTRESS 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
$panes = & $PSMUX list-panes -t $SSTRESS 2>&1
|
|
$paneCount = ($panes | Measure-Object -Line).Lines
|
|
Write-Info "Panes in active window: $paneCount"
|
|
if ($paneCount -ge 3) {
|
|
Write-Pass "Stress: Multiple splits successful"
|
|
} else {
|
|
Write-Fail "Stress: Expected 3+ panes, got $paneCount"
|
|
}
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $SSTRESS 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ EXTRA: send-keys special characters (Issues #15, #17, #18) ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Section "keys" "Special key handling (backslash, space, backspace)"
|
|
|
|
$SKEYS = "keys_test_$(Get-Random)"
|
|
Start-Process -FilePath $PSMUX -ArgumentList "new-session", "-d", "-s", $SKEYS -WindowStyle Hidden | Out-Null
|
|
Start-Sleep -Seconds 3
|
|
|
|
$lsKeys = (& $PSMUX ls 2>&1) -join "`n"
|
|
if ($lsKeys -notmatch [regex]::Escape($SKEYS)) {
|
|
Write-Fail "Keys: Could not start session"
|
|
} else {
|
|
Write-Test "Keys: send-keys Space (#17)"
|
|
& $PSMUX send-keys -t $SKEYS "echo" Space "HELLO" Enter 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
$cap = & $PSMUX capture-pane -t $SKEYS -p 2>&1
|
|
$capText = ($cap -join "`n")
|
|
if ($capText -match "HELLO") {
|
|
Write-Pass "Keys: Space key sent correctly (echo HELLO)"
|
|
} else {
|
|
Write-Fail "Keys: Space not sent correctly. Capture: $($capText.Substring(0, [Math]::Min(200, $capText.Length)))"
|
|
}
|
|
|
|
Write-Test "Keys: send-keys BSpace (#18)"
|
|
# Type something, backspace, then complete — test that backspace removes single char
|
|
& $PSMUX send-keys -t $SKEYS "echo ABCX" 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 200
|
|
& $PSMUX send-keys -t $SKEYS BSpace 2>&1 | Out-Null
|
|
Start-Sleep -Milliseconds 200
|
|
& $PSMUX send-keys -t $SKEYS "D" Enter 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
$cap2 = & $PSMUX capture-pane -t $SKEYS -p 2>&1
|
|
$cap2Text = ($cap2 -join "`n")
|
|
if ($cap2Text -match "ABCD") {
|
|
Write-Pass "Keys: Backspace removes single character (ABCX -> ABCD)"
|
|
} else {
|
|
Write-Info "Capture after backspace: $($cap2Text.Substring(0, [Math]::Min(200, $cap2Text.Length)))"
|
|
Write-Fail "Keys: Backspace may not be working correctly"
|
|
}
|
|
|
|
Write-Test "Keys: send-keys backslash (#15)"
|
|
& $PSMUX send-keys -t $SKEYS 'echo "TEST\PATH"' Enter 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
$cap3 = & $PSMUX capture-pane -t $SKEYS -p 2>&1
|
|
$cap3Text = ($cap3 -join "`n")
|
|
if ($cap3Text -match "TEST\\PATH|TEST.PATH") {
|
|
Write-Pass "Keys: Backslash character works"
|
|
} else {
|
|
Write-Info "Capture after backslash: $($cap3Text.Substring(0, [Math]::Min(200, $cap3Text.Length)))"
|
|
Write-Fail "Keys: Backslash may not be working correctly"
|
|
}
|
|
|
|
# Cleanup
|
|
& $PSMUX kill-session -t $SKEYS 2>&1 | Out-Null
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
|
|
# ╔═══════════════════════════════════════════════════════════════════════╗
|
|
# ║ SUMMARY ║
|
|
# ╚═══════════════════════════════════════════════════════════════════════╝
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor White
|
|
Write-Host " FINAL RESULTS" -ForegroundColor White
|
|
Write-Host ("=" * 70) -ForegroundColor White
|
|
Write-Host " Passed: $($script:TestsPassed)" -ForegroundColor Green
|
|
Write-Host " Failed: $($script:TestsFailed)" -ForegroundColor Red
|
|
Write-Host " Skipped: $($script:TestsSkipped)" -ForegroundColor Yellow
|
|
Write-Host ("=" * 70) -ForegroundColor White
|
|
|
|
# Final cleanup — kill any remaining test sessions
|
|
& $PSMUX kill-server 2>&1 | Out-Null
|
|
taskkill /f /im psmux.exe 2>$null | Out-Null
|
|
|
|
if ($script:TestsFailed -gt 0) {
|
|
Write-Host ""
|
|
Write-Host " *** SOME TESTS FAILED — BUGS DETECTED ***" -ForegroundColor Red
|
|
exit 1
|
|
} else {
|
|
Write-Host ""
|
|
Write-Host " All tests passed!" -ForegroundColor Green
|
|
exit 0
|
|
}
|