tmux-cli-test
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesetmux CLI Testing
tmux CLI 测试
Test CLI applications by running them in tmux sessions, waiting for conditions, sending input, and asserting on output. Never sleep - always wait on a condition.
通过在tmux会话中运行命令、等待条件、发送输入并对输出进行断言,来测试CLI应用。绝不使用sleep,始终基于条件等待。
Helpers
辅助工具
Source the helper script for all primitives:
bash
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh引入辅助脚本以使用所有基础功能:
bash
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.shQuick Reference
速查手册
| Function | Purpose |
|---|---|
| Launch command in detached tmux session |
| Kill session |
| Check if session is running |
| Get pane text |
| Get pane text with ANSI codes |
| Poll until text appears |
| Poll until regex matches |
| Poll until text disappears |
| Poll until process exits |
| Send keys (tmux key names) |
| Type literal text |
| Assert text present |
| Assert text absent |
| Assert regex matches |
| Send then wait |
| Full lifecycle test |
| 函数 | 用途 |
|---|---|
| 在后台tmux会话中启动命令 |
| 终止会话 |
| 检查会话是否在运行 |
| 获取面板文本内容 |
| 获取包含ANSI代码的面板文本 |
| 轮询直到指定文本出现 |
| 轮询直到正则表达式匹配成功 |
| 轮询直到指定文本消失 |
| 轮询直到进程退出 |
| 发送按键(使用tmux按键名称) |
| 输入字面文本内容 |
| 断言指定文本存在 |
| 断言指定文本不存在 |
| 断言正则表达式匹配成功 |
| 发送按键后等待指定文本出现 |
| 完整生命周期测试 |
Configuration
配置
Override defaults before calling functions:
bash
TMUX_TEST_POLL_INTERVAL=0.3 # seconds between polls
TMUX_TEST_TIMEOUT=30 # max wait seconds
TMUX_TEST_WIDTH=120 # terminal columns
TMUX_TEST_HEIGHT=30 # terminal rows在调用函数前可覆盖默认值:
bash
TMUX_TEST_POLL_INTERVAL=0.3 # 轮询间隔(秒)
TMUX_TEST_TIMEOUT=30 # 最大等待时间(秒)
TMUX_TEST_WIDTH=120 # 终端列数
TMUX_TEST_HEIGHT=30 # 终端行数Workflow
工作流程
Every test follows this pattern:
1. Start session with command
2. Wait for ready signal (text appearing)
3. Interact (send keys, wait for responses)
4. Assert on captured output
5. Kill session每个测试都遵循以下模式:
1. 使用命令启动会话
2. 等待就绪信号(指定文本出现)
3. 进行交互(发送按键、等待响应)
4. 对捕获的输出进行断言
5. 终止会话Minimal Example
最简示例
bash
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
tmux_start "test-help" "./crates/target/debug/gpu --help"
tmux_wait_for "test-help" "Usage:" 10
tmux_assert_contains "test-help" "run"
tmux_assert_contains "test-help" "dashboard"
tmux_kill "test-help"bash
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
tmux_start "test-help" "./crates/target/debug/gpu --help"
tmux_wait_for "test-help" "Usage:" 10
tmux_assert_contains "test-help" "run"
tmux_assert_contains "test-help" "dashboard"
tmux_kill "test-help"Using tmux_test for Lifecycle
使用tmux_test进行生命周期管理
bash
test_help_page() {
local s="$1"
tmux_assert_contains "$s" "run" "help shows run command"
tmux_assert_contains "$s" "dashboard" "help shows dashboard command"
tmux_assert_not_contains "$s" "ERROR" "no errors in help"
}
tmux_test "help-test" "./crates/target/debug/gpu --help" "Usage:" test_help_pagebash
test_help_page() {
local s="$1"
tmux_assert_contains "$s" "run" "帮助信息显示run命令"
tmux_assert_contains "$s" "dashboard" "帮助信息显示dashboard命令"
tmux_assert_not_contains "$s" "ERROR" "帮助信息中无错误"
}
tmux_test "help-test" "./crates/target/debug/gpu --help" "Usage:" test_help_pageGPU CLI Patterns
GPU CLI 测试模式
Testing the Dashboard (TUI)
测试仪表板(TUI)
bash
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
TMUX_TEST_WIDTH=140
TMUX_TEST_HEIGHT=35
GPU_BIN="./crates/target/debug/gpu"
tmux_start "dash" "$GPU_BIN dashboard"
tmux_wait_for "dash" "Pods" 15bash
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
TMUX_TEST_WIDTH=140
TMUX_TEST_HEIGHT=35
GPU_BIN="./crates/target/debug/gpu"
tmux_start "dash" "$GPU_BIN dashboard"
tmux_wait_for "dash" "Pods" 15Verify panels rendered
验证面板已渲染
tmux_assert_contains "dash" "Pods" "pods panel visible"
tmux_assert_contains "dash" "Jobs" "jobs panel visible"
tmux_assert_contains "dash" "Pods" "Pods面板可见"
tmux_assert_contains "dash" "Jobs" "Jobs面板可见"
Navigate with keys
使用键盘导航
tmux_send "dash" j
tmux_send "dash" j
tmux_wait_for "dash" "▶" 5 # selection cursor
tmux_send "dash" j
tmux_send "dash" j
tmux_wait_for "dash" "▶" 5 # 选择光标
Switch panel
切换面板
tmux_send "dash" Tab
tmux_wait_for_regex "dash" "Jobs.*selected" 5
tmux_send "dash" Tab
tmux_wait_for_regex "dash" "Jobs.*selected" 5
Open help overlay
打开帮助浮层
tmux_send "dash" '?'
tmux_wait_for "dash" "Help" 5
tmux_assert_contains "dash" "Keybindings" "help shows keybindings"
tmux_send "dash" '?'
tmux_wait_for "dash" "Help" 5
tmux_assert_contains "dash" "Keybindings" "帮助信息显示快捷键"
Close help
关闭帮助
tmux_send "dash" Escape
tmux_wait_gone "dash" "Keybindings" 5
tmux_send "dash" Escape
tmux_wait_gone "dash" "Keybindings" 5
Quit
退出
tmux_send "dash" q
tmux_wait_exit "dash" 5
undefinedtmux_send "dash" q
tmux_wait_exit "dash" 5
undefinedTesting Interactive Prompts
测试交互式提示
bash
tmux_start "init" "$GPU_BIN init"
tmux_wait_for "init" "project" 10bash
tmux_start "init" "$GPU_BIN init"
tmux_wait_for "init" "project" 10Type project name
输入项目名称
tmux_type "init" "my-test-project"
tmux_send "init" Enter
tmux_type "init" "my-test-project"
tmux_send "init" Enter
Wait for next prompt
等待下一个提示
tmux_wait_for "init" "GPU" 10
tmux_wait_for "init" "GPU" 10
Select GPU with arrow keys
使用方向键选择GPU
tmux_send "init" j j Enter
tmux_wait_for "init" "provider" 10
tmux_kill "init"
undefinedtmux_send "init" j j Enter
tmux_wait_for "init" "provider" 10
tmux_kill "init"
undefinedTesting Command Output
测试命令输出
bash
tmux_start "status" "$GPU_BIN status"
tmux_wait_for_regex "status" "(No active|Pod)" 10
FRAME=$(tmux_capture "status")
if echo "$FRAME" | grep -q "No active"; then
echo "No pods running - expected for cold test"
elif echo "$FRAME" | grep -q "Pod"; then
tmux_assert_matches "status" "Pod.*READY\|ACTIVE" "pod in valid state"
fi
tmux_wait_exit "status" 15bash
tmux_start "status" "$GPU_BIN status"
tmux_wait_for_regex "status" "(No active|Pod)" 10
FRAME=$(tmux_capture "status")
if echo "$FRAME" | grep -q "No active"; then
echo "无Pod运行 - 冷启动测试符合预期"
elif echo "$FRAME" | grep -q "Pod"; then
tmux_assert_matches "status" "Pod.*READY\|ACTIVE" "Pod处于有效状态"
fi
tmux_wait_exit "status" 15Testing Error Handling
测试错误处理
bash
tmux_start "bad-cmd" "$GPU_BIN run --nonexistent-flag"
tmux_wait_for_regex "bad-cmd" "error|Error|unknown" 10
tmux_assert_not_contains "bad-cmd" "panic" "no panics on bad input"
tmux_wait_exit "bad-cmd" 5bash
tmux_start "bad-cmd" "$GPU_BIN run --nonexistent-flag"
tmux_wait_for_regex "bad-cmd" "error|Error|unknown" 10
tmux_assert_not_contains "bad-cmd" "panic" "非法输入时无崩溃"
tmux_wait_exit "bad-cmd" 5Testing Long-Running Commands with Ctrl-C
使用Ctrl-C测试长时间运行的命令
bash
tmux_start "run-job" "$GPU_BIN run python -c 'import time; time.sleep(3600)'"
tmux_wait_for "run-job" "Running" 30bash
tmux_start "run-job" "$GPU_BIN run python -c 'import time; time.sleep(3600)'"
tmux_wait_for "run-job" "Running" 30Interrupt
中断命令
tmux_send "run-job" C-c
tmux_wait_for_regex "run-job" "cancel|interrupt|stopped" 10
tmux_wait_exit "run-job" 10
undefinedtmux_send "run-job" C-c
tmux_wait_for_regex "run-job" "cancel|interrupt|stopped" 10
tmux_wait_exit "run-job" 10
undefinedDocker Containers
Docker容器测试
For testing CLI apps running inside Docker containers (e.g., FTR testing), source the Docker helpers:
bash
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"若要测试运行在Docker容器内的CLI应用(如FTR测试),引入Docker辅助脚本:
bash
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"TMUX_DOCKER_SESSION="test" # default, override if needed
TMUX_DOCKER_SESSION="test" # 默认值,可按需覆盖
Set `TMUX_DOCKER_CONTAINER` once, then all `docker_tmux_*` calls route through `docker exec` to that container. Same API as the local helpers but without session/container args:
| Function | Purpose |
|----------|---------|
| `docker_tmux_send <keys...>` | Send tmux keys |
| `docker_tmux_type <text>` | Type literal text |
| `docker_tmux_capture` | Get pane text |
| `docker_tmux_capture_ansi` | Get pane text with ANSI codes |
| `docker_tmux_wait_for <text> [timeout]` | Poll until text appears |
| `docker_tmux_wait_regex <pattern> [timeout]` | Poll until regex matches |
| `docker_tmux_wait_gone <text> [timeout]` | Poll until text disappears |
| `docker_tmux_assert_contains <text> [label]` | Assert text present |
| `docker_tmux_assert_not_contains <text> [label]` | Assert text absent |
| `docker_tmux_assert_matches <pattern> [label]` | Assert regex matches |
| `docker_tmux_send_and_wait <keys> <text> [timeout]` | Send then wait |
设置一次`TMUX_DOCKER_CONTAINER`后,所有`docker_tmux_*`调用将通过`docker exec`路由到该容器。API与本地辅助工具相同,但无需会话/容器参数:
| 函数 | 用途 |
|----------|---------|
| `docker_tmux_send <keys...>` | 发送tmux按键 |
| `docker_tmux_type <text>` | 输入字面文本 |
| `docker_tmux_capture` | 获取面板文本 |
| `docker_tmux_capture_ansi` | 获取包含ANSI代码的面板文本 |
| `docker_tmux_wait_for <text> [timeout]` | 轮询直到文本出现 |
| `docker_tmux_wait_regex <pattern> [timeout]` | 轮询直到正则表达式匹配成功 |
| `docker_tmux_wait_gone <text> [timeout]` | 轮询直到文本消失 |
| `docker_tmux_assert_contains <text> [label]` | 断言文本存在 |
| `docker_tmux_assert_not_contains <text> [label]` | 断言文本不存在 |
| `docker_tmux_assert_matches <pattern> [label]` | 断言正则表达式匹配成功 |
| `docker_tmux_send_and_wait <keys> <text> [timeout]` | 发送按键后等待文本出现 |Docker Example
Docker示例
bash
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"
docker_tmux_send "gpu dashboard" Enter
docker_tmux_wait_for "Pods" 15
docker_tmux_capture
docker_tmux_send j
docker_tmux_send "?"
docker_tmux_wait_for "Help" 5
docker_tmux_assert_contains "Keybindings" "help shows keybindings"
docker_tmux_send Escape
docker_tmux_wait_gone "Help" 5
docker_tmux_send qbash
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"
docker_tmux_send "gpu dashboard" Enter
docker_tmux_wait_for "Pods" 15
docker_tmux_capture
docker_tmux_send j
docker_tmux_send "?"
docker_tmux_wait_for "Help" 5
docker_tmux_assert_contains "Keybindings" "帮助信息显示快捷键"
docker_tmux_send Escape
docker_tmux_wait_gone "Help" 5
docker_tmux_send qWriting Tests Inline
编写内联测试
When no helper script is needed (quick one-off checks), use tmux commands directly:
bash
undefined当不需要辅助脚本时(快速一次性检查),可直接使用tmux命令:
bash
undefinedStart
启动会话
tmux new-session -d -s test -x 120 -y 30 "./crates/target/debug/gpu dashboard"
tmux new-session -d -s test -x 120 -y 30 "./crates/target/debug/gpu dashboard"
Poll for ready (inline wait loop)
轮询等待就绪(内联等待循环)
for i in $(seq 1 100); do
tmux capture-pane -t test -p | grep -q "Pods" && break
sleep 0.3
done
for i in $(seq 1 100); do
tmux capture-pane -t test -p | grep -q "Pods" && break
sleep 0.3
done
Assert
断言验证
FRAME=$(tmux capture-pane -t test -p)
echo "$FRAME" | grep -q "Pods" && echo "PASS" || echo "FAIL"
FRAME=$(tmux capture-pane -t test -p)
echo "$FRAME" | grep -q "Pods" && echo "PASS" || echo "FAIL"
Cleanup
清理
tmux send-keys -t test q
tmux kill-session -t test 2>/dev/null
undefinedtmux send-keys -t test q
tmux kill-session -t test 2>/dev/null
undefinedAnti-Patterns
反模式
| Bad | Good | Why |
|---|---|---|
| | Sleeps are flaky and slow |
| | Condition, not duration |
| Hardcoded binary path | | Easy to switch debug/release |
| No cleanup on failure | | Leftover sessions break next run |
| | Text may not be rendered yet |
Checking | Check display content only | Unicode width != byte length |
| 不良做法 | 正确做法 | 原因 |
|---|---|---|
| | Sleep不可靠且会拖慢测试 |
| | 应基于条件而非时长等待 |
| 硬编码二进制路径 | | 便于切换debug/release版本 |
| 测试失败时不清理 | | 残留会话会影响后续测试运行 |
无超时循环的 | | 文本可能尚未渲染完成 |
检查TUI文本的 | 仅检查显示内容 | Unicode宽度不等于字节长度 |
Debugging Failed Tests
调试失败的测试
When a test fails, capture the frame for diagnosis:
bash
undefined测试失败时,捕获界面帧用于诊断:
bash
undefinedCapture what's actually on screen
捕获当前屏幕内容
tmux_capture "session-name"
tmux_capture "session-name"
Save to file for comparison
保存到文件以便对比
tmux_capture_to_file "session-name" "/tmp/failed-frame.txt"
tmux_capture_to_file "session-name" "/tmp/failed-frame.txt"
Capture with colors to verify styling
捕获带颜色的内容以验证样式
tmux_capture_ansi "session-name"
undefinedtmux_capture_ansi "session-name"
undefinedSession Naming Convention
会话命名规范
Use descriptive, prefixed session names to avoid collisions:
gpu-test-dashboard
gpu-test-init
gpu-test-run-basic
gpu-test-status
gpu-test-error-handling使用描述性的前缀会话名称以避免冲突:
gpu-test-dashboard
gpu-test-init
gpu-test-run-basic
gpu-test-status
gpu-test-error-handling