02c26cfb86
scripts/run_unit_tests.sh
用法注释里写的是「bash run_unit_tests.sh src/<pkg>/test/test_x.py -x」,但实现是
`pytest "${test_dirs[@]}" "$@"` —— 传进来的路径只是被**追加**到「全部测试目录」
后面,实际仍然跑整仓库。用法示例就是承诺,它不成立会让人以为「我只改了 x,
怎么蹦出别的包的失败」。改成:位置参数里**存在的路径**当作「只跑这些」,
其余(-k / -x / --tb=… 及其取值)原样透传。
docs/HANDBOOK.md §4.3
原文让人逐个包手敲 `python3 -m pytest src/xxx/test`。这条命令在本仓库**会失败**,
而且是两种最容易被误读的失败:
· 少挂一个 src 包 → 收集阶段 ModuleNotFoundError,现象是「0 个测试 / 1 个 error」,
像测试环境坏了;
· 更隐蔽:install/ 里那份**旧的**同名模块会先被找到。实测直接跑
src/arm_sim_gazebo/test 报 `cannot import name 'RESET_GIVE_UP'`,导的是
install/ 里的旧副本,源码里明明有这个符号 —— 测的根本不是你改的那份。
改为统一用 run_unit_tests.sh(它把 src/* 挂到 PYTHONPATH 最前面),并把这段
原因写进文档。顺带把过期的统计数字更新为实测值:608 → 1118 项,
分项 arm_application 233→547、arm_sim_gazebo 236→400、arm_interfaces 46→78。
实测:src/arm_sim_gazebo/test 直跑 = 2 个收集错误;经运行器 = 400 passed。
单文件 28 passed;-k real_preflight = 28 passed / 1 skipped;
无参数全量 = 1118 passed / 1 skipped。
docs/QUICKSTART.md
「第一次实机」以体检开场(它只读、不需要机械臂能动),并列出四项判据各抓什么,
免得现场跳过它直接起栈。
Co-authored-by: Cursor <cursoragent@cursor.com>
61 lines
2.5 KiB
Bash
Executable File
61 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 跑全仓库的单元测试(纯逻辑:不需要硬件、不需要 Gazebo、不发运动指令)。
|
||
#
|
||
# 为什么要有这个脚本,而不是让人手敲 pytest
|
||
# ----------------------------------------
|
||
# 测试都按**包名**导入(arm_application.* / arm_sim_gazebo.* / arm_kinematics.*),
|
||
# 所以 PYTHONPATH 必须同时包含 src/ 下所有包目录。手敲很容易漏一个:
|
||
# 漏了 src/arm_kinematics,`from arm_application.pipeline_node import ...` 会在
|
||
# 收集阶段就 ModuleNotFoundError —— 现象是"测试跑了 0 个 / 1 个 error",
|
||
# 很容易被读成"测试环境坏了",而不是"少了一个路径"。
|
||
#
|
||
# 用法:
|
||
# bash scripts/run_unit_tests.sh # 全部
|
||
# bash scripts/run_unit_tests.sh -k gauge # 只跑名字带 gauge 的
|
||
# bash scripts/run_unit_tests.sh src/arm_sim_gazebo/test/test_sim_scene.py -x
|
||
#
|
||
# 环境:真机 conda 环境或仿真环境都能跑;缺可选依赖的用例自己会 skip
|
||
#(例如 onnx/ultralytics 只在真机环境有 —— 见各测试里的 pytest.importorskip)。
|
||
set -euo pipefail
|
||
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
PY="${PYTHON:-python3}"
|
||
|
||
# 把所有 src/* 目录挂到 PYTHONPATH 最前面(包名不冲突,顺序无关)。
|
||
paths=()
|
||
for d in "${ROOT}"/src/*/; do
|
||
paths+=("${d%/}")
|
||
done
|
||
export PYTHONPATH="$(IFS=:; echo "${paths[*]}")${PYTHONPATH:+:${PYTHONPATH}}"
|
||
|
||
test_dirs=()
|
||
for d in "${ROOT}"/src/*/test; do
|
||
[ -d "$d" ] && test_dirs+=("$d")
|
||
done
|
||
if [ "${#test_dirs[@]}" -eq 0 ]; then
|
||
echo "没有找到任何测试目录(src/*/test)—— 仓库不完整?" >&2
|
||
exit 1
|
||
fi
|
||
|
||
cd "${ROOT}"
|
||
# 位置参数里**存在的路径**当作"只跑这些",其余(-k / -x / --tb=… 及其取值)
|
||
# 原样透传给 pytest。
|
||
#
|
||
# 为什么要分开:以前是 `pytest "${test_dirs[@]}" "$@"` —— 传进来的路径只是被
|
||
# **追加**到"全部测试目录"后面,于是 `run_unit_tests.sh src/x/test` 实际跑的是
|
||
# **整仓库**。脚本头部的用法示例看起来像"只跑这个文件",实际不是,很容易被读成
|
||
# "我只改了 x,怎么蹦出别的包的失败"。用法示例就是承诺,得让它成立。
|
||
paths=()
|
||
extra=()
|
||
for a in "$@"; do
|
||
if [[ "${a}" != -* && -e "${a}" ]]; then
|
||
paths+=("${a}")
|
||
else
|
||
extra+=("${a}")
|
||
fi
|
||
done
|
||
if [ "${#paths[@]}" -gt 0 ]; then
|
||
exec "${PY}" -m pytest "${paths[@]}" -q "${extra[@]}"
|
||
fi
|
||
exec "${PY}" -m pytest "${test_dirs[@]}" -q "${extra[@]}"
|