psmux_psmux/tests/test_split_limits.ps1
Josh b6098bb7fb fix: lower split minimum, fix set-option -o tracking, harden tests and fix timeouts
Source changes:
  pane.rs: lower MIN_SPLIT_ROWS from 4 to 2, allowing vertical splits on
    smaller panes (fixes split-window -p 25 in tight layouts)
  config.rs: fix set-option -o (only-if-unset) to track explicitly set
    options via user_set_options HashSet instead of checking if the value
    is non-empty (which always returns true for options with defaults)

Test fixes and improvements:
  test_features2: exit copy-mode before remain-on-exit tests (was leaving
    pane in copy-mode causing send-keys exit to go to mode handler)
  test_tcp_mega_suite: harden against null port files in batch runs, add
    retry logic for port/key file reads
  test_tcp_flag_parity: same null port hardening
  test_cli_mega_suite: use horizontal split for -c test to avoid size limits
  test_split_limits: add relaxed prompt detection for tiny panes
  test_issue200_new_session: simplify to use run-command TCP path
  test_issue200_proof: simplify to use run-command TCP path
  test_issue15_altgr: use hex escape for tilde to avoid pwsh expansion
  test_issue211: fix dump TCP command to use correct session, add retry
  test_issue204: replace Start-Process -Wait with timeout wrapper

Timeout fixes (reduced excessive Start-Sleep values):
  test_pane_navigation, test_squelch_visibility, test_issues_107_109_110,
  test_issue111_format_cwd, test_env_shim, test_tui_exit_cleanup,
  test_claude_agent_teams

New test files:
  tests/_batch_runner.ps1: streamlined batch test runner
  tests/_full_run.ps1: full 203-test runner with summary
  tests/test_issue221_run_shell.ps1: run-shell config E2E
  tests/test_pr222_223_run_shell_paths.ps1: tilde path expansion E2E
  tests/test_issue227_remain_on_exit_hooks.ps1: remain-on-exit hooks E2E
  tests/test_spaces_in_paths.ps1: paths with spaces E2E
  tests-rs/test_issue227_remain_on_exit_hooks.rs: Rust integration test

Docs: expanded features.md and keybindings.md with new capabilities
2026-04-18 00:41:21 +05:30

549 lines
20 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env pwsh
# =============================================================================
# Test: pane split dimension limits & prompt verification
# Verifies that:
# 1. Creating splits doesn't crash the server (was crashing after ~6 splits)
# 2. The server returns an error when panes are too small to split
# 3. Every NEW pane gets a real pwsh prompt (PS C:\), verified by capture-pane
# 4. Pane count actually increases after each successful split
# 5. All pane dimensions stay >= 2x2 (ConPTY safety)
# =============================================================================
$ErrorActionPreference = 'Continue'
$PSMUX = (Resolve-Path "$PSScriptRoot\..\target\release\psmux.exe" -ErrorAction SilentlyContinue).Path
if (-not $PSMUX) { $PSMUX = (Resolve-Path "$PSScriptRoot\..\target\release\tmux.exe" -ErrorAction SilentlyContinue).Path }
if (-not $PSMUX) { Write-Error "psmux binary not found"; exit 1 }
$totalTests = 0
$passedTests = 0
$failedTests = 0
$failures = @()
function Log { param([string]$msg) Write-Host "[$(Get-Date -Format 'HH:mm:ss.fff')] $msg" }
function Pass { param([string]$name, [string]$detail)
$script:totalTests++; $script:passedTests++
Write-Host " [PASS] $name - $detail" -ForegroundColor Green
}
function Fail { param([string]$name, [string]$detail)
$script:totalTests++; $script:failedTests++
$script:failures += "$name : $detail"
Write-Host " [FAIL] $name - $detail" -ForegroundColor Red
}
function Cleanup {
try { & $PSMUX kill-server 2>&1 | Out-Null } catch {}
Start-Sleep -Seconds 1
try { Get-Process psmux -ErrorAction SilentlyContinue | Stop-Process -Force } catch {}
try { Get-Process tmux -ErrorAction SilentlyContinue | Stop-Process -Force } catch {}
try { Get-Process pmux -ErrorAction SilentlyContinue | Stop-Process -Force } catch {}
Start-Sleep -Milliseconds 500
}
function Get-PaneCount {
param([string]$Session)
$panes = & $PSMUX list-panes -t $Session 2>&1 | Out-String
$lines = ($panes -split "`n") | Where-Object { $_ -match '\S' }
return $lines.Count
}
function Wait-Prompt {
param([string]$Target, [int]$Timeout = 15000, [switch]$Relaxed)
$sw = [System.Diagnostics.Stopwatch]::StartNew()
while ($sw.ElapsedMilliseconds -lt $Timeout) {
try {
$cap = & $PSMUX capture-pane -t $Target -p 2>&1 | Out-String
if ($cap -match "PS [A-Z]:\\") {
return @{ Found = $true; ElapsedMs = $sw.ElapsedMilliseconds; Output = $cap }
}
# In very small panes the "PS " prefix wraps off screen; accept a trailing ">"
if ($Relaxed -and $cap -match ">\s*$") {
return @{ Found = $true; ElapsedMs = $sw.ElapsedMilliseconds; Output = $cap }
}
} catch {}
Start-Sleep -Milliseconds 200
}
$finalCap = ""
try { $finalCap = & $PSMUX capture-pane -t $Target -p 2>&1 | Out-String } catch {}
return @{ Found = $false; ElapsedMs = $sw.ElapsedMilliseconds; Output = $finalCap }
}
function Check-ServerAlive {
param([string]$Session)
& $PSMUX has-session -t $Session 2>&1 | Out-Null
return $LASTEXITCODE -eq 0
}
# Send a command into a pane via send-keys, wait, then capture-pane and check
# that the expected output string appears in the pane content.
function Run-And-Verify {
param(
[string]$Target, # e.g. "split4:1.2"
[string]$Command, # e.g. "echo HELLO_MARKER"
[string]$Expected, # regex to match in captured output, e.g. "HELLO_MARKER"
[int]$Timeout = 10000
)
# Send the command + Enter
& $PSMUX send-keys -t $Target "$Command" Enter 2>&1 | Out-Null
# Poll capture-pane until the expected output appears
$sw = [System.Diagnostics.Stopwatch]::StartNew()
while ($sw.ElapsedMilliseconds -lt $Timeout) {
Start-Sleep -Milliseconds 300
try {
$cap = & $PSMUX capture-pane -t $Target -p 2>&1 | Out-String
if ($cap -match $Expected) {
return @{ Found = $true; ElapsedMs = $sw.ElapsedMilliseconds; Output = $cap }
}
} catch {}
}
$finalCap = ""
try { $finalCap = & $PSMUX capture-pane -t $Target -p 2>&1 | Out-String } catch {}
return @{ Found = $false; ElapsedMs = $sw.ElapsedMilliseconds; Output = $finalCap }
}
Log "Using: $PSMUX"
Write-Host ""
# =============================================================================
# TEST 1: Repeated vertical splits — verify each NEW pane
# =============================================================================
Write-Host ("=" * 60)
Log "TEST 1: Repeated vertical splits - verify NEW pane creation"
Write-Host ("=" * 60)
Cleanup
& $PSMUX new-session -d -s split1 2>&1 | Out-Null
Start-Sleep -Seconds 3
$r = Wait-Prompt "split1:0"
if ($r.Found) { Pass "Initial prompt" "$($r.ElapsedMs)ms" }
else { Fail "Initial prompt" "No PS prompt in initial window" }
$maxSplits = 15
$successfulSplits = 0
$refusedSplits = 0
for ($i = 1; $i -le $maxSplits; $i++) {
$panesBefore = Get-PaneCount "split1"
$out = & $PSMUX split-window -t split1 -v 2>&1 | Out-String
Start-Sleep -Milliseconds 500
if (-not (Check-ServerAlive "split1")) {
Fail "Server alive (vsplit $i)" "SERVER CRASHED after vsplit $i!"
break
}
$panesAfter = Get-PaneCount "split1"
# Check: did the server return an error?
if ($out -match "too small|error|no space") {
$refusedSplits++
# Verify pane count did NOT increase
if ($panesAfter -eq $panesBefore) {
Pass "Vsplit $i refused" "correctly refused ($($out.Trim())), panes=$panesAfter"
} else {
Fail "Vsplit $i refused but created" "error returned but pane count went $panesBefore -> $panesAfter"
}
continue
}
# No error - split should have succeeded
if ($panesAfter -le $panesBefore) {
Fail "Vsplit $i no new pane" "no error but pane count unchanged ($panesBefore -> $panesAfter)"
continue
}
$successfulSplits++
$newPaneIdx = $panesAfter - 1
# KEY CHECK: verify the NEW pane has a PS prompt
$r = Wait-Prompt "split1:0.$newPaneIdx"
if ($r.Found) {
Pass "Vsplit $i new pane prompt" "pane $newPaneIdx prompt in $($r.ElapsedMs)ms (panes=$panesAfter)"
} else {
# Also check if any pane has the prompt — maybe active pane changed
$anyPrompt = $false
for ($p = 0; $p -lt $panesAfter; $p++) {
$rc = Wait-Prompt "split1:0.$p" 3000
if ($rc.Found -and $p -eq $newPaneIdx) { $anyPrompt = $true; break }
}
if ($anyPrompt) {
Pass "Vsplit $i new pane prompt (retry)" "pane $newPaneIdx found on retry"
} else {
Fail "Vsplit $i new pane prompt" "pane $newPaneIdx has NO PS prompt (output: $($r.Output.Substring(0, [Math]::Min(80, $r.Output.Length))))"
}
}
}
if (Check-ServerAlive "split1") {
Pass "Server survived vsplits" "$successfulSplits created, $refusedSplits refused"
} else {
Fail "Server survived vsplits" "Server died"
}
# =============================================================================
# TEST 2: Repeated horizontal splits
# =============================================================================
Write-Host ""
Write-Host ("=" * 60)
Log "TEST 2: Repeated horizontal splits - verify NEW pane creation"
Write-Host ("=" * 60)
Cleanup
& $PSMUX new-session -d -s split2 2>&1 | Out-Null
Start-Sleep -Seconds 3
$r = Wait-Prompt "split2:0"
if ($r.Found) { Pass "Initial prompt (hsplit)" "$($r.ElapsedMs)ms" }
else { Fail "Initial prompt (hsplit)" "No PS prompt" }
$successfulSplits = 0
$refusedSplits = 0
for ($i = 1; $i -le $maxSplits; $i++) {
$panesBefore = Get-PaneCount "split2"
$out = & $PSMUX split-window -t split2 -h 2>&1 | Out-String
Start-Sleep -Milliseconds 500
if (-not (Check-ServerAlive "split2")) {
Fail "Server alive (hsplit $i)" "SERVER CRASHED!"
break
}
$panesAfter = Get-PaneCount "split2"
if ($out -match "too small|error|no space") {
$refusedSplits++
if ($panesAfter -eq $panesBefore) {
Pass "Hsplit $i refused" "correctly refused, panes=$panesAfter"
} else {
Fail "Hsplit $i refused but created" "error but panes $panesBefore -> $panesAfter"
}
continue
}
if ($panesAfter -le $panesBefore) {
Fail "Hsplit $i no new pane" "no error but panes unchanged ($panesBefore -> $panesAfter)"
continue
}
$successfulSplits++
$newPaneIdx = $panesAfter - 1
$r = Wait-Prompt "split2:0.$newPaneIdx"
if ($r.Found) {
Pass "Hsplit $i new pane prompt" "pane $newPaneIdx in $($r.ElapsedMs)ms (panes=$panesAfter)"
} else {
Fail "Hsplit $i new pane prompt" "pane $newPaneIdx has NO PS prompt"
}
}
if (Check-ServerAlive "split2") {
Pass "Server survived hsplits" "$successfulSplits created, $refusedSplits refused"
} else {
Fail "Server survived hsplits" "Server died"
}
# =============================================================================
# TEST 3: Alternating V/H splits (most realistic user scenario)
# =============================================================================
Write-Host ""
Write-Host ("=" * 60)
Log "TEST 3: Alternating V/H splits - verify every new pane"
Write-Host ("=" * 60)
Cleanup
& $PSMUX new-session -d -s split3 2>&1 | Out-Null
Start-Sleep -Seconds 3
$r = Wait-Prompt "split3:0"
if ($r.Found) { Pass "Initial prompt (alt)" "$($r.ElapsedMs)ms" }
else { Fail "Initial prompt (alt)" "No PS prompt" }
$successfulSplits = 0
$refusedSplits = 0
for ($i = 1; $i -le 20; $i++) {
$dir = if ($i % 2 -eq 1) { "-v" } else { "-h" }
$dirName = if ($i % 2 -eq 1) { "V" } else { "H" }
$panesBefore = Get-PaneCount "split3"
$out = & $PSMUX split-window -t split3 $dir 2>&1 | Out-String
Start-Sleep -Milliseconds 500
if (-not (Check-ServerAlive "split3")) {
Fail "Server alive (alt split $i $dirName)" "SERVER CRASHED!"
break
}
$panesAfter = Get-PaneCount "split3"
if ($out -match "too small|error|no space") {
$refusedSplits++
if ($panesAfter -eq $panesBefore) {
Pass "AltSplit $i ($dirName) refused" "correctly refused, panes=$panesAfter"
} else {
Fail "AltSplit $i ($dirName) refused but created" "error but panes changed"
}
continue
}
if ($panesAfter -le $panesBefore) {
Fail "AltSplit $i ($dirName) no new pane" "no error but panes unchanged"
continue
}
$successfulSplits++
$newPaneIdx = $panesAfter - 1
# Later panes are very small; give extra time for shell startup
# Panes at index >= 5 are tiny (< 15 cols); "PS " wraps off screen so use relaxed matching
$promptTimeout = if ($newPaneIdx -ge 5) { 25000 } else { 15000 }
$useRelaxed = $newPaneIdx -ge 5
$r = Wait-Prompt "split3:0.$newPaneIdx" $promptTimeout -Relaxed:$useRelaxed
if ($r.Found) {
Pass "AltSplit $i ($dirName) new pane prompt" "pane $newPaneIdx in $($r.ElapsedMs)ms (panes=$panesAfter)"
} else {
Fail "AltSplit $i ($dirName) new pane prompt" "pane $newPaneIdx no prompt"
}
}
if (Check-ServerAlive "split3") {
Pass "Server survived alt splits" "$successfulSplits created, $refusedSplits refused"
} else {
Fail "Server survived alt splits" "Server died"
}
# =============================================================================
# TEST 4: Multiple windows + splits (the user's exact scenario)
# =============================================================================
Write-Host ""
Write-Host ("=" * 60)
Log "TEST 4: 5 windows x 3 splits each - verify every new pane"
Write-Host ("=" * 60)
Cleanup
& $PSMUX new-session -d -s split4 2>&1 | Out-Null
Start-Sleep -Seconds 3
$r = Wait-Prompt "split4:0"
if ($r.Found) { Pass "Initial prompt (multi)" "$($r.ElapsedMs)ms" }
else { Fail "Initial prompt (multi)" "No PS prompt" }
$totalPanes = 1
$totalRefused = 0
for ($w = 1; $w -le 4; $w++) {
& $PSMUX new-window -t split4 2>&1 | Out-Null
Start-Sleep -Milliseconds 1000
if (-not (Check-ServerAlive "split4")) {
Fail "Server alive (window $w)" "SERVER CRASHED creating window!"
break
}
$r = Wait-Prompt "split4:$w"
if ($r.Found) {
Pass "Window $w prompt" "$($r.ElapsedMs)ms"
$totalPanes++
} else {
Fail "Window $w prompt" "No prompt"
}
for ($s = 1; $s -le 3; $s++) {
$dir = if ($s % 2 -eq 1) { "-v" } else { "-h" }
$panesBefore = Get-PaneCount "split4"
$out = & $PSMUX split-window -t split4 $dir 2>&1 | Out-String
Start-Sleep -Milliseconds 500
if (-not (Check-ServerAlive "split4")) {
Fail "Server alive (win $w split $s)" "SERVER CRASHED!"
break
}
$panesAfter = Get-PaneCount "split4"
if ($out -match "too small|error|no space") {
$totalRefused++
Log " Win $w split $s refused (expected)"
continue
}
if ($panesAfter -le $panesBefore) {
Fail "Win$w Split$s no new pane" "no error but panes unchanged"
continue
}
$totalPanes++
$newPaneIdx = $panesAfter - 1
$r = Wait-Prompt "split4:$w.$newPaneIdx"
if ($r.Found) {
Pass "Win$w Split$s new pane prompt" "pane $newPaneIdx in $($r.ElapsedMs)ms"
} else {
Fail "Win$w Split$s new pane prompt" "pane $newPaneIdx has no prompt"
}
}
}
if (Check-ServerAlive "split4") {
Pass "Server survived multi-window" "$totalPanes panes created, $totalRefused refused"
} else {
Fail "Server survived multi-window" "Server died"
}
# =============================================================================
# TEST 5: Verify all pane dimensions are >= 2x2 (ConPTY safety)
# =============================================================================
Write-Host ""
Write-Host ("=" * 60)
Log "TEST 5: Verify all pane dimensions >= 2x2"
Write-Host ("=" * 60)
if (Check-ServerAlive "split4") {
for ($w = 0; $w -le 4; $w++) {
& $PSMUX select-window -t "split4:$w" 2>&1 | Out-Null
Start-Sleep -Milliseconds 300
$panes = & $PSMUX list-panes -t split4 2>&1 | Out-String
$paneLines = ($panes -split "`n") | Where-Object { $_ -match '\S' }
foreach ($line in $paneLines) {
if ($line -match '\[(\d+)x(\d+)\]') {
$cols = [int]$Matches[1]
$rows = [int]$Matches[2]
if ($cols -lt 2 -or $rows -lt 2) {
Fail "Pane dim win$w" "DANGEROUS dimensions: ${cols}x${rows} - ConPTY will crash! ($line)"
} else {
Pass "Pane dim win$w" "${cols}x${rows}"
}
}
}
}
} else {
Fail "Dimension check" "Server not alive"
}
# =============================================================================
# TEST 6: Verify exit code on refused split
# =============================================================================
Write-Host ""
Write-Host ("=" * 60)
Log "TEST 6: Exit code on refused split"
Write-Host ("=" * 60)
if (Check-ServerAlive "split4") {
# Try to split a pane that should be too small (window 0 has been split multiple times)
& $PSMUX select-window -t "split4:0" 2>&1 | Out-Null
Start-Sleep -Milliseconds 300
# Try to split the already-split panes — at least one should be too small
# Force split into a pane we know is already at minimum
$out = & $PSMUX split-window -t split4 -v 2>&1 | Out-String
$exitCode = $LASTEXITCODE
if ($out -match "too small") {
if ($exitCode -ne 0) {
Pass "Exit code on refuse" "exit=$exitCode, got error: $($out.Trim())"
} else {
Pass "Exit code on refuse" "exit=$exitCode (0 is acceptable), error: $($out.Trim())"
}
} else {
# Split might have succeeded if there's still room
Pass "Exit code test" "split succeeded (pane had room), exit=$exitCode"
}
} else {
Fail "Exit code test" "Server not alive"
}
# =============================================================================
# TEST 7: Run commands in every pane and verify output
# Creates a fresh session with 3 windows × 2 splits, then sends echo + ls
# into every single pane and verifies the output appeared via capture-pane.
# =============================================================================
Write-Host ""
Write-Host ("=" * 60)
Log "TEST 7: Run commands in every pane and verify output"
Write-Host ("=" * 60)
Cleanup
& $PSMUX new-session -d -s cmdtest 2>&1 | Out-Null
Start-Sleep -Seconds 3
$r = Wait-Prompt "cmdtest:0"
if ($r.Found) { Pass "Cmdtest initial prompt" "$($r.ElapsedMs)ms" }
else { Fail "Cmdtest initial prompt" "No PS prompt" }
# Create 2 more windows, each with 2 splits (V then H) = 3 panes per window
for ($w = 1; $w -le 2; $w++) {
& $PSMUX new-window -t cmdtest 2>&1 | Out-Null
Start-Sleep -Milliseconds 1000
$r = Wait-Prompt "cmdtest:$w"
if (-not $r.Found) { Fail "Cmdtest window $w prompt" "No prompt"; continue }
& $PSMUX split-window -t cmdtest -v 2>&1 | Out-Null
Start-Sleep -Milliseconds 500
& $PSMUX split-window -t cmdtest -h 2>&1 | Out-Null
Start-Sleep -Milliseconds 500
}
# Also split the first window
& $PSMUX select-window -t "cmdtest:0" 2>&1 | Out-Null
Start-Sleep -Milliseconds 300
& $PSMUX split-window -t cmdtest -v 2>&1 | Out-Null
Start-Sleep -Milliseconds 500
# Wait for all panes to settle
Start-Sleep -Seconds 2
# Now iterate every window and every pane, run two commands:
# 1) echo MARKER_<win>_<pane> — verify the unique marker appears
# 2) Get-ChildItem env:COMPUTERNAME — verify ls-like command works
for ($w = 0; $w -le 2; $w++) {
& $PSMUX select-window -t "cmdtest:$w" 2>&1 | Out-Null
Start-Sleep -Milliseconds 300
$paneCount = Get-PaneCount "cmdtest"
for ($p = 0; $p -lt $paneCount; $p++) {
$target = "cmdtest:$w.$p"
$marker = "PANE_OK_${w}_${p}"
# First wait for the prompt to appear in this pane
$pr = Wait-Prompt $target 8000
if (-not $pr.Found) {
Fail "Pane $target prompt" "No PS prompt before running command"
continue
}
# TEST 7a: echo a unique marker and verify it appears
$r = Run-And-Verify -Target $target -Command "echo $marker" -Expected $marker -Timeout 8000
if ($r.Found) {
Pass "echo in $target" "marker appeared in $($r.ElapsedMs)ms"
} else {
$snippet = if ($r.Output.Length -gt 80) { $r.Output.Substring(0, 80) } else { $r.Output }
Fail "echo in $target" "marker '$marker' not found (captured: $snippet)"
}
# TEST 7b: run Get-ChildItem and verify directory listing output
$r = Run-And-Verify -Target $target -Command "Get-ChildItem env:COMPUTERNAME" -Expected "COMPUTERNAME" -Timeout 8000
if ($r.Found) {
Pass "ls in $target" "Get-ChildItem output in $($r.ElapsedMs)ms"
} else {
$snippet = if ($r.Output.Length -gt 80) { $r.Output.Substring(0, 80) } else { $r.Output }
Fail "ls in $target" "COMPUTERNAME not found (captured: $snippet)"
}
}
}
if (Check-ServerAlive "cmdtest") {
Pass "Server survived cmd test" "all command executions completed"
} else {
Fail "Server survived cmd test" "Server died"
}
# =============================================================================
# CLEANUP & SUMMARY
# =============================================================================
Write-Host ""
Cleanup
Write-Host ("=" * 60)
$color = if ($failedTests -eq 0) { "Green" } else { "Red" }
Write-Host "RESULTS: $passedTests passed, $failedTests failed, $totalTests total" -ForegroundColor $color
if ($failures.Count -gt 0) {
Write-Host "Failures:" -ForegroundColor Red
$failures | ForEach-Object { Write-Host " - $_" -ForegroundColor Red }
}
Write-Host ("=" * 60)
exit $failedTests