psmux 3.3.4

Terminal multiplexer for Windows - tmux alternative for PowerShell and Windows Terminal
# Issue #226: send-keys C-/ and C-o produce identical bytes (0x0F),
# making them indistinguishable in send-keys. tmux sends 0x1F for C-/
# and 0x0F only for C-o. This test exercises the real send-keys path
# end-to-end via the live psmux binary.

$ErrorActionPreference = "Continue"
$PSMUX = (Get-Command psmux -EA Stop).Source
$SESSION = "test_issue226"
$psmuxDir = "$env:USERPROFILE\.psmux"
$script:TestsPassed = 0
$script:TestsFailed = 0

function Write-Pass($msg) { Write-Host "  [PASS] $msg" -ForegroundColor Green; $script:TestsPassed++ }
function Write-Fail($msg) { Write-Host "  [FAIL] $msg" -ForegroundColor Red;   $script:TestsFailed++ }

function Cleanup {
    & $PSMUX kill-session -t $SESSION 2>&1 | Out-Null
    Start-Sleep -Milliseconds 500
    Remove-Item "$psmuxDir\$SESSION.*" -Force -EA SilentlyContinue
}

# Helper: send a key string, then send the printable token "PROBE" + Enter.
# capture-pane will show the echoed control byte (e.g. ^O, ^_) immediately
# before "PROBE", so we can identify which raw byte was emitted.
function Probe-Key {
    param([string]$Key)
    & $PSMUX send-keys -t $SESSION 'clear' Enter | Out-Null
    Start-Sleep -Milliseconds 700
    & $PSMUX send-keys -t $SESSION $Key | Out-Null
    Start-Sleep -Milliseconds 200
    & $PSMUX send-keys -t $SESSION 'PROBE' Enter | Out-Null
    Start-Sleep -Seconds 1
    return (& $PSMUX capture-pane -t $SESSION -p 2>&1 | Out-String)
}

Cleanup
& $PSMUX new-session -d -s $SESSION
Start-Sleep -Seconds 3
& $PSMUX has-session -t $SESSION 2>$null
if ($LASTEXITCODE -ne 0) { Write-Fail "session creation failed"; exit 1 }

Write-Host "`n=== Issue #226: send-keys C-/ vs C-o parity ===" -ForegroundColor Cyan

# --- Test 1: C-o still produces ^O ---
Write-Host "`n[Test 1] C-o produces ^O (0x0F)" -ForegroundColor Yellow
$capO = Probe-Key 'C-o'
if ($capO -match '\^O') { Write-Pass "C-o renders as ^O in pane" }
else { Write-Fail "C-o did not render as ^O. Pane: $capO" }

# --- Test 2: C-/ must NOT produce ^O ---
Write-Host "`n[Test 2] C-/ does NOT produce ^O" -ForegroundColor Yellow
$capSlash = Probe-Key 'C-/'
if ($capSlash -notmatch '\^O') {
    Write-Pass "C-/ no longer collapses to ^O (bug #226 fixed)"
} else {
    Write-Fail "BUG #226 STILL PRESENT: C-/ rendered as ^O. Pane: $capSlash"
}

# --- Test 3: C-/ produces ^_ (0x1F) per tmux parity ---
Write-Host "`n[Test 3] C-/ produces ^_ (0x1F) like tmux" -ForegroundColor Yellow
# PowerShell echoes 0x1F as ^_ in the prompt buffer.
if ($capSlash -match '\^_') {
    Write-Pass "C-/ renders as ^_ matching tmux behavior"
} else {
    # Some shells swallow 0x1F silently. Accept either: bytes were not ^O,
    # AND the captured output differs from C-o's output.
    if ($capSlash -ne $capO) {
        Write-Pass "C-/ output differs from C-o output (bytes are distinct)"
    } else {
        Write-Fail "C-/ produced the same pane content as C-o. Pane: $capSlash"
    }
}

# --- Test 4: Direct TCP send-keys verifies server-side path ---
Write-Host "`n[Test 4] TCP send-keys path produces distinct bytes for C-/ vs C-o" -ForegroundColor Yellow
function Send-Tcp {
    param([string]$Cmd)
    $port = (Get-Content "$psmuxDir\$SESSION.port" -Raw).Trim()
    $key  = (Get-Content "$psmuxDir\$SESSION.key"  -Raw).Trim()
    $tcp  = [System.Net.Sockets.TcpClient]::new("127.0.0.1", [int]$port)
    $tcp.NoDelay = $true
    $stream = $tcp.GetStream()
    $w = [System.IO.StreamWriter]::new($stream)
    $r = [System.IO.StreamReader]::new($stream)
    $w.Write("AUTH $key`n"); $w.Flush(); $null = $r.ReadLine()
    $w.Write("$Cmd`n"); $w.Flush()
    $stream.ReadTimeout = 3000
    try { $resp = $r.ReadLine() } catch { $resp = "" }
    $tcp.Close()
    return $resp
}
& $PSMUX send-keys -t $SESSION 'clear' Enter | Out-Null
Start-Sleep -Milliseconds 700
$null = Send-Tcp "send-keys C-/"
Start-Sleep -Milliseconds 200
& $PSMUX send-keys -t $SESSION 'TCPMARK1' Enter | Out-Null
Start-Sleep -Seconds 1
$capTcpSlash = & $PSMUX capture-pane -t $SESSION -p 2>&1 | Out-String

& $PSMUX send-keys -t $SESSION 'clear' Enter | Out-Null
Start-Sleep -Milliseconds 700
$null = Send-Tcp "send-keys C-o"
Start-Sleep -Milliseconds 200
& $PSMUX send-keys -t $SESSION 'TCPMARK2' Enter | Out-Null
Start-Sleep -Seconds 1
$capTcpO = & $PSMUX capture-pane -t $SESSION -p 2>&1 | Out-String

# C-o on TCP path should still produce ^O.
if ($capTcpO -match '\^O') { Write-Pass "TCP send-keys C-o renders as ^O" }
else { Write-Fail "TCP send-keys C-o did not render as ^O" }

# C-/ on TCP path must NOT match ^O.
if ($capTcpSlash -notmatch '\^O') {
    Write-Pass "TCP send-keys C-/ does not collapse to ^O"
} else {
    Write-Fail "TCP path: C-/ still collapses to ^O"
}

# === Win32 TUI VISUAL VERIFICATION ===
Write-Host "`n$('=' * 60)" -ForegroundColor Cyan
Write-Host "Win32 TUI VISUAL VERIFICATION" -ForegroundColor Cyan
Write-Host ('=' * 60) -ForegroundColor Cyan

$SESSION_TUI = "test_issue226_tui"
& $PSMUX kill-session -t $SESSION_TUI 2>&1 | Out-Null
Remove-Item "$psmuxDir\$SESSION_TUI.*" -Force -EA SilentlyContinue
$proc = Start-Process -FilePath $PSMUX -ArgumentList "new-session","-s",$SESSION_TUI -PassThru
Start-Sleep -Seconds 4

& $PSMUX send-keys -t $SESSION_TUI 'clear' Enter | Out-Null
Start-Sleep -Milliseconds 700
& $PSMUX send-keys -t $SESSION_TUI 'C-/' | Out-Null
Start-Sleep -Milliseconds 200
& $PSMUX send-keys -t $SESSION_TUI 'TUIMARK' Enter | Out-Null
Start-Sleep -Seconds 1
$capTui = & $PSMUX capture-pane -t $SESSION_TUI -p 2>&1 | Out-String
if ($capTui -notmatch '\^O') { Write-Pass "TUI: send-keys C-/ does not collapse to ^O" }
else { Write-Fail "TUI: send-keys C-/ still collapses to ^O" }

& $PSMUX kill-session -t $SESSION_TUI 2>&1 | Out-Null
try { Stop-Process -Id $proc.Id -Force -EA SilentlyContinue } catch {}
Remove-Item "$psmuxDir\$SESSION_TUI.*" -Force -EA SilentlyContinue

Cleanup

Write-Host "`n=== Results ===" -ForegroundColor Cyan
Write-Host "  Passed: $($script:TestsPassed)" -ForegroundColor Green
Write-Host "  Failed: $($script:TestsFailed)" -ForegroundColor $(if ($script:TestsFailed -gt 0) { "Red" } else { "Green" })
exit $script:TestsFailed