Adora 测试指南
本指南介绍如何在 Adora 工作空间中运行、编写和排查测试。
快速开始(5 分钟验证)
运行这三个命令来验证工作空间是否健康:
# 1. Format check (~5s)
cargo fmt --all -- --check
# 2. Lint (~60s first run, cached after)
cargo clippy --all \
--exclude adora-node-api-python \
--exclude adora-operator-api-python \
--exclude adora-ros2-bridge-python \
-- -D warnings
# 3. Unit + integration tests (~90s first run)
cargo test --all \
--exclude adora-node-api-python \
--exclude adora-operator-api-python \
--exclude adora-ros2-bridge-python
在创建 PR 之前,以下三项必须全部通过。Python 包因需要 maturin 而被排除。
Test Tiers
| Tier | What it covers | 命令 | Speed |
|---|---|---|---|
| Format | Code style | cargo fmt --all -- --check | ~5s |
| Lint | Warnings, correctness | cargo clippy --all ... | ~60s |
| Unit | Individual functions | cargo test --all ... | ~90s |
| CLI | Command parsing, validation | cargo test -p adora-cli | ~5s |
| Integration | 通过环境变量进行节点 I/O | cargo test --test example-tests | ~30s |
| Smoke | Full CLI lifecycle | cargo test --test example-smoke -- --test-threads=1 | ~3min |
| E2E | Multi-dataflow scenarios | cargo test --test ws-cli-e2e -- --ignored --test-threads=1 | ~2min |
| Fault tolerance | Restart policies, timeouts | cargo test --test fault-tolerance-e2e | ~45s |
| Typos | Spelling | 安装 typos-cli,然后运行 typos | ~2s |
Tier Details
单元测试
单元测试使用 #[cfg(test)] 模块,与被测试的代码放在同一文件中。包含测试的主要 crate:
| Crate | Test count | What’s tested |
|---|---|---|
| adora-arrow-convert | ~26 | Arrow 类型的往返转换 |
| adora-cli | ~96 | 命令解析、值解析器、日志过滤、JSON 解析、WebSocket 客户端、集群配置 |
| adora-coordinator | ~24 | WS 控制面/守护进程面、健康检查、并发请求、构件存储、速率限制器、错误信息脱敏 |
| adora-coordinator-store | ~10 | 内存和 redb CRUD、模式版本管理、持久化 |
| adora-core | ~8 | Dataflow descriptor validation |
| adora-daemon | ~2 | Shlex argument parsing |
| adora-node-api | ~10 | 输入跟踪、服务/动作辅助函数(ID 生成、send_service_request/response) |
| adora-log-utils | ~11 | Log parsing utilities |
| adora-message | ~36 | 通用类型、WS 协议、节点/数据 ID、元数据、认证令牌 |
| ros2-bridge | ~30 | ROS2 message/service/action parsing |
运行单个 crate 的测试:
cargo test -p adora-cli
cargo test -p adora-core
cargo test -p adora-arrow-convert
CLI Tests
CLI 测试验证命令解析、参数校验和值解析器,无需实际执行任何命令。它们位于 CLI crate 内的 #[cfg(test)] 模块中。
What’s tested:
- Clap 模式验证(
Args::command().debug_assert()) - 每个子命令的解析(
run、up、down、start、stop、list、logs、build、graph、new、status、inspect top、topic list/hz/echo、node list) - 拒绝未知子命令
--help和--version退出码- 值解析器:
parse_store_spec(协调器存储后端)、parse_window(topic hz 窗口) - Utility functions:
parse_version_from_pip_show
How to run:
cargo test -p adora-cli
如何添加新测试:
添加新的 CLI 子命令或值解析器时,请在同一文件的 #[cfg(test)] 模块中添加对应的测试。对于子命令解析,在 binaries/cli/src/command/mod.rs 中添加 parse_ok 调用。对于值解析器,在定义解析函数的文件中添加测试。
集成测试(节点 I/O)
File: tests/example-tests.rs
这些测试使用预录制的输入运行编译好的节点可执行文件,并将输出与预期基线进行比较。无需协调器或守护进程。
cargo test --test example-tests
How it works:
- 构建并运行节点 crate(例如
rust-dataflow-example-node) - 将
ADORA_TEST_WITH_INPUTS设置为包含定时事件的 JSON 文件 - 设置
ADORA_TEST_NO_OUTPUT_TIME_OFFSET=1以获得确定性输出 - 将 JSONL 输出与
tests/sample-inputs/expected-outputs-*.jsonl进行比较
示例输入/输出文件位于 tests/sample-inputs/ 中。
冒烟测试
File: tests/example-smoke.rs
每个适用的示例都在两种执行模式下进行测试:
- 联网模式(
adora up+adora start --detach+ 轮询 +adora stop+adora down):测试完整的协调器/守护进程 WS 控制面。 - 本地模式(
adora run --stop-after):在进程内运行所有组件,测试单进程数据流路径。
# Must run single-threaded (shared coordinator port)
cargo test --test example-smoke -- --test-threads=1
# Run only networked or local tests
cargo test --test example-smoke smoke_rust -- --test-threads=1
cargo test --test example-smoke smoke_local -- --test-threads=1
还提供了一个 bash 脚本用于快速本地验证:
./scripts/smoke-all.sh # all examples
./scripts/smoke-all.sh --rust-only # Rust examples only
./scripts/smoke-all.sh --python-only # Python examples only
Networked tests (17):
| Test | 示例 | Timeout |
|---|---|---|
smoke_rust_dataflow | rust-dataflow/dataflow.yml | 30s |
smoke_rust_dataflow_dynamic | rust-dataflow/dataflow_dynamic.yml | 30s |
smoke_rust_dataflow_socket | rust-dataflow/dataflow_socket.yml | 30s |
smoke_rust_dataflow_url | rust-dataflow-url/dataflow.yml | 30s |
smoke_benchmark | benchmark/dataflow.yml | 30s |
smoke_log_sink_file | log-sink-file/dataflow.yml | 30s |
smoke_log_sink_alert | log-sink-alert/dataflow.yml | 30s |
smoke_log_sink_tcp | log-sink-tcp/dataflow.yml | 30s |
smoke_python_dataflow | python-dataflow/dataflow.yml | 30s |
smoke_python_async | python-async/dataflow.yaml | 15s |
smoke_python_drain | python-drain/dataflow.yaml | 15s |
smoke_python_log | python-log/dataflow.yaml | 15s |
smoke_python_logging | python-logging/dataflow.yml | 15s |
smoke_python_multiple_arrays | python-multiple-arrays/dataflow.yml | 15s |
smoke_python_concurrent_rw | python-concurrent-rw/dataflow.yml | 15s |
smoke_service_example | service-example/dataflow.yml | 30s |
smoke_action_example | action-example/dataflow.yml | 30s |
Local tests (9):
| Test | 示例 | stop-after |
|---|---|---|
smoke_local_python_dataflow | python-dataflow/dataflow.yml | 30s |
smoke_local_python_async | python-async/dataflow.yaml | 10s |
smoke_local_python_drain | python-drain/dataflow.yaml | 10s |
smoke_local_python_log | python-log/dataflow.yaml | 10s |
smoke_local_python_logging | python-logging/dataflow.yml | 10s |
smoke_local_python_multiple_arrays | python-multiple-arrays/dataflow.yml | 10s |
smoke_local_python_concurrent_rw | python-concurrent-rw/dataflow.yml | 10s |
smoke_local_service_example | service-example/dataflow.yml | 10s |
smoke_local_action_example | action-example/dataflow.yml | 10s |
需要特殊依赖项(摄像头、CUDA、ROS2、C/C++ 工具链、多机部署)的示例不包含在冒烟测试中。
端到端测试(WebSocket CLI)
File: tests/ws-cli-e2e.rs
Two groups:
非忽略(快速): 启动进程内协调器并直接测试 WsSession:
cargo test --test ws-cli-e2e
cli_list_empty—— 空数据流列表cli_status_no_daemon—— 守护进程连接检查cli_stop_nonexistent—— 不存在的数据流返回错误cli_multiple_requests_same_session—— 会话复用
忽略标记(全栈): 使用 adora up 运行真实节点:
cargo test --test ws-cli-e2e -- --ignored --test-threads=1
e2e_start_list_stop—— 启动、列表、停止生命周期e2e_sequential_dataflows—— 两个数据流依次执行
Fault Tolerance Tests
File: tests/fault-tolerance-e2e.rs
这些测试直接使用 Daemon::run_dataflow 测试重启策略和输入超时(无需 CLI)。
cargo test --test fault-tolerance-e2e
Tests:
restart_recovers_from_failure—— 设置了restart_policy: on-failure的节点能够在恐慌后恢复(15秒)max_restarts_limit_reached—— 节点耗尽max_restarts: 2的重启预算(15秒)input_timeout_closes_stale_input—— 当上游停止时触发input_timeout: 2.0s(10秒)
这些测试的数据流 YAML 文件位于 tests/dataflows/ 中。
Coordinator Integration Tests
Files: binaries/coordinator/tests/ws_control_tests.rs, binaries/coordinator/tests/ws_daemon_tests.rs
这些测试启动进程内协调器并测试 WebSocket 控制面/守护进程面。
cargo test -p adora-coordinator
涵盖主题:健康检查、列表/停止/销毁请求、无效 JSON/参数、并发请求、ping/pong、守护进程注册、断开连接清理、错误信息脱敏(不泄露内部错误链)、构件存储的 drop 清理。
CI Pipeline
CI 在推送/PR 到 main 时运行。请参阅 .github/workflows/ci.yml。
fmt ──────────────┐
clippy ────────────┤ (all run in parallel)
test ──────────────┤
typos ─────────────┘
│
e2e (depends on test)
| Job | Runner | What runs |
|---|---|---|
| fmt | ubuntu-latest | cargo fmt --all -- --check |
| clippy | ubuntu-latest | cargo clippy --all ... -- -D warnings |
| test | ubuntu-latest | cargo test --all ...(排除 Python + adora-examples) |
| e2e | ubuntu-latest | example-tests、容错测试、冒烟测试、WS 端到端测试 |
| typos | ubuntu-latest | crate-ci/typos@master |
e2e 任务仅在 test 通过后运行。所有其他任务并行运行。
Writing New Tests
Unit tests
在被测试代码所在的同一文件中添加 #[cfg(test)] 模块:
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_valid_input() {
let result = parse("valid");
assert_eq!(result, expected);
}
}
}
节点的集成测试
使用 adora-node-api 中的集成测试框架。三种方式:
1. setup_integration_testing (recommended)
在节点的 main 函数之前调用,以注入输入并捕获输出:
#![allow(unused)]
fn main() {
#[test]
fn test_main_function() -> eyre::Result<()> {
let events = vec![
TimedIncomingEvent {
time_offset_secs: 0.01,
event: IncomingEvent::Input {
id: "tick".into(),
metadata: None,
data: None,
},
},
TimedIncomingEvent {
time_offset_secs: 0.055,
event: IncomingEvent::Stop,
},
];
let inputs = TestingInput::Input(
IntegrationTestInput::new("node_id".parse().unwrap(), events),
);
let (tx, rx) = flume::unbounded();
let outputs = TestingOutput::ToChannel(tx);
let options = TestingOptions { skip_output_time_offsets: true };
integration_testing::setup_integration_testing(inputs, outputs, options);
crate::main()?;
let outputs = rx.try_iter().collect::<Vec<_>>();
assert_eq!(outputs, expected_outputs);
Ok(())
}
}
2. 环境变量模式
直接测试编译后的可执行文件,最接近生产环境行为:
ADORA_TEST_WITH_INPUTS=path/to/inputs.json \
ADORA_TEST_NO_OUTPUT_TIME_OFFSET=1 \
ADORA_TEST_WRITE_OUTPUTS_TO=/tmp/out.jsonl \
cargo run -p my-node
3. AdoraNode::init_testing
用于测试节点逻辑而不经过 main 函数:
#![allow(unused)]
fn main() {
let (node, events) = AdoraNode::init_testing(inputs, outputs, Default::default())?;
}
生成测试输入文件
通过设置 ADORA_WRITE_EVENTS_TO 录制真实的数据流事件:
ADORA_WRITE_EVENTS_TO=/tmp/recorded-events adora run examples/rust-dataflow/dataflow.yml
这将写入 inputs-{node_id}.json 文件,可直接用于 ADORA_TEST_WITH_INPUTS。
Workspace-level integration tests
在 tests/ 目录中添加新的测试文件。对于需要完整 CLI 栈的测试,请遵循 tests/example-smoke.rs 中的模式:
联网模式(测试协调器 + 守护进程):
- 使用
Once守卫构建节点(避免每个测试都重新构建) - 使用
adora down清理残留进程 - 使用
adora up启动集群 - 使用
adora start --detach运行数据流 - 轮询
adora list --json检查完成状态 - 使用
adora stop --all和adora down进行清理
本地模式(单进程,进程内协调器):
- 使用
Once守卫构建 CLI - 运行
adora run <yaml> --stop-after <duration> - 断言退出码为成功
Conventions
- 使用
assert2::assert!获取更好的错误信息(可作为 dev-dependency 使用) - 使用
tempfile::NamedTempFile生成临时输出文件 - 需要独占端口访问的端到端测试应标记为
#[ignore]并使用--test-threads=1运行 - 异步测试使用
#[tokio::test(flavor = "multi_thread")] - 容错测试的数据流文件放在
tests/dataflows/中 - 示例输入/输出基线文件放在
tests/sample-inputs/中
故障排除
cargo test 无法编译 Python 包
始终排除 Python 包:
cargo test --all \
--exclude adora-node-api-python \
--exclude adora-operator-api-python \
--exclude adora-ros2-bridge-python
冒烟/端到端测试报 “address already in use” 错误
有残留的协调器或守护进程仍在运行。请清理:
adora down
# or kill processes manually:
pkill -f adora-coordinator
pkill -f adora-daemon
冒烟测试挂起或超时
-
如果您的机器较慢,请增加测试中的超时时间(查找
Duration::from_secs(...)) -
检查示例节点是否能成功构建:
cargo build -p rust-dataflow-example-node -p rust-dataflow-example-status-node \ -p rust-dataflow-example-sink -p rust-dataflow-example-sink-dynamic cargo build -p log-sink-file -p log-sink-alert -p log-sink-tcp cargo build --release -p benchmark-example-node -p benchmark-example-sink -
对于 Python 冒烟测试,请确保已安装
pyarrow和numpy
端到端测试并行运行时失败
冒烟测试和被忽略的端到端测试必须单线程运行:
cargo test --test example-smoke -- --test-threads=1
cargo test --test ws-cli-e2e -- --ignored --test-threads=1
集成测试输出与预期不匹配
- 检查是否设置了
ADORA_TEST_NO_OUTPUT_TIME_OFFSET=1(时间偏移因机器而异) - 如果节点行为有意更改,请重新生成基线:
ADORA_TEST_WITH_INPUTS=tests/sample-inputs/inputs-rust-node.json \ ADORA_TEST_NO_OUTPUT_TIME_OFFSET=1 \ ADORA_TEST_WRITE_OUTPUTS_TO=tests/sample-inputs/expected-outputs-rust-node.jsonl \ cargo run -p rust-dataflow-example-node
Typos check fails
拼写检查配置位于 _typos.toml。添加误报排除项:
[default.extend-identifiers]
MyCustomIdent = "MyCustomIdent"
测试在本地通过但在 CI 中失败
- CI 在 Ubuntu 上运行;检查是否存在平台相关的假设(路径、进程信号)
- CI 使用
rust-cache,因此依赖版本可能与本地 lockfile 不同 - 确保
cargo fmt --all -- --check通过(CI 强制执行此检查)