Files
arm_dev 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>
2026-09-28 10:42:56 +08:00

61 lines
2.5 KiB
Bash
Executable File
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 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[@]}"